diff --git a/.github/workflows/call-build-project.yml b/.github/workflows/call-build-project.yml
index 0e961df..6d3b553 100644
--- a/.github/workflows/call-build-project.yml
+++ b/.github/workflows/call-build-project.yml
@@ -8,19 +8,17 @@ jobs:
strategy:
matrix:
include:
- - id: "fabric"
- task: ":instances:fabric:build"
- - id: "forge"
- task: ":instances:forge:build"
- - id: "velocity"
- task: ":instances:velocity:build"
- id: "bukkit"
task: ":instances:bukkit:build"
+ - id: "forge"
+ task: ":instances:forge:build"
+ - id: "neoforge"
+ task: ":instances:neoforge:build"
steps:
- name: Checkout Git repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Set up JDK 21
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: 'adopt'
java-version: 21
diff --git a/.github/workflows/call-detekt-validation.yml b/.github/workflows/call-detekt-validation.yml
index a4c6a43..56134ee 100644
--- a/.github/workflows/call-detekt-validation.yml
+++ b/.github/workflows/call-detekt-validation.yml
@@ -6,9 +6,9 @@ jobs:
name: "Check project by detekt"
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up JDK 21
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
diff --git a/.github/workflows/call-gradle-wrapper-validation.yml b/.github/workflows/call-gradle-wrapper-validation.yml
index 40b9686..29cb46a 100644
--- a/.github/workflows/call-gradle-wrapper-validation.yml
+++ b/.github/workflows/call-gradle-wrapper-validation.yml
@@ -7,6 +7,6 @@ jobs:
name: "Gradle Wrapper Validation"
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v3
\ No newline at end of file
diff --git a/.github/workflows/call-tests-validation.yml b/.github/workflows/call-tests-validation.yml
index 9db21c0..f27ce31 100644
--- a/.github/workflows/call-tests-validation.yml
+++ b/.github/workflows/call-tests-validation.yml
@@ -7,9 +7,9 @@ jobs:
name: "Run tests"
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up JDK 21
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: 'adopt'
java-version: '21'
diff --git a/.github/workflows/call-upload-artifacts.yml b/.github/workflows/call-upload-artifacts.yml
index 0cadab0..ebf433d 100644
--- a/.github/workflows/call-upload-artifacts.yml
+++ b/.github/workflows/call-upload-artifacts.yml
@@ -8,24 +8,22 @@ jobs:
strategy:
matrix:
include:
- - id: "fabric"
- task: ":instances:fabric:assemble"
- - id: "forge"
- task: ":instances:forge:shadowJar"
- - id: "velocity"
- task: ":instances:velocity:shadowJar"
- id: "bukkit"
task: ":instances:bukkit:shadowJar"
+ - id: "forge"
+ task: ":instances:forge:shadowJar"
+ - id: "neoforge"
+ task: ":instances:neoforge:shadowJar"
steps:
- name: Checkout Git repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- uses: christian-draeger/read-properties@1.1.1
id: properties
with:
path: './gradle.properties'
- properties: 'makeevrserg.project.name'
+ properties: 'klibs.project.name'
- name: Set up JDK 21
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: 'adopt'
java-version: 21
@@ -35,8 +33,8 @@ jobs:
uses: gradle/gradle-build-action@v3
with:
arguments: ${{ matrix.task }} --stacktrace
- - uses: actions/upload-artifact@184d73b71b93c222403b2e7f1ffebe4508014249
+ - uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
name: "Upload ${{ matrix.id }}"
with:
name: ${{ matrix.id }}
- path: ./jars/${{ steps.properties.outputs.makeevrserg-project-name }}-${{ matrix.id }}*.jar
\ No newline at end of file
+ path: ./jars/${{ steps.properties.outputs.klibs-project-name }}-${{ matrix.id }}*.jar
\ No newline at end of file
diff --git a/.github/workflows/call-upload-github-release.yml b/.github/workflows/call-upload-github-release.yml
index faca993..f73e692 100644
--- a/.github/workflows/call-upload-github-release.yml
+++ b/.github/workflows/call-upload-github-release.yml
@@ -10,33 +10,28 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Git repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- uses: christian-draeger/read-properties@1.1.1
id: properties
with:
path: './gradle.properties'
- properties: 'makeevrserg.project.name makeevrserg.project.version.string'
+ properties: 'klibs.project.name klibs.project.version.string'
- - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
- name: "Download velocity"
+ - uses: actions/download-artifact@d0ce8fd1167ed839810201de977912a090ab10a7
+ name: "Download bukkit"
with:
- name: velocity
+ name: bukkit
path: ./jars
- - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
+ - uses: actions/download-artifact@d0ce8fd1167ed839810201de977912a090ab10a7
name: "Download forge"
with:
- name: forge
- path: ./jars
- - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
- name: "Download fabric"
- with:
- name: fabric
+ name: bungee
path: ./jars
- - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
- name: "Download bukkit"
+ - uses: actions/download-artifact@d0ce8fd1167ed839810201de977912a090ab10a7
+ name: "Download neoforge"
with:
- name: bukkit
+ name: bungee
path: ./jars
- name: Create release
@@ -47,7 +42,7 @@ jobs:
with:
files: |
./jars/*.jar
- tag_name: ${{ steps.properties.outputs.makeevrserg-project-version-string }}
- name: ${{ steps.properties.outputs.makeevrserg-project-name }} - ${{ steps.properties.outputs.makeevrserg-project-version-string }}
+ tag_name: ${{ steps.properties.outputs.klibs-project-version-string }}
+ name: ${{ steps.properties.outputs.klibs-project-name }} - ${{ steps.properties.outputs.klibs-project-version-string }}
draft: false
prerelease: false
diff --git a/.github/workflows/call-upload-modrinth-release.yml b/.github/workflows/call-upload-modrinth-release.yml
index fd7c4aa..b562c7e 100644
--- a/.github/workflows/call-upload-modrinth-release.yml
+++ b/.github/workflows/call-upload-modrinth-release.yml
@@ -11,25 +11,19 @@ jobs:
strategy:
matrix:
include:
- - id: "fabric"
- task: ":instances:fabric:assemble"
- - id: "forge"
- task: ":instances:forge:shadowJar"
- - id: "velocity"
- task: ":instances:velocity:shadowJar"
- id: "bukkit"
task: ":instances:bukkit:shadowJar"
steps:
- name: Checkout Git repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- uses: christian-draeger/read-properties@1.1.1
id: properties
with:
path: './gradle.properties'
- properties: 'makeevrserg.project.name makeevrserg.project.version.string'
+ properties: 'klibs.project.name klibs.project.version.string'
- - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
+ - uses: actions/download-artifact@d0ce8fd1167ed839810201de977912a090ab10a7
name: "Download ${{ matrix.id }}"
with:
name: ${{ matrix.id }}
@@ -40,8 +34,8 @@ jobs:
# your assets on Modrinth.
modrinth-id: 6NpNwzA1
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
- name: ${{ steps.properties.outputs.makeevrserg-project-name }}-${{ matrix.id }}
- version: ${{ steps.properties.outputs.makeevrserg-project-version-string }}
+ name: ${{ steps.properties.outputs.klibs-project-name }}-${{ matrix.id }}
+ version: ${{ steps.properties.outputs.klibs-project-version-string }}
changelog: ${{ join(github.event.commits.*.message, '\n') }}
version_type: beta
java: |
diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml
index 82fa4bc..7cd59b6 100644
--- a/.github/workflows/release-master.yml
+++ b/.github/workflows/release-master.yml
@@ -14,9 +14,3 @@ jobs:
uses: ./.github/workflows/call-upload-github-release.yml
secrets:
ACTIONS_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
-
- upload_modrinth_artifacts:
- needs: [ upload_artifacts ]
- uses: ./.github/workflows/call-upload-modrinth-release.yml
- secrets:
- MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
diff --git a/.gitignore b/.gitignore
index f8c8b9c..7a93ec7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,14 +4,6 @@
.kotlin
astra.properties
local.properties
-build
+**/build/*
+**/runs/*
jars
-# modules
-modules/api-local/build
-modules/api-remote/build
-modules/dto/build
-# instances
-instances/fabric/build
-instances/forge/build
-instances/plugin/build
-instances/velocity/build
\ No newline at end of file
diff --git a/README.md b/README.md
index 0935419..ec1f27e 100644
--- a/README.md
+++ b/README.md
@@ -1,220 +1,308 @@
-[](https://github.com/Astra-Interactive/AstraLibs)
-[](https://github.com/Astra-Interactive/AstraLibs)
-[](https://github.com/Astra-Interactive/AstraLibs)
-[](https://github.com/Astra-Interactive/AstraLibs)
-
-# AstraTemplate v7.0.0
-
-### MultiPlatform (Plugin-first) Spigot/Velocity/Fabric/Forge plugin
-
-This is a Minecraft Multiplatform template that provides architecture and various tools you'll need to create new
-Spigot/Velocity/Fabric/Forge server-plugins as fast as possible
-
-
☄️ Plugins based on AstraTemplate☄️
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+[](https://papermc.io)
+[](https://files.minecraftforge.net)
+[](https://neoforged.net)
-## Not for novice developers
+# AstraTemplate
-This project can be very difficult for novice developers. Especially for those who were working with java.
+A production-grade Minecraft plugin/mod template written in Kotlin. Provides a modular, lifecycle-driven architecture that runs across Paper, Forge, and NeoForge from a single shared codebase.
-## Overview
+---
-AstraTemplate and it's libs design after more than 3 years of developing spigot plugins and android applications.
-It contains powerful and scalable architecture template which will help in your development.
+## Plugins built on this template
-## 1. Directory structure
+
+
+
+
+
+
+
- ├── modules
- │ ├── api-local # Local api with runtime reloadable SQLite
- │ ├── api-remote # Remote sample RickMorty API
- │ ├── build-konfig # Compile-time constants
- │ └── core # Core multiplatform module
- ├── instances
- │ ├── bukkit # Paper API plugin
- │ ├── forge # Forge server mod
- │ ├── fabric # Fabric server mod
- └── └── velocity # Velocity plugin
+---
-## 2. Gradle plugin
+## Project structure
-Build convention is great, but it's a lot of boilerplate when you have different projects.
+```
+AstraTemplate/
+├── instances/
+│ ├── bukkit/ ← Paper entry point + platform wiring
+│ ├── forge/ ← Forge entry point + platform wiring
+│ └── neoforge/ ← NeoForge entry point + platform wiring
+└── modules/
+ ├── api/
+ │ ├── local/ ← Database (Exposed ORM, platform-agnostic)
+ │ └── remote/ ← REST client (Ktor, platform-agnostic)
+ ├── core/ ← Config, translations, coroutine scopes
+ ├── build-konfig/ ← Compile-time constants (id, version, etc.)
+ ├── feature-command/ ← All commands (platform-agnostic!)
+ ├── feature-gui/
+ │ ├── api/ ← GUI interfaces (Router, GuiModule)
+ │ └── bukkit/ ← Bukkit chest-GUI implementation
+ └── feature-event/
+ ├── bukkit/ ← Bukkit event listeners
+ ├── forge/ ← Forge event listeners
+ └── neoforge/ ← NeoForge event listeners
+```
-Because of this I've made a decision to implement my gradle plugin into AstraTemplate
+Each `instances/` builds a fat jar via ShadowJar and is the only place that knows about a specific platform. Everything in `modules/` is either fully platform-agnostic or has a clearly named platform variant.
-My gradle plugin is well-documented and easy to use. [Please, see it](https://github.com/makeevrserg/gradle-plugin)
+## Modules
-## 3. Modules overview
+
+modules/core — config, translations, coroutine scopes
-#### 3.1 `api-local`
+The foundation every other module depends on. Provides:
-This module contains local API with sqlite database. It's Jvm only. Due to this factor, you can easily share this module
-between your spigot/velocity plugin or fabric/forge.
+- **Config** — `PluginConfiguration` is a `@Serializable` data class written to `config.yml`. Reloaded on `/atempreload` via `StateFlowKrate`.
+- **Translations** — `PluginTranslation` works the same way with `translation.yml`. Every string has a default value so the plugin works out of the box with no files present.
+- **Coroutine scopes** — `ioScope`, `mainScope`, and `unconfinedScope` backed by `KotlinDispatchers` (platform-provided abstraction over `Dispatchers.IO` / main thread / etc.). All scopes are cancelled in `onDisable`.
-With this module you will be only dependent on LocalApi, which is an interface.
+
-Currently Exposed is used for SQLite api-local, but you can replace it with anything you want.
-Only implementation will be changed, but LocalApi will be untouched, also as other functionality of your plugin/mod
+
+modules/api/local — local database via Exposed ORM
-#### 3.2 `api-remote`
+Local database access via [Jetbrains Exposed](https://github.com/JetBrains/Exposed) ORM. The `LocalDao` interface exposes suspend functions for CRUD operations on `UserTable` and `UserRatingTable`. The underlying database connection is derived reactively from the config flow, so switching from H2 to MySQL is a one-line config change and a reload.
-This module contains remote api with RickMortyApi. It will return random character with suspend async response.
-Like `api-local`, this module also contains only jvm dependencies, so can be used in spigot/fabrict and others.
+Supported drivers (configured in `libs.versions.toml`): **H2**, **SQLite**, **MySQL**, **MariaDB**.
-#### 3.3 `build-konfig`
+
-Sometimes you need to share constants generated at compile-time between other modules, so this module exactly what you
-need.
+
+modules/api/remote — REST API client via Ktor
+
+REST API client built with [Ktor](https://ktor.io). Demonstrates fetching data from an external HTTP endpoint (the Rick & Morty API). The `RickMortyApi` interface returns `Result` — errors are never thrown, always returned explicitly.
-#### 3.4 `core`
+
-Usually this module contains shared translation/configuration or utilities.
+
+modules/build-konfig — compile-time constants
+
+Generates compile-time constants (`id`, `version`, etc.) via the BuildConfig Gradle plugin. Import from any module that needs to reference the plugin's identity at runtime without hardcoding strings.
+
+
-## 4. Velocity/Fabric/Forge
+
+modules/feature-command — cross-platform commands (no platform imports)
-I've not been working with this loaders too much, but th modules contains basic functionality with plugin
-information generation.
+All commands in one place, with **no platform imports**. Uses the Brigadier DSL from AstraLibs to define commands that compile and run identically on Paper, Forge, and NeoForge. The platform-specific `MultiplatformCommand` adapter is injected at the `RootModule` level.
-## 5. Quick overview
+
-This plugin contains advanced and powerful spigot functionality
+
+modules/feature-gui — chest GUI (Bukkit, with stub for other platforms)
-- GUI
-- Commands
-- Events
-- Translation
-- DI
-- Permissions
-- Configuration
-- ORM(database)
-- Reloading
+Split into `api` (the `Router` interface + `GuiModule`) and `bukkit` (the implementation). The Bukkit implementation provides a paginated chest inventory driven by `StateFlow` — the GUI re-renders automatically whenever the underlying data changes. On Forge/NeoForge a `StubGuiModule` satisfies the interface so the shared command module compiles without pulling in Bukkit.
-## 6. Architecture overview
+
- (Click to expand) Lifecycle diagram
+modules/feature-event — platform-specific event listeners
-With this hierarchy its' possible to create independent modules
+Platform-specific event listeners, one submodule per platform. The Bukkit variant listens to `BlockPlaceEvent`; Forge and NeoForge variants listen to the server tick. Each submodule exposes a `Lifecycle` so `RootModule` can register and unregister listeners cleanly.
-Each Module contains Lifecycle which is handled by it's parent module
+
+
+---
-Each Lifecycle contains three methods:
+## Architecture
-- onEnable
-- onDisable
-- onReload
+### Lifecycle tree
-In this example, we have `RootPlugin` which is `JavaPlugin`.
-`RootPlugin` contains list of child lifecycles.
-Child lifecycles called when RootPlugins's lifecycle methods is called.
+Every module exposes a `Lifecycle` with three callbacks: `onEnable`, `onDisable`, `onReload`. The plugin entry point creates a `RootModule`, chains all child lifecycles, and delegates to them:
-RootPlugin doesn't go beyond it's area of responsibility.
-All children handle it's own lifecycles.
+```kotlin
+// instances/bukkit — AstraTemplate.kt
+class AstraTemplate : LifecyclePlugin() {
+ private val rootModule = RootModule(this)
+
+ override fun onEnable() = rootModule.lifecycle.onEnable()
+ override fun onDisable() = rootModule.lifecycle.onDisable()
+ override fun onReload() = rootModule.lifecycle.onReload()
+}
+```
+
+```kotlin
+// instances/bukkit — RootModule.kt
+class RootModule(plugin: AstraTemplate) {
+ val coreModule = CoreModule(plugin.dataFolder, DefaultBukkitDispatchers(plugin))
+ val apiLocalModule = ApiLocalModule(coreModule.configKrate.cachedStateFlow, coreModule.ioScope)
+ val apiRemoteModule = ApiRemoteModule()
+ val eventModule = EventModule(coreModule, plugin)
+ val guiModule = BukkitGuiModule(coreModule, apiLocalModule)
+ val commandModule = CommandModule(coreModule, apiRemoteModule, guiModule, ...)
+
+ val lifecycle = Lifecycle.Lambda(
+ onEnable = { listOf(coreModule, eventModule, apiLocalModule, commandModule).forEach(Lifecycle::onEnable) },
+ onDisable = { /* same list, reversed */ },
+ onReload = { /* same list */ }
+ )
+}
+```
+
+This makes the plugin **reloadable at runtime** — `/atempreload` walks the same chain in reverse and re-enables it, picking up any config or translation changes on the fly.
```mermaid
-classDiagram
- class RootPlugin {
- lifecycles
- onEnable()
- onDisable()
- onReload()
- }
+graph TD
+ Plugin --> RootModule
+ RootModule --> CoreModule
+ RootModule --> ApiLocalModule
+ RootModule --> ApiRemoteModule
+ RootModule --> EventModule
+ RootModule --> CommandModule
+ EventModule --> TemplateEvent
+ EventModule --> BetterAnotherEvent
+```
- RootPlugin ..> CoreModule: Child
- RootPlugin ..> EventModule: Child
- RootPlugin ..> DatabaseModule: Child
- EventModule ..> MoveEventModule: Child
+### Dependency injection
- class MoveEventModule {
- lifecycle: Lifecycle
- onEnable()
- onDisable()
- }
- class EventModule {
- lifecycle: Lifecycle
- onEnable()
- onDisable()
+There is no DI framework. Each module is a plain class whose constructor receives other module interfaces it depends on. `RootModule` is the composition root and instantiates everything in the right order, using `lazy {}` where initialization must be deferred.
+
+```kotlin
+// Pass the whole module interface, not individual services extracted from it
+val commandModule = CommandModule(
+ coreModule = coreModule,
+ guiModule = guiModule,
+ apiRemoteModule = apiRemoteModule,
+ ...
+)
+```
+
+This keeps coupling explicit and avoids hidden runtime failures from missing bindings.
+
+---
+
+## Cross-platform commands
+
+Commands live in `modules/feature-command` — a plain Kotlin module with **zero platform dependencies**. They use the Brigadier DSL from AstraLibs, which abstracts over Paper's and Forge's native Brigadier adapters.
+
+```kotlin
+// Works on Paper, Forge, and NeoForge without any changes
+command("rickandmorty") {
+ literal("random") {
+ runs { ctx ->
+ scope.launch(dispatchers.IO) {
+ rmApi.getRandomCharacter(Random.nextInt(0, 100))
+ .onSuccess { ctx.getSender().sendMessage(...) }
+ .onFailure { ctx.getSender().sendMessage(...) }
+ }
+ }
}
- class CoreModule {
- lifecycle: Lifecycle
- onReload()
+ literal("specific") {
+ argument("number", IntegerArgumentType.integer()) { numberArg ->
+ runs { ctx -> send(ctx.getSender(), ctx.requireArgument(numberArg)) }
+ }
}
- class DatabaseModule {
- lifecycle: Lifecycle
- onEnable()
- onDisable()
+}
+```
+
+On each platform the `RootModule` provides a `MultiplatformCommand` backed by the right adapter (`PaperMultiplatformCommands`, `MinecraftMultiplatformCommands`). The shared command code never needs to change.
+
+### Available commands
+
+| Command | Description |
+|-------------------------------------|------------------------------------------------------|
+| `/add [amount]` | Add item to a player's inventory |
+| `/translation` | Show current translation value (useful after reload) |
+| `/adamage ` | Deal damage to a player |
+| `/atempgui` | Open the sample paginated GUI |
+| `/rickandmorty random` | Fetch a random Rick & Morty character via REST |
+| `/rickandmorty specific ` | Fetch a specific character by id |
+| `/atempreload` | Reload config, translations, and database connection |
+
+---
+
+## Configuration
+
+Config and translations are plain `@Serializable` data classes serialized to YAML via [kaml](https://github.com/charleskorn/kaml). Inline doc-comments render directly in the generated YAML file:
+
+```kotlin
+@Serializable
+data class PluginConfiguration(
+ @YamlComment("First line description for config1", "Second line description for config2")
+ @SerialName("config_1")
+ val config1: String = "NONE",
+
+ @SerialName("database")
+ val database: DatabaseConfiguration = DatabaseConfiguration.H2("db")
+)
+```
+
+Both config and translations are stored as `StateFlowKrate` / `CachedKrate`. Any module that reads them always sees the latest value after a reload — no manual propagation needed.
+
+---
+
+## Local database
+
+`modules/api/local` uses [Jetbrains Exposed](https://github.com/JetBrains/Exposed) as the ORM. The database connection is derived reactively from the config flow — when the config is reloaded with a new database URL, the connection is replaced automatically:
+
+```kotlin
+private val databaseFlow = configFlow
+ .map { it.database }
+ .distinctUntilChanged()
+ .flatMapLatest { configuration -> configuration.connectAsFlow() }
+ .onEach { db ->
+ transaction(db) { SchemaUtils.create(UserRatingTable, UserTable) }
}
+ .shareIn(ioScope, SharingStarted.Eagerly, 1)
```
-
+Supported drivers (swap in `libs.versions.toml`): **H2**, **SQLite**, **MySQL**, **MariaDB**.
-## 7.1 Plugin usage
+---
-| Command | Permission | Description |
-|:------------------------------------|:-----------|:----------------------------------------------|
-| `/add [amount]` | `-` | `Add item to player inventory` |
-| `/atemp translation` | `-` | `Show translation change after /atempreload` |
-| `/adamage ` | `-` | `Damage player` |
-| `/atempgui` | `-` | `Open sample gui` |
-| `/rickandmorty ` | `-` | `Send to executor random RickMorty character` |
+## Remote API
-## 7.1 Fabric mod usage
+`modules/api/remote` shows how to call an external REST endpoint using Ktor. The interface is minimal:
-| Command | Permission | Description |
-|:--------------|:-----------|:-------------------------------|
-| `/rickmorty` | `-` | `Rick morty character println` |
-| `/helloworld` | `-` | `Hello world println` |
+```kotlin
+interface RickMortyApi {
+ suspend fun getRandomCharacter(id: Int): Result
+}
+```
-## 7.2 Forge mod usage
+Errors are returned as `Result` — never thrown — so callers handle failures explicitly.
-| Command | Permission | Description |
-|:--------------|:-----------|:----------------------|
-| `/helloworld` | `-` | `Hello world println` |
+---
-### Platforms
+## GUI (Bukkit)
-- [x] Spigot/Paper
-- [x] Fabric - Pre Alpha state
-- [x] Forge - Pre-Alpha state
-- [x] Velocity
+The GUI layer sits behind a `Router` interface defined in `modules/feature-gui/api`. The Bukkit implementation provides a paginated chest inventory with reactive state via Kotlin `StateFlow`:
-### Build jar executables
+- `SampleGuiComponent` owns state (`Loading` / `Items` / `Users`)
+- `SampleGUI` observes state and re-renders on every emission
+- Navigation (next/prev page, change mode, add user, back/close) is handled by dedicated button objects
-Firstly, change gradle/libs.versions.toml destination-xxx to your folder
+On Forge/NeoForge a `StubGuiModule` satisfies the `GuiModule` interface so the shared `CommandModule` compiles without a Bukkit dependency.
- $ ./gradlew :plugin:shadowJar # assemble the plugin .jar
- $ ./gradlew :velocity:shadowJar # assemble the plugin .jar
- $ ./gradlew :fabric:build # assemble the fabric .jar
- $ ./gradlew :forge:shadowJar # assemble the forge .jar
+---
-### Test server
+## Building
-There's located [docker-compose.yml](docker-compose.yml) which can launch any server you need for testing purposes.
+```bash
+# Paper plugin
+./gradlew :instances:bukkit:shadowJar
-Also, checkout [AstraLearner](https://play.google.com/store/apps/details?id=com.makeevrserg.astralearner) - it will help
-you to learn foreign words easily!
+# Forge mod
+./gradlew :instances:forge:shadowJar
-### Afterword
+# NeoForge mod
+./gradlew :instances:neoforge:shadowJar
-AstraTemplate highly dependent on self-written open source libraries
+# Run all tests
+./gradlew allTests
+```
+
+Output jars land in each instance's `build/libs/` directory and are optionally copied to a remote server by the FTP Gradle plugin (configure the destination in `libs.versions.toml`).
+
+---
+
+## Test server (Docker)
-- [AstraLibs](https://github.com/Astra-Interactive/AstraLibs) - Minecraft development
-- [klibs.mikro](https://github.com/makeevrserg/klibs.mikro) - Mapper, UseCase, Dispatchers interface
-- [klibs.kstorage](https://github.com/makeevrserg/klibs.kstorage) - Key-value storage wrapper
-- [klibs.kdi](https://github.com/makeevrserg/klibs.kdi) - Manual DI
+`docker-compose.yml` at the project root starts a local test server using [itzg/minecraft-server](https://github.com/itzg/docker-minecraft-server).
+
+Before running, **manually edit `docker-compose.yml`** to uncomment the block for your target platform (Forge, NeoForge, or Paper) and comment out the others. Each block sets the `TYPE`, `VERSION`, and platform-specific version variables, and the matching `volumes` entry below it.
+
+```bash
+docker compose up
+```
diff --git a/build.gradle.kts b/build.gradle.kts
index af240c1..76e6145 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,22 +1,16 @@
plugins {
java
- `maven-publish`
`java-library`
- alias(libs.plugins.kotlin.jvm) apply false
- alias(libs.plugins.kotlin.serialization) apply false
+ `maven-publish`
alias(libs.plugins.gradle.buildconfig) apply false
-
- // klibs - core
+ alias(libs.plugins.gradle.forgegradle) apply false
+ alias(libs.plugins.gradle.neoforgegradle) apply false
alias(libs.plugins.klibs.gradle.detekt) apply false
- alias(libs.plugins.klibs.gradle.detekt.compose) apply false
alias(libs.plugins.klibs.gradle.dokka.root) apply false
- alias(libs.plugins.klibs.gradle.dokka.module) apply false
- alias(libs.plugins.klibs.gradle.java.core) apply false
+ alias(libs.plugins.klibs.gradle.java.version) apply false
alias(libs.plugins.klibs.gradle.publication) apply false
alias(libs.plugins.klibs.gradle.rootinfo) apply false
alias(libs.plugins.klibs.minecraft.resource.processor) apply false
+ alias(libs.plugins.kotlin.jvm) apply false
+ alias(libs.plugins.kotlin.serialization) apply false
}
-
-apply(plugin = "ru.astrainteractive.gradleplugin.dokka.root")
-apply(plugin = "ru.astrainteractive.gradleplugin.detekt")
-apply(plugin = "ru.astrainteractive.gradleplugin.root.info")
diff --git a/docker-compose.yml b/docker-compose.yml
index 58fc43c..af1303e 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,29 +1,36 @@
services:
- # For command call use `docker exec mc_docker rcon-cli `
- mc_docker:
+ # For command call use `docker exec mc_docker_astratemplate rcon-cli `
+ mc_docker_astratemplate:
restart: no
- container_name: mc_docker
+ container_name: mc_docker_astratemplate
image: itzg/minecraft-server # https://github.com/itzg/docker-minecraft-server
tty: true
stdin_open: true
ports:
- "25565:25565"
environment:
+ LEVEL_TYPE: FLAT
+ EXISTING_OPS_FILE: MERGE
+ OPS: |
+ RomaRoman
EULA: true
- # Forge
-# TYPE: FORGE
-# VERSION: "1.20.1"
-# FORGE_VERSION: "47.2.20"
- # Custom server
- TYPE: PAPER
- #CUSTOM_SERVER: https://api.papermc.io/v2/projects/paper/versions/1.21.1/builds/121/downloads/paper-1.21.1-121.jar
- VERSION: 1.21.4
+ ONLINE_MODE: false
+ # NeoForge --------------------
+ # TYPE: NEOFORGE
+ # VERSION: "1.20.1"
+ # NEOFORGE_VERSION: "21.1.129"
+ # Forge --------------------
+ TYPE: FORGE
+ VERSION: "1.20.1"
+ FORGE_VERSION: "47.4.12"
+ # Paper --------------------
+# TYPE: PAPER
+# VERSION: 1.21.8
+ # Custom -------------------
+ # CUSTOM_SERVER: https://api.papermc.io/v2/projects/paper/versions/1.21.1/builds/121/downloads/paper-1.21.1-121.jar
volumes:
- # Forge
-# - ./build/forge:/data
- # Bukkit/Paper/Spigot
- - ./build/bukkit:/data
- # Velocity
-# - ./build/velocity:/data
- # Fabric
-# - ./build/fabric:/data
\ No newline at end of file
+ - ./build/forge:/data # Forge
+# - ./build/neoforge:/data # NeoForge
+# - ./build/bukkit:/data # Bukkit/Paper/Spigot
+# - ./build/velocity:/data # Velocity
+# - ./build/fabric:/data # Fabric
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 69d7f29..e4427f4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,15 +1,20 @@
-# Kotlin
-kotlin.code.style=official
# Gradle
-org.gradle.jvmargs=-Xmx4000m
+org.gradle.parallel=true
+org.gradle.configuration-cache=true
+org.gradle.caching=true
+kotlin.incremental=true
+org.gradle.jvmargs=-Xmx5g -XX:+UseParallelGC -Dkotlin.daemon.jvm.options\="-Xmx5G"
+kotlin.code.style=official
+testTask.ignoreFailures=false
# Java
-makeevrserg.java.source=8
-makeevrserg.java.target=21
-makeevrserg.java.ktarget=21
+klibs.java.source=8
+klibs.java.target=21
+klibs.java.ktarget=21
# Project
-makeevrserg.project.name=AstraTemplate
-makeevrserg.project.group=ru.astrainteractive.astratemplate
-makeevrserg.project.version.string=8.0.0-alpha01
-makeevrserg.project.description=Template plugin for EmpireProjekt
-makeevrserg.project.developers=makeevrserg|Makeev Roman|makeevrserg@gmail.com
-makeevrserg.project.url=https://github.com/Astra-Interactive/AstraTemplate
+klibs.project.name=AstraTemplate
+klibs.project.group=ru.astrainteractive.astratemplate
+klibs.project.version.string=9.0.0
+klibs.project.description=Template plugin for EmpireProjekt
+klibs.project.developers=makeevrserg|Makeev Roman|makeevrserg@gmail.com
+klibs.project.url=https://github.com/Astra-Interactive/AstraTemplate
+klibs.publish.developers=makeevrserg|Makeev Roman|makeevrserg@gmail.com
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 89079a8..406f811 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,64 +1,72 @@
[versions]
cache4k = "0.14.0"
-discordsrv = "1.30.2"
+discordsrv = "1.30.5"
driver-h2 = "2.4.240"
-driver-jdbc = "3.50.3.0"
-driver-mariadb = "3.5.6"
+driver-jdbc = "3.53.0.0"
+driver-mariadb = "3.5.8"
driver-mysql = "8.0.33"
-exposed = "0.61.0"
-forgegradle = "[6.0,6.2)"
-gradle-buildconfig = "5.7.0"
-gradle-ftp = "0.1.2"
-gradle-shadow = "9.2.2"
-gson = "2.13.2"
-guava = "33.5.0-jre"
-jda = "6.1.0"
+exposed = "1.2.0"
+gradle-buildconfig = "6.0.9"
+gradle-ftp = "0.1.3"
+gradle-shadow = "9.4.1"
+gson = "2.14.0"
+guava = "33.6.0-jre"
+jda = "6.4.1"
jda-webhook = "0.8.4"
-klibs-gradleplugin = "1.11.1"
+jmh-core = "1.37"
+joml = "1.10.8"
+klibs-gradleplugin = "2.1.1"
klibs-kdi = "1.4.8"
-klibs-kstorage = "4.4.1"
-klibs-mikro = "1.16.0"
-kotlin-benchmark = "0.4.14"
+klibs-kstorage = "5.0.4"
+klibs-mikro = "1.23.0"
+kotlin-benchmark = "0.4.16"
kotlin-coroutines = "1.10.2"
kotlin-datetime = "0.7.1-0.6.x-compat"
-kotlin-json = "1.9.0"
-kotlin-serialization-kaml = "0.102.0"
-kotlin-serialization = "1.9.0"
+kotlin-json = "1.11.0"
+kotlin-serialization = "1.11.0"
+kotlin-serialization-kaml = "0.104.0"
kotlin-version = "2.2.0"
-ktor = "3.3.1"
-kyori = "4.25.0"
-kyori-adventure = "4.25.0"
-mcprotocollib = "1.21.7-SNAPSHOT"
-minecraft-astralibs = "3.29.0"
-minecraft-bstats = "3.1.0"
-minecraft-bungee = "1.21-R0.5-SNAPSHOT"
+ktor = "3.4.3"
+minecraft-astralibs = "3.40.5"
+minecraft-brigadier = "1.3.10"
+minecraft-authlib = "6.0.54"
+minecraft-bstats = "3.2.1"
+minecraft-bungee = "26.1-R0.1-SNAPSHOT"
+minecraft-datafixerupper = "8.0.16"
minecraft-essentialsx = "2.21.2"
-minecraft-fabric-api = "0.134.1+"
+minecraft-fabric-api = "0.139.5+"
+minecraft-fabric-kotlin = "1.13.7+"
+minecraft-fabric-loader = "0.19.2"
+minecraft-fabric-loom = "1.16.1"
minecraft-fabric-yarn = "1.20.1+build.10"
-minecraft-fabric-kotlin = "1.13.1+"
-minecraft-fabric-loader = "0.17.2"
-minecraft-fabric-loom = "1.11.8"
+minecraft-forge-bus="6.0.5"
+minecraft-forgegradle = "7.0.25"
+minecraft-forge-jarjar = "0.2.3"
+minecraft-forgerenamer = "1.0.14"
+minecraft-forgeversion = "1.20.1-47.4.12"
+minecraft-kyori = "5.0.1"
minecraft-luckperms = "5.5"
-minecraft-mockbukkit = "4.33.2"
+minecraft-mcprotocollib = "1.21.7-SNAPSHOT"
+minecraft-mockbukkit = "4.108.0"
minecraft-mojang-version = "1.20.1"
-minecraft-packetevents = "2.10.0"
-minecraft-papi = "2.11.6"
+minecraft-neoforged-bus = "8.0.2"
+minecraft-neoforgegradle = "7.1.20"
+minecraft-neoforgeversion = "21.1.129"
+minecraft-packetevents = "2.12.1"
+minecraft-papi = "2.12.2"
minecraft-protocollib = "5.3.0"
minecraft-spigot = "1.21.9-R0.1-SNAPSHOT"
-minecraft-towny = "0.101.2.1"
+minecraft-towny = "0.102.0.0"
minecraft-vault = "1.7.1"
-minecraft-velocity = "3.4.0-SNAPSHOT" # https://github.com/PaperMC/Velocity
-minecraft-forgeversion = "47.2.20"
-shadow = "8.1.8"
-telegrambots = "9.2.0"
-tests-junit-bom = "6.0.1"
-tests-junit-jupiter-api = "6.0.1"
-tests-mockito = "5.20.0"
-tests-org-testing = "7.11.0"
-turbine = "1.2.1"
+minecraft-velocity = "4.0.0-SNAPSHOT"
+telegrambots = "9.5.0"
+tests-junit-bom = "6.0.3"
+tests-junit-jupiter-api = "6.0.3"
+tests-mockito = "5.23.0"
+tests-org-testing = "7.12.0"
+tests-turbine = "1.2.1"
[libraries]
-minecraft-astralibs-core = { module = "ru.astrainteractive.astralibs:core", version.ref = "minecraft-astralibs" }
cache4k = { module = "io.github.reactivecircus.cache4k:cache4k-jvm", version.ref = "cache4k" }
discord-webhook = { module = "club.minnced:discord-webhooks", version.ref = "jda-webhook" }
driver-h2 = { module = "com.h2database:h2", version.ref = "driver-h2" }
@@ -69,23 +77,24 @@ exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "e
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
exposed-java-time = { module = "org.jetbrains.exposed:exposed-java-time", version.ref = "exposed" }
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
-gradle-shadow = { module = "gradle.plugin.com.github.johnrengelman:shadow", version.ref = "gradle-shadow" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }
jda = { module = "net.dv8tion:JDA", version.ref = "jda" }
+jmh-core = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh-core" }
+joml = { module = "org.joml:joml", version.ref = "joml" }
klibs-kdi = { module = "ru.astrainteractive.klibs:kdi-jvm", version.ref = "klibs-kdi" }
klibs-kstorage = { module = "ru.astrainteractive.klibs:kstorage", version.ref = "klibs-kstorage" }
klibs-mikro-core = { module = "ru.astrainteractive.klibs:mikro-core", version.ref = "klibs-mikro" }
-klibs-mikro-extensions = { module = "ru.astrainteractive.klibs:mikro-extensions", version.ref = "klibs-mikro" }
+klibs-mikro-extensions = { module = "ru.astrainteractive.klibs:mikro-extensions-jvm", version.ref = "klibs-mikro" }
kotlin-benchmark-runtime = { module = "org.jetbrains.kotlinx:kotlinx-benchmark-runtime", version.ref = "kotlin-benchmark" }
kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
kotlin-coroutines-coreJvm = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", version.ref = "kotlin-coroutines" }
kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin-coroutines" }
kotlin-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime-jvm", version.ref = "kotlin-datetime" }
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-version" }
-kotlin-serialization-kaml = { module = "com.charleskorn.kaml:kaml", version.ref = "kotlin-serialization-kaml" }
kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin-version" }
kotlin-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlin-serialization" }
+kotlin-serialization-kaml = { module = "com.charleskorn.kaml:kaml", version.ref = "kotlin-serialization-kaml" }
kotlin-serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlin-json" }
kotlin-tooling-core = { module = "org.jetbrains.kotlin:kotlin-tooling-core", version.ref = "kotlin-version" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
@@ -94,29 +103,39 @@ ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-json = { module = "io.ktor:ktor-client-json", version.ref = "ktor" }
ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
-kyori-plain = { module = "net.kyori:adventure-text-serializer-plain", version.ref = "kyori-adventure" }
-kyori-api = { module = "net.kyori:adventure-api", version.ref = "kyori" }
-kyori-gson = { module = "net.kyori:adventure-text-serializer-gson", version.ref = "kyori" }
-kyori-legacy = { module = "net.kyori:adventure-text-serializer-legacy", version.ref = "kyori" }
-kyori-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "kyori" }
-mcprotocollib = { module = "org.geysermc.mcprotocollib:protocol", version.ref = "mcprotocollib" }
minecraft-astralibs-command = { module = "ru.astrainteractive.astralibs:command", version.ref = "minecraft-astralibs" }
minecraft-astralibs-command-bukkit = { module = "ru.astrainteractive.astralibs:command-bukkit", version.ref = "minecraft-astralibs" }
+minecraft-astralibs-core = { module = "ru.astrainteractive.astralibs:core", version.ref = "minecraft-astralibs" }
minecraft-astralibs-core-bukkit = { module = "ru.astrainteractive.astralibs:core-bukkit", version.ref = "minecraft-astralibs" }
minecraft-astralibs-core-forge = { module = "ru.astrainteractive.astralibs:core-forge", version.ref = "minecraft-astralibs" }
+minecraft-astralibs-core-minecraft = { module = "ru.astrainteractive.astralibs:core-minecraft", version.ref = "minecraft-astralibs" }
+minecraft-astralibs-core-neoforge = { module = "ru.astrainteractive.astralibs:core-neoforge", version.ref = "minecraft-astralibs" }
minecraft-astralibs-exposed = { module = "ru.astrainteractive.astralibs:exposed", version.ref = "minecraft-astralibs" }
minecraft-astralibs-menu-bukkit = { module = "ru.astrainteractive.astralibs:menu-bukkit", version.ref = "minecraft-astralibs" }
minecraft-astralibs-orm = { module = "ru.astrainteractive.astralibs:orm", version.ref = "minecraft-astralibs" }
+minecraft-brigadier = { module = "com.mojang:brigadier", version.ref = "minecraft-brigadier" }
+minecraft-authlib = { module = "com.mojang:authlib", version.ref = "minecraft-authlib" }
minecraft-bstats = { module = "org.bstats:bstats-bukkit", version.ref = "minecraft-bstats" }
minecraft-bungee = { module = "net.md-5:bungeecord-api", version.ref = "minecraft-bungee" }
+minecraft-datafixerupper = { module = "com.mojang:datafixerupper", version.ref = "minecraft-datafixerupper" }
minecraft-discordsrv = { module = "com.discordsrv:discordsrv", version.ref = "discordsrv" }
minecraft-essentialsx = { module = "net.essentialsx:EssentialsX", version.ref = "minecraft-essentialsx" }
minecraft-fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "minecraft-fabric-api" }
minecraft-fabric-kotlin = { module = "net.fabricmc:fabric-language-kotlin", version.ref = "minecraft-fabric-kotlin" }
minecraft-fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "minecraft-fabric-loader" }
minecraft-fabric-mojang = { module = "com.mojang:minecraft", version.ref = "minecraft-mojang-version" }
+minecraft-forge-bus = { module = "net.minecraftforge:eventbus", version.ref = "minecraft-forge-bus" }
+minecraft-forgeversion = { module = "net.minecraftforge:forge", version.ref = "minecraft-forgeversion" }
+minecraft-kyori-api = { module = "net.kyori:adventure-api", version.ref = "minecraft-kyori" }
+minecraft-kyori-gson = { module = "net.kyori:adventure-text-serializer-gson", version.ref = "minecraft-kyori" }
+minecraft-kyori-legacy = { module = "net.kyori:adventure-text-serializer-legacy", version.ref = "minecraft-kyori" }
+minecraft-kyori-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "minecraft-kyori" }
+minecraft-kyori-plain = { module = "net.kyori:adventure-text-serializer-plain", version.ref = "minecraft-kyori" }
minecraft-luckperms = { module = "net.luckperms:api", version.ref = "minecraft-luckperms" }
+minecraft-mcprotocollib = { module = "org.geysermc.mcprotocollib:protocol", version.ref = "minecraft-mcprotocollib" }
minecraft-mockbukkit = { module = "com.github.MockBukkit:MockBukkit", version.ref = "minecraft-mockbukkit" }
+minecraft-neoforged-bus = { module = "net.neoforged:bus", version.ref = "minecraft-neoforged-bus" }
+minecraft-neoforgeversion = { module = "net.neoforged:neoforge", version.ref = "minecraft-neoforgeversion" }
minecraft-packetevents = { module = "com.github.retrooper:packetevents-spigot", version.ref = "minecraft-packetevents" }
minecraft-paper-api = { module = "io.papermc.paper:paper-api", version.ref = "minecraft-spigot" }
minecraft-papi = { module = "me.clip:placeholderapi", version.ref = "minecraft-papi" }
@@ -136,28 +155,33 @@ tests-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5" }
tests-kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test" }
tests-mockito = { module = "org.mockito:mockito-core", version.ref = "tests-mockito" }
tests-org-testing = { module = "org.testng:testng", version.ref = "tests-org-testing" }
-turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
+tests-turbine = { module = "app.cash.turbine:turbine", version.ref = "tests-turbine" }
[plugins]
-fabric-loom = { id = "fabric-loom", version.ref = "minecraft-fabric-loom" }
-forgegradle = { id = "net.minecraftforge.gradle", version.ref = "forgegradle" }
gradle-buildconfig = { id = "com.github.gmazzo.buildconfig", version.ref = "gradle-buildconfig" }
+gradle-fabric-loom = { id = "fabric-loom", version.ref = "minecraft-fabric-loom" }
+gradle-forgegradle = { id = "net.minecraftforge.gradle", version.ref = "minecraft-forgegradle" }
+gradle-forge-jarjar = { id = "net.minecraftforge.jarjar", version.ref = "minecraft-forge-jarjar" }
+gradle-forgerenamer = { id = "net.minecraftforge.renamer", version.ref = "minecraft-forgerenamer" }
gradle-ftp = { id = "ru.astrainteractive.ftpplugin.gradleftp", version.ref = "gradle-ftp" }
+gradle-neoforgegradle = { id = "net.neoforged.gradle.userdev", version.ref = "minecraft-neoforgegradle" }
gradle-shadow = { id = "com.gradleup.shadow", version.ref = "gradle-shadow" }
+klibs-gradle-minecraft-platform = { id = "ru.astrainteractive.gradleplugin.minecraft.platform", version.ref = "klibs-gradleplugin" }
klibs-gradle-detekt = { id = "ru.astrainteractive.gradleplugin.detekt", version.ref = "klibs-gradleplugin" }
klibs-gradle-detekt-compose = { id = "ru.astrainteractive.gradleplugin.detekt.compose", version.ref = "klibs-gradleplugin" }
-klibs-gradle-dokka-module = { id = "ru.astrainteractive.gradleplugin.dokka.module", version.ref = "klibs-gradleplugin" }
klibs-gradle-dokka-root = { id = "ru.astrainteractive.gradleplugin.dokka.root", version.ref = "klibs-gradleplugin" }
klibs-gradle-java-core = { id = "ru.astrainteractive.gradleplugin.java.core", version.ref = "klibs-gradleplugin" }
+klibs-gradle-java-version = { id = "ru.astrainteractive.gradleplugin.java.version", version.ref = "klibs-gradleplugin" }
klibs-gradle-minecraft-empty = { id = "ru.astrainteractive.gradleplugin.minecraft.empty", version.ref = "klibs-gradleplugin" }
klibs-gradle-minecraft-multiplatform = { id = "ru.astrainteractive.gradleplugin.minecraft.multiplatform", version.ref = "klibs-gradleplugin" }
klibs-gradle-publication = { id = "ru.astrainteractive.gradleplugin.publication", version.ref = "klibs-gradleplugin" }
-klibs-gradle-rootinfo = { id = "ru.astrainteractive.gradleplugin.root.info", version.ref = "klibs-gradleplugin" }
+klibs-gradle-rootinfo = { id = "ru.astrainteractive.gradleplugin.rootinfo", version.ref = "klibs-gradleplugin" }
klibs-gradle-stub-javadoc = { id = "ru.astrainteractive.gradleplugin.stub.javadoc", version.ref = "klibs-gradleplugin" }
-klibs-minecraft-resource-processor = { id = "ru.astrainteractive.gradleplugin.minecraft.resource-processor", version.ref = "klibs-gradleplugin" }
+klibs-minecraft-resource-processor = { id = "ru.astrainteractive.gradleplugin.minecraft.resource.processor", version.ref = "klibs-gradleplugin" }
+klibs-minecraft-shadow = { id = "ru.astrainteractive.gradleplugin.minecraft.shadow", version.ref = "klibs-gradleplugin" }
kotlin-allopen = { id = "org.jetbrains.kotlin.plugin.allopen", version.ref = "kotlin-version" }
kotlin-benchmark = { id = "org.jetbrains.kotlinx.benchmark", version.ref = "kotlin-benchmark" }
kotlin-gradle = { id = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-version" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-version" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin-version" }
-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-version" }
+kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-version" }
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5c82cb0..b52fb7e 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
+networkTimeout=10000
+retries=0
+retryBackOffMs=500
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/instances/bukkit/build.gradle.kts b/instances/bukkit/build.gradle.kts
index 53f7c9d..4d9cc00 100644
--- a/instances/bukkit/build.gradle.kts
+++ b/instances/bukkit/build.gradle.kts
@@ -1,35 +1,48 @@
+import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-import org.gradle.kotlin.dsl.named
-import ru.astrainteractive.gradleplugin.property.extension.ModelPropertyValueExt.requireProjectInfo
-
+import org.gradle.kotlin.dsl.shadow
+import ru.astrainteractive.gradleplugin.property.util.requireProjectInfo
plugins {
- kotlin("jvm")
- alias(libs.plugins.klibs.gradle.java.core)
- alias(libs.plugins.klibs.minecraft.resource.processor)
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
alias(libs.plugins.gradle.shadow)
+ alias(libs.plugins.klibs.minecraft.resource.processor)
}
dependencies {
- // Kotlin
- implementation(libs.kotlin.coroutines.core)
- implementation(libs.kotlin.serialization.json)
- implementation(libs.kotlin.serialization.kaml)
- // AstraLibs
- implementation(libs.minecraft.astralibs.core)
- implementation(libs.minecraft.astralibs.command)
- implementation(libs.minecraft.astralibs.command.bukkit)
- implementation(libs.minecraft.astralibs.menu.bukkit)
- implementation(libs.minecraft.astralibs.core.bukkit)
- implementation(libs.klibs.mikro.core)
- implementation(libs.klibs.kstorage)
- // Spigot dependencies
+ compileOnly(libs.driver.h2)
+ compileOnly(libs.driver.jdbc)
+ compileOnly(libs.driver.mysql)
+ compileOnly(libs.minecraft.discordsrv)
+ compileOnly(libs.minecraft.essentialsx)
+ compileOnly(libs.minecraft.luckperms)
compileOnly(libs.minecraft.paper.api)
- implementation(libs.minecraft.bstats)
- // Local
- implementation(projects.modules.apiLocal)
- implementation(projects.modules.apiRemote)
- implementation(projects.modules.core)
+ compileOnly(libs.minecraft.vaultapi)
+ compileOnly(libs.minecraft.kyori.api)
+
+ shadow(libs.klibs.mikro.core)
+ shadow(libs.kotlin.coroutines.core)
+ shadow(libs.kotlin.serialization.json)
+ shadow(libs.minecraft.astralibs.command)
+ shadow(libs.minecraft.astralibs.command.bukkit)
+ shadow(libs.minecraft.astralibs.core)
+ shadow(libs.minecraft.astralibs.core.bukkit)
+ shadow(libs.minecraft.astralibs.menu.bukkit)
+ shadow(libs.minecraft.bstats)
+
+ shadow(projects.modules.api.local)
+ shadow(projects.modules.api.remote)
+ shadow(projects.modules.buildKonfig)
+ shadow(projects.modules.core)
+ shadow(projects.modules.featureCommand)
+ shadow(projects.modules.featureGui.api)
+ shadow(projects.modules.featureGui.bukkit)
+ shadow(projects.modules.featureEvent.bukkit)
+
+ testImplementation(libs.tests.kotlin.test)
}
minecraftProcessResource {
@@ -39,57 +52,178 @@ minecraftProcessResource {
libs.driver.h2.get(),
libs.driver.jdbc.get(),
libs.driver.mysql.get(),
+ libs.driver.mariadb.get()
).joinToString("\",\"", "[\"", "\"]")
)
)
}
-val shadowJar = tasks.named("shadowJar")
-shadowJar.configure {
-
- val projectInfo = requireProjectInfo
- isReproducibleFileOrder = true
+val shadowJar by tasks.getting(ShadowJar::class) {
mergeServiceFiles()
- dependsOn(configurations)
- archiveClassifier.set(null as String?)
-
- minimize {
- exclude(dependency(libs.exposed.jdbc.get()))
- exclude(dependency(libs.exposed.dao.get()))
- }
- archiveVersion.set(projectInfo.versionString)
- archiveBaseName.set("${projectInfo.name}-bukkit")
- destinationDirectory = rootDir.resolve("build")
- .resolve("bukkit")
+ dependsOn(tasks.named("processResources"))
+ configurations = listOf(project.configurations.shadow.get())
+ isReproducibleFileOrder = true
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ archiveClassifier = null as String?
+ archiveVersion = requireProjectInfo.versionString
+ archiveBaseName = "${requireProjectInfo.name}-${project.name}"
+ destinationDirectory = rootProject.layout.buildDirectory.get()
+ .asFile
+ .resolve(project.name)
.resolve("plugins")
.takeIf(File::exists)
- ?: File(rootDir, "jars").also(File::mkdirs)
-
- relocate("org.bstats", projectInfo.group)
- listOf(
- "co.touchlab",
- "com.mysql",
- "google.protobuf",
- "io.github.reactivecircus",
- "ch.qos.logback",
- "com.charleskorn.kaml",
- "com.ibm.icu",
- "it.krzeminski.snakeyaml",
- "net.thauvin.erik",
- "okio",
- "org.apache",
- "org.intellij",
- "org.slf4j",
- "org.jetbrains.annotations",
- "ru.astrainteractive.klibs",
- "ru.astrainteractive.astralibs"
- ).forEach { pattern -> relocate(pattern, "${projectInfo.group}.$pattern") }
- listOf(
- "org.jetbrains.exposed",
- "kotlinx",
- ).forEach { pattern ->
- relocate(pattern, "${projectInfo.group}.$pattern") {
- exclude("kotlin/kotlin.kotlin_builtins")
+ ?: rootDir.resolve("jars")
+ dependencies {
+ // Dependencies
+ exclude(dependency("org.jetbrains:annotations"))
+ exclude("ch/qos/logback/**")
+ exclude("com/ibm/icu/**")
+ exclude("it/unimi/dsi/**")
+ exclude("javax/**")
+ exclude("mozilla/**")
+ exclude("org/apache/batik/**")
+ exclude("org/apache/commons/logging/**")
+ exclude("org/apache/xmlgraphics/**")
+ exclude("org/intellij/lang/annotations/**")
+ exclude("org/jetbrains/annotations/**")
+ exclude("org/slf4j/**")
+ exclude("org/w3c/dom/**")
+ // Root
+ if (project.name == "forge" || project.name == "neoforge") {
+ // Use kotlin-neoforge or kotlin-forge
+ exclude("kotlin/**")
+ }
+ exclude("_COROUTINE/**")
+ exclude("DebugProbesKt.bin")
+ exclude("jetty-dir.css")
+ exclude("license/**")
+ exclude("**LICENCE**")
+ exclude("**LICENSE**")
+ // Other dependencies
+ exclude("club/minnced/opus/**")
+ exclude("co/touchlab/stately/**")
+ exclude("com/google/**")
+ exclude("com/ibm/icu/**")
+ exclude("com/sun/**")
+ exclude("google/protobuf/**")
+ exclude("io/github/**")
+ exclude("io/javalin/**")
+ exclude("jakarta/servlet/**")
+ exclude("javax/annotation/**")
+ exclude("javax/servlet/**")
+ exclude("natives/**")
+ exclude("net/luckperms/**")
+ exclude("nl/altindag/**")
+ exclude("org/bouncycastle/**")
+ exclude("org/checkerframework/**")
+ exclude("org/conscrypt/**")
+ exclude("org/apache/batik/**")
+ exclude("org/apache/xmlgraphics/**")
+ exclude("org/apache/xmlcommons/**")
+ exclude("org/eclipse/**")
+ exclude("jdk/xml/**")
+ exclude("org/w3c/**")
+ exclude("tomp2p/opuswrapper/**")
+ exclude("org/slf4j/**")
+ exclude("javax/xml/**")
+ exclude("org/xml/**")
+ // META
+ exclude("META-INF/**.md")
+ exclude("META-INF/**.MD")
+ exclude("META-INF/**.txt**")
+ exclude("META-INF/**LICENCE**")
+ exclude("META-INF/com.android.tools/**")
+ exclude("META-INF/gradle-plugins/**")
+ exclude("META-INF/imports/**")
+ exclude("META-INF/kotlin-reflection.kotlin_module")
+ exclude("META-INF/license/**")
+ exclude("META-INF/maven/**")
+ exclude("META-INF/native-image/**")
+ exclude("META-INF/native/**")
+ exclude("META-INF/proguard/**")
+ exclude("META-INF/rewrite/**")
+ exclude("META-INF/services/kotlin.reflect.**")
+ if (project.name != "forge") {
+ // Don't remove in: [forge]
+ exclude("META-INF/versions/**")
+ }
+ // DEPENDENCIES
+ if (project.name == "bukkit") {
+ exclude(dependency("com.fasterxml.jackson.core:.*"))
+ exclude(dependency("com.google.code.gson:.*"))
+ exclude(dependency("com.google.crypto.tink:.*"))
+ exclude(dependency("com.google.errorprone:.*"))
+ exclude(dependency("com.mojang:brigadier"))
+ exclude(dependency("com.mysql:mysql-connector-j"))
+ exclude(dependency("mysql:mysql-connector-java"))
+ exclude(dependency("net.java.dev.jna:.*"))
+ exclude(dependency("net.kyori:.*"))
+ exclude(dependency("org.apache.xmlgraphics:.*"))
+ exclude(dependency("org.bouncycastle:.*"))
+ exclude(dependency("org.checkerframework:.*"))
+ exclude(dependency("org.conscrypt:.*"))
+ exclude(dependency("org.eclipse.jetty.toolchain:.*"))
+ exclude(dependency("org.eclipse.jetty:.*"))
+ exclude(dependency("org.xerial:sqlite-jdbc"))
}
}
+
+ relocate("org.bstats", requireProjectInfo.group)
+ // Be sure to relocate EXACT PACKAGES!!
+ // For example, relocate org.some.package instead of org
+ // Becuase relocation org will break other non-relocated dependencies such as org.minecraft
+ // Don't relocate `org.jetbrains.exposed` and `kotlin`
+ buildList {
+ add("ch.qos.logback")
+ add("club.minnced.discord")
+ add("club.minnced.opus")
+ add("co.touchlab.stately")
+ add("com.arkivanov")
+ add("com.charleskorn.kaml")
+ if (project.name != "bukkit") {
+ // Don't relocate on: [bukkit]
+ add("com.fasterxml")
+ }
+ add("com.ibm.icu")
+ add("com.neovisionaries")
+ add("dev.icerock")
+ add("gnu.trove")
+ add("google.protobuf")
+ add("io.github.reactivecircus")
+ add("it.krzeminski")
+ add("it.krzeminski.snakeyaml")
+ if (project.name != "bukkit") {
+ // Is present on: [bukkit]
+ add("javax.xml")
+ }
+ add("kotlinx")
+ add("net.dv8tion")
+ if (project.name != "bukkit") {
+ // Don't relocate on: [bukkit]
+ add("net.kyori")
+ }
+ add("net.thauvin")
+ add("okhttp3")
+ add("okio")
+ add("org.apache")
+ if (project.name != "bukkit") {
+ // Don't relocate on: [bukkit]
+ add("org.h2")
+ }
+
+ add("org.intellij")
+ add("org.jetbrains.annotations")
+// add("org.jetbrains.exposed") // Don't relocate on: [*]
+ add("org.jetbrains.kotlinx")
+ add("org.json")
+ add("org.json")
+// add("org.sqlite") // Don't relocate on: [*]
+ add("org.telegram")
+ add("org.telegram.telegrambots")
+ add("org.w3c.css")
+ add("org.w3c.dom")
+ add("ru.astrainteractive.astralibs")
+ add("ru.astrainteractive.klibs")
+ add("tomp2p.opuswrapper")
+ }.forEach { pattern -> relocate(pattern, "${requireProjectInfo.group}.shade.$pattern") }
}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemCommandRegistry.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemCommandRegistry.kt
deleted file mode 100644
index d1db451..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemCommandRegistry.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package ru.astrainteractive.astratemplate.command.additem
-
-import com.mojang.brigadier.arguments.IntegerArgumentType
-import com.mojang.brigadier.arguments.StringArgumentType
-import com.mojang.brigadier.tree.LiteralCommandNode
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import org.bukkit.Bukkit
-import org.bukkit.Material
-import ru.astrainteractive.astralibs.command.api.argumenttype.ArgumentConverter
-import ru.astrainteractive.astralibs.command.api.exception.NoPlayerException
-import ru.astrainteractive.astralibs.command.api.util.argument
-import ru.astrainteractive.astralibs.command.api.util.command
-import ru.astrainteractive.astralibs.command.api.util.requireArgument
-import ru.astrainteractive.astralibs.command.api.util.runs
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astratemplate.command.errorhandler.DefaultErrorHandler
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-
-internal class AddItemCommandRegistry(
- kyoriKrate: CachedKrate,
- private val errorHandler: DefaultErrorHandler,
- private val executor: AddItemExecutor
-) : KyoriComponentSerializer by kyoriKrate.unwrap() {
- private object MaterialArgumentConverter : ArgumentConverter {
- override fun transform(argument: String): Material {
- return Material.getMaterial(argument) ?: throw AddItemCommand.Error.ItemNotfound()
- }
- }
-
- fun createNode(): LiteralCommandNode {
- return command("add") {
- argument("player", StringArgumentType.word()) { playerArg ->
- argument("material", StringArgumentType.word()) { materialArg ->
- argument("amount", IntegerArgumentType.integer(1, 64)) { amountArg ->
- runs(errorHandler::handle) { ctx ->
- val playerName = ctx.requireArgument(playerArg)
- val player = Bukkit.getPlayerExact(playerName) ?: throw NoPlayerException(playerName)
-
- val material = ctx.requireArgument(materialArg, MaterialArgumentConverter)
-
- val amount = ctx.requireArgument(amountArg)
- executor.execute(
- AddItemCommand.Result(
- player = player,
- amount = amount,
- item = material
- )
- )
- }
- }
- }
- }
- }.build()
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemExecutor.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemExecutor.kt
deleted file mode 100644
index 5c55c80..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemExecutor.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package ru.astrainteractive.astratemplate.command.additem
-
-import org.bukkit.inventory.ItemStack
-
-internal class AddItemExecutor {
- fun execute(input: AddItemCommand.Result) {
- val itemStack = ItemStack(input.item, input.amount)
- input.player.inventory.addItem(itemStack)
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/common/CommonCommandsRegistry.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/common/CommonCommandsRegistry.kt
deleted file mode 100644
index fca9ef8..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/common/CommonCommandsRegistry.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package ru.astrainteractive.astratemplate.command.common
-
-import com.mojang.brigadier.tree.LiteralCommandNode
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import ru.astrainteractive.astralibs.command.api.util.command
-import ru.astrainteractive.astralibs.command.api.util.runs
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-import ru.astrainteractive.klibs.kstorage.util.getValue
-
-internal class CommonCommandsRegistry(
- translationKrate: CachedKrate,
- kyoriKrate: CachedKrate
-) : KyoriComponentSerializer by kyoriKrate.unwrap() {
- private val translation by translationKrate
-
- fun createNode(): LiteralCommandNode {
- return command("translation") {
- runs { ctx ->
- ctx.source.sender.sendMessage(translation.general.getByByCheck.component)
- }
- }.build()
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/damage/DamageCommandRegistry.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/damage/DamageCommandRegistry.kt
deleted file mode 100644
index 8653fb9..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/damage/DamageCommandRegistry.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package ru.astrainteractive.astratemplate.command.damage
-
-import com.mojang.brigadier.arguments.DoubleArgumentType
-import com.mojang.brigadier.arguments.StringArgumentType
-import com.mojang.brigadier.tree.LiteralCommandNode
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import org.bukkit.Bukkit
-import ru.astrainteractive.astralibs.command.api.exception.NoPlayerException
-import ru.astrainteractive.astralibs.command.api.util.argument
-import ru.astrainteractive.astralibs.command.api.util.command
-import ru.astrainteractive.astralibs.command.api.util.requireArgument
-import ru.astrainteractive.astralibs.command.api.util.requirePermission
-import ru.astrainteractive.astralibs.command.api.util.runs
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astratemplate.command.errorhandler.DefaultErrorHandler
-import ru.astrainteractive.astratemplate.core.plugin.PluginPermission
-import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-import ru.astrainteractive.klibs.kstorage.util.getValue
-
-internal class DamageCommandRegistry(
- translationKrate: CachedKrate,
- kyoriKrate: CachedKrate,
- private val errorHandler: DefaultErrorHandler
-) : KyoriComponentSerializer by kyoriKrate.unwrap() {
- private val translation by translationKrate
-
- fun createNode(): LiteralCommandNode {
- return command("adamage") {
- argument("player", StringArgumentType.word()) { playerArg ->
- runs(errorHandler::handle) { ctx ->
- ctx.requirePermission(PluginPermission.Damage)
-
- val playerName = ctx.requireArgument(playerArg)
- val player = Bukkit.getPlayerExact(playerName) ?: throw NoPlayerException(playerName)
- player.sendMessage(translation.custom.damaged(ctx.source.sender.name).component)
- player.damage(0.0)
- }
- argument("damage", DoubleArgumentType.doubleArg(0.0)) { damageArg ->
- runs(errorHandler::handle) { ctx ->
- ctx.requirePermission(PluginPermission.Damage)
-
- val playerName = ctx.requireArgument(playerArg)
- val player = Bukkit.getPlayerExact(playerName) ?: throw NoPlayerException(playerName)
- val damage = ctx.requireArgument(damageArg)
- player.sendMessage(translation.custom.damaged(ctx.source.sender.name).component)
- player.damage(damage)
- }
- }
- }
- }.build()
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/di/CommandModule.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/di/CommandModule.kt
deleted file mode 100644
index 1eb3906..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/di/CommandModule.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-package ru.astrainteractive.astratemplate.command.di
-
-import ru.astrainteractive.astralibs.command.api.registrar.PaperCommandRegistrarContext
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
-import ru.astrainteractive.astratemplate.command.additem.AddItemCommandRegistry
-import ru.astrainteractive.astratemplate.command.additem.AddItemExecutor
-import ru.astrainteractive.astratemplate.command.common.CommonCommandsRegistry
-import ru.astrainteractive.astratemplate.command.damage.DamageCommandRegistry
-import ru.astrainteractive.astratemplate.command.errorhandler.DefaultErrorHandler
-import ru.astrainteractive.astratemplate.command.gui.GuiCommandRegistry
-import ru.astrainteractive.astratemplate.command.reload.ReloadCommandRegistry
-import ru.astrainteractive.astratemplate.command.rickmorty.RickMortyCommandRegistrar
-import ru.astrainteractive.astratemplate.core.di.CoreModule
-import ru.astrainteractive.astratemplate.di.BukkitModule
-import ru.astrainteractive.astratemplate.gui.di.GuiModule
-
-internal class CommandModule(
- coreModule: CoreModule,
- bukkitModule: BukkitModule,
- guiModule: GuiModule,
- apiRemoteModule: ApiRemoteModule,
-) {
- private val commandRegistrarContext = PaperCommandRegistrarContext(
- coreModule.mainScope,
- bukkitModule.plugin
- )
- private val nodes = buildList {
- val errorHandler = DefaultErrorHandler(
- translationKrate = coreModule.translationKrate,
- kyoriKrate = bukkitModule.kyoriKrate
- )
- AddItemCommandRegistry(
- kyoriKrate = bukkitModule.kyoriKrate,
- errorHandler = errorHandler,
- executor = AddItemExecutor()
- ).createNode().run(::add)
- CommonCommandsRegistry(
- kyoriKrate = bukkitModule.kyoriKrate,
- translationKrate = coreModule.translationKrate,
- ).createNode().run(::add)
- DamageCommandRegistry(
- errorHandler = errorHandler,
- kyoriKrate = bukkitModule.kyoriKrate,
- translationKrate = coreModule.translationKrate,
- ).createNode().run(::add)
- GuiCommandRegistry(
- router = guiModule.router,
- kyoriKrate = bukkitModule.kyoriKrate,
- errorHandler = errorHandler,
- ).createNode().run(::add)
- ReloadCommandRegistry(
- plugin = bukkitModule.plugin,
- translationKrate = coreModule.translationKrate,
- kyoriKrate = bukkitModule.kyoriKrate,
- errorHandler = errorHandler,
- ).createNode().run(::add)
- RickMortyCommandRegistrar(
- scope = coreModule.ioScope,
- dispatchers = coreModule.dispatchers,
- rmApi = apiRemoteModule.rickMortyApi,
- errorHandler = errorHandler,
- ).createNode().run(::add)
- }
- val lifecycle: Lifecycle by lazy {
- Lifecycle.Lambda(
- onEnable = {
- nodes.forEach(commandRegistrarContext::registerWhenReady)
- }
- )
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/errorhandler/DefaultErrorHandler.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/errorhandler/DefaultErrorHandler.kt
deleted file mode 100644
index f7a2365..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/errorhandler/DefaultErrorHandler.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package ru.astrainteractive.astratemplate.command.errorhandler
-
-import com.mojang.brigadier.context.CommandContext
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import ru.astrainteractive.astralibs.command.api.exception.ArgumentConverterException
-import ru.astrainteractive.astralibs.command.api.exception.BadArgumentException
-import ru.astrainteractive.astralibs.command.api.exception.NoPermissionException
-import ru.astrainteractive.astralibs.command.api.exception.NoPlayerException
-import ru.astrainteractive.astralibs.command.api.exception.NoPotionEffectTypeException
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astratemplate.command.additem.AddItemCommand
-import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-import ru.astrainteractive.klibs.kstorage.util.getValue
-import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
-import ru.astrainteractive.klibs.mikro.core.logging.Logger
-
-internal class DefaultErrorHandler(
- translationKrate: CachedKrate,
- kyoriKrate: CachedKrate
-) : Logger by JUtiltLogger("AstraTemplate-DefaultErrorHandler"),
- KyoriComponentSerializer by kyoriKrate.unwrap() {
- private val translation by translationKrate
-
- fun handle(ctx: CommandContext, throwable: Throwable) {
- when (throwable) {
- is AddItemCommand.Error -> {
- when (throwable) {
- is AddItemCommand.Error.ItemNotfound -> {
- ctx.source.sender.sendMessage(translation.fault.itemNotFound.component)
- }
-
- is AddItemCommand.Error.SenderNotPlayer -> {
- ctx.source.sender.sendMessage(translation.fault.notPlayer.component)
- }
- }
- }
-
- is NoPlayerException -> {
- ctx.source.sender.sendMessage(translation.fault.playerNotExists.component)
- }
-
- is NoPermissionException -> {
- ctx.source.sender.sendMessage(translation.fault.noPermission.component)
- }
-
- is NoPotionEffectTypeException,
- is BadArgumentException,
- is ArgumentConverterException -> {
- error { "#handle intentionally unhandled error: $throwable" }
- }
-
- else -> {
- error { "#handle unhandled error: $throwable" }
- }
- }
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/gui/GuiCommandRegistry.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/gui/GuiCommandRegistry.kt
deleted file mode 100644
index b32411e..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/gui/GuiCommandRegistry.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package ru.astrainteractive.astratemplate.command.gui
-
-import com.mojang.brigadier.tree.LiteralCommandNode
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import ru.astrainteractive.astralibs.command.api.util.command
-import ru.astrainteractive.astralibs.command.api.util.requirePlayer
-import ru.astrainteractive.astralibs.command.api.util.runs
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astratemplate.command.errorhandler.DefaultErrorHandler
-import ru.astrainteractive.astratemplate.gui.router.Router
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-
-internal class GuiCommandRegistry(
- kyoriKrate: CachedKrate,
- private val router: Router,
- private val errorHandler: DefaultErrorHandler
-) : KyoriComponentSerializer by kyoriKrate.unwrap() {
-
- fun createNode(): LiteralCommandNode {
- return command("atempgui") {
- runs(errorHandler::handle) { ctx ->
- val player = ctx.requirePlayer()
- val route = Router.Route.Sample
- router.open(player, route)
- }
- }.build()
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/reload/ReloadCommandRegistry.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/reload/ReloadCommandRegistry.kt
deleted file mode 100644
index 19b65c9..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/reload/ReloadCommandRegistry.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package ru.astrainteractive.astratemplate.command.reload
-
-import com.mojang.brigadier.tree.LiteralCommandNode
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import ru.astrainteractive.astralibs.command.api.util.command
-import ru.astrainteractive.astralibs.command.api.util.requirePermission
-import ru.astrainteractive.astralibs.command.api.util.runs
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astralibs.lifecycle.LifecyclePlugin
-import ru.astrainteractive.astratemplate.command.errorhandler.DefaultErrorHandler
-import ru.astrainteractive.astratemplate.core.plugin.PluginPermission
-import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-import ru.astrainteractive.klibs.kstorage.util.getValue
-
-internal class ReloadCommandRegistry(
- translationKrate: CachedKrate,
- kyoriKrate: CachedKrate,
- private val plugin: LifecyclePlugin,
- private val errorHandler: DefaultErrorHandler
-) : KyoriComponentSerializer by kyoriKrate.unwrap() {
- private val translation by translationKrate
-
- fun createNode(): LiteralCommandNode {
- return command("atempreload") {
- runs(errorHandler::handle) { ctx ->
- ctx.requirePermission(PluginPermission.Damage)
- ctx.source.sender.sendMessage(translation.general.reload.component)
- plugin.onReload()
- ctx.source.sender.sendMessage(translation.general.reloadComplete.component)
- }
- }.build()
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/rickmorty/RickMortyCommandRegistrar.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/rickmorty/RickMortyCommandRegistrar.kt
deleted file mode 100644
index d5e5ed9..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/rickmorty/RickMortyCommandRegistrar.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package ru.astrainteractive.astratemplate.command.rickmorty
-
-import com.mojang.brigadier.arguments.IntegerArgumentType
-import com.mojang.brigadier.tree.LiteralCommandNode
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import org.bukkit.command.CommandSender
-import ru.astrainteractive.astralibs.command.api.util.argument
-import ru.astrainteractive.astralibs.command.api.util.command
-import ru.astrainteractive.astralibs.command.api.util.literal
-import ru.astrainteractive.astralibs.command.api.util.requireArgument
-import ru.astrainteractive.astralibs.command.api.util.runs
-import ru.astrainteractive.astratemplate.api.remote.RickMortyApi
-import ru.astrainteractive.astratemplate.command.errorhandler.DefaultErrorHandler
-import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
-import kotlin.random.Random
-
-internal class RickMortyCommandRegistrar(
- private val scope: CoroutineScope,
- private val dispatchers: KotlinDispatchers,
- private val rmApi: RickMortyApi,
- private val errorHandler: DefaultErrorHandler
-) {
-
- private fun send(sender: CommandSender, number: Int) {
- scope.launch(dispatchers.IO) {
- val result = rmApi.getRandomCharacter(number)
- result.onSuccess {
- sender.sendMessage("Got response: $it")
- }
- result.onFailure {
- it.printStackTrace()
- sender.sendMessage("Fail: ${it.message}")
- }
- }
- }
-
- fun createNode(): LiteralCommandNode {
- return command("rickandmorty") {
- literal("random") {
- runs(errorHandler::handle) { ctx ->
- send(
- sender = ctx.source.sender,
- number = Random.nextInt(0, 100)
- )
- }
- }
- literal("specific") {
- argument("number", IntegerArgumentType.integer()) { numberArg ->
- runs(errorHandler::handle) { ctx ->
- send(
- sender = ctx.source.sender,
- number = ctx.requireArgument(numberArg)
- )
- }
- }
- }
- }.build()
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/di/BukkitModule.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/di/BukkitModule.kt
deleted file mode 100644
index 902dcea..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/di/BukkitModule.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package ru.astrainteractive.astratemplate.di
-
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astratemplate.AstraTemplate
-import ru.astrainteractive.klibs.kstorage.api.impl.DefaultMutableKrate
-import ru.astrainteractive.klibs.kstorage.util.asCachedKrate
-
-class BukkitModule(val plugin: AstraTemplate) {
- val kyoriKrate = DefaultMutableKrate(
- loader = { null },
- factory = { KyoriComponentSerializer.Legacy }
- ).asCachedKrate()
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
index ec316eb..a62707b 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
+++ b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
@@ -1,43 +1,50 @@
package ru.astrainteractive.astratemplate.di
-import ru.astrainteractive.astralibs.async.DefaultBukkitDispatchers
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.brigadier.command.PaperMultiplatformCommands
+import ru.astrainteractive.astralibs.command.api.registrar.PaperCommandRegistrarContext
+import ru.astrainteractive.astralibs.coroutines.DefaultBukkitDispatchers
import ru.astrainteractive.astralibs.lifecycle.Lifecycle
import ru.astrainteractive.astratemplate.AstraTemplate
import ru.astrainteractive.astratemplate.api.local.di.ApiLocalModule
import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
-import ru.astrainteractive.astratemplate.command.di.CommandModule
import ru.astrainteractive.astratemplate.core.di.CoreModule
-import ru.astrainteractive.astratemplate.event.di.EventModule
-import ru.astrainteractive.astratemplate.gui.di.GuiModule
+import ru.astrainteractive.astratemplate.feature.command.di.CommandModule
+import ru.astrainteractive.astratemplate.feature.event.di.EventModule
+import ru.astrainteractive.astratemplate.feature.gui.di.BukkitGuiModule
internal class RootModule(plugin: AstraTemplate) {
- val bukkitModule: BukkitModule = BukkitModule(plugin)
-
val coreModule: CoreModule = CoreModule(
- dataFolder = bukkitModule.plugin.dataFolder,
- dispatchers = DefaultBukkitDispatchers(plugin)
+ dataFolder = plugin.dataFolder,
+ dispatchers = DefaultBukkitDispatchers(plugin),
+ commandRegistrarContextFactory = { mainScope ->
+ PaperCommandRegistrarContext(
+ mainScope = mainScope,
+ plugin = plugin
+ )
+ }
)
val apiLocalModule: ApiLocalModule = ApiLocalModule(
- dataFolder = bukkitModule.plugin.dataFolder,
configFlow = coreModule.configKrate.cachedStateFlow,
- scope = coreModule.ioScope
+ ioScope = coreModule.ioScope
)
val apiRemoteModule: ApiRemoteModule = ApiRemoteModule()
- val eventModule: EventModule = EventModule(this)
- val guiModule: GuiModule = GuiModule(
+ val eventModule: EventModule = EventModule(coreModule, plugin)
+ val guiModule: BukkitGuiModule = BukkitGuiModule(
coreModule = coreModule,
- bukkitModule = bukkitModule,
apiLocalModule = apiLocalModule
)
val commandModule: CommandModule = CommandModule(
coreModule = coreModule,
- bukkitModule = bukkitModule,
apiRemoteModule = apiRemoteModule,
- guiModule = guiModule
+ guiModule = guiModule,
+ lifecyclePlugin = plugin,
+ commandRegistrarContext = coreModule.commandRegistrarContext,
+ multiplatformCommand = MultiplatformCommand(PaperMultiplatformCommands())
)
private val lifecycles: List
@@ -49,7 +56,7 @@ internal class RootModule(plugin: AstraTemplate) {
)
val lifecycle = Lifecycle.Lambda(
onEnable = { lifecycles.forEach(Lifecycle::onEnable) },
- onDisable = { lifecycles.forEach(Lifecycle::onDisable) },
+ onDisable = { lifecycles.reversed().forEach(Lifecycle::onDisable) },
onReload = { lifecycles.forEach(Lifecycle::onReload) }
)
}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/di/EventModule.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/di/EventModule.kt
deleted file mode 100644
index 8138be0..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/di/EventModule.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package ru.astrainteractive.astratemplate.event.di
-
-import org.bukkit.event.HandlerList
-import ru.astrainteractive.astralibs.event.EventListener
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astralibs.menu.event.DefaultInventoryClickEvent
-import ru.astrainteractive.astratemplate.di.RootModule
-import ru.astrainteractive.astratemplate.event.event.BetterAnotherEvent
-import ru.astrainteractive.astratemplate.event.event.MultipleEventsDSL
-import ru.astrainteractive.astratemplate.event.event.TemplateEvent
-
-internal class EventModule(rootModule: RootModule) {
- val eventListener: EventListener = EventListener.Default()
- val inventoryClickListener: EventListener = DefaultInventoryClickEvent()
-
- private val events = buildList {
- eventListener.run(::add)
- inventoryClickListener.run(::add)
- TemplateEvent(
- kyoriKrate = rootModule.bukkitModule.kyoriKrate,
- translationKrate = rootModule.coreModule.translationKrate
- ).run(::add)
- BetterAnotherEvent().run(::add)
- }
-
- val lifecycle: Lifecycle = Lifecycle.Lambda(
- onEnable = {
- events.forEach { it.onEnable(rootModule.bukkitModule.plugin) }
- MultipleEventsDSL(
- eventListener = eventListener,
- plugin = rootModule.bukkitModule.plugin
- )
- },
- onDisable = {
- events.forEach(EventListener::onDisable)
- HandlerList.unregisterAll(rootModule.bukkitModule.plugin)
- }
- )
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/MultipleEventsDSL.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/MultipleEventsDSL.kt
deleted file mode 100644
index fb67a75..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/MultipleEventsDSL.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package ru.astrainteractive.astratemplate.event.event
-
-import org.bukkit.event.block.BlockBreakEvent
-import org.bukkit.event.entity.EntityDamageEvent
-import org.bukkit.plugin.java.JavaPlugin
-import ru.astrainteractive.astralibs.event.EventListener
-import ru.astrainteractive.astralibs.event.inlineEvent
-
-/**
- * This is a most convenient way to use bukkit events in kotlin
- */
-internal class MultipleEventsDSL(
- eventListener: EventListener,
- plugin: JavaPlugin
-) {
-
- val blockBreakEvent = inlineEvent(eventListener, plugin) {
- println("DSLEvent: blockBreakEvent ${it.player.name}")
- }
-
- val entityDamageEvent = inlineEvent(eventListener, plugin) {
- println("DSLEvent: entityDamageEvent ${it.entity.name}")
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/di/GuiModule.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/di/GuiModule.kt
deleted file mode 100644
index a2383ad..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/di/GuiModule.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package ru.astrainteractive.astratemplate.gui.di
-
-import ru.astrainteractive.astratemplate.api.local.di.ApiLocalModule
-import ru.astrainteractive.astratemplate.core.di.CoreModule
-import ru.astrainteractive.astratemplate.di.BukkitModule
-import ru.astrainteractive.astratemplate.gui.api.ItemStackSpigotAPI
-import ru.astrainteractive.astratemplate.gui.domain.GetRandomColorUseCaseImpl
-import ru.astrainteractive.astratemplate.gui.domain.SetDisplayNameUseCaseImpl
-import ru.astrainteractive.astratemplate.gui.router.Router
-import ru.astrainteractive.astratemplate.gui.router.RouterImpl
-
-internal class GuiModule(
- coreModule: CoreModule,
- bukkitModule: BukkitModule,
- apiLocalModule: ApiLocalModule
-) {
- private val getRandomColorUseCase = GetRandomColorUseCaseImpl()
- val router: Router = RouterImpl(
- ioScope = coreModule.ioScope,
- dispatchers = coreModule.dispatchers,
- kyoriKrate = bukkitModule.kyoriKrate,
- translationKrate = coreModule.translationKrate,
- localDao = apiLocalModule.localDao,
- itemStackSpigotAPi = ItemStackSpigotAPI,
- getRandomColorUseCase = getRandomColorUseCase,
- setDisplayNameUseCase = SetDisplayNameUseCaseImpl(getRandomColorUseCase)
- )
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/router/Router.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/router/Router.kt
deleted file mode 100644
index 83f0045..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/router/Router.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package ru.astrainteractive.astratemplate.gui.router
-
-import org.bukkit.entity.Player
-
-internal interface Router {
- sealed interface Route {
- data object Sample : Route
- }
-
- fun open(player: Player, route: Route)
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/router/RouterImpl.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/router/RouterImpl.kt
deleted file mode 100644
index 3e1d45f..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/router/RouterImpl.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package ru.astrainteractive.astratemplate.gui.router
-
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.bukkit.entity.Player
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astratemplate.api.local.dao.LocalDao
-import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
-import ru.astrainteractive.astratemplate.gui.api.ItemStackSpigotAPI
-import ru.astrainteractive.astratemplate.gui.domain.GetRandomColorUseCase
-import ru.astrainteractive.astratemplate.gui.domain.SetDisplayNameUseCase
-import ru.astrainteractive.astratemplate.gui.sample.feature.DefaultSampleGUIComponent
-import ru.astrainteractive.astratemplate.gui.sample.gui.SampleGUI
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
-
-@Suppress("LongParameterList")
-internal class RouterImpl(
- private val ioScope: CoroutineScope,
- private val dispatchers: KotlinDispatchers,
- private val kyoriKrate: CachedKrate,
- private val translationKrate: CachedKrate,
- private val localDao: LocalDao,
- private val itemStackSpigotAPi: ItemStackSpigotAPI,
- private val getRandomColorUseCase: GetRandomColorUseCase,
- private val setDisplayNameUseCase: SetDisplayNameUseCase
-) : Router {
- private fun buildRoute(player: Player, route: Router.Route) = when (route) {
- Router.Route.Sample -> SampleGUI(
- player = player,
- kyoriKrate = kyoriKrate,
- translationKrate = translationKrate,
- sampleComponent = DefaultSampleGUIComponent(
- localDao = localDao,
- itemStackSpigotAPi = itemStackSpigotAPi,
- getRandomColorUseCase = getRandomColorUseCase,
- setDisplayNameUseCase = setDisplayNameUseCase
- )
- )
- }
-
- override fun open(player: Player, route: Router.Route) {
- ioScope.launch {
- val gui = buildRoute(player, route)
- withContext(dispatchers.Main) { gui.open() }
- }
- }
-}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/gui/SampleGUI.kt b/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/gui/SampleGUI.kt
deleted file mode 100644
index bb10c67..0000000
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/gui/SampleGUI.kt
+++ /dev/null
@@ -1,186 +0,0 @@
-package ru.astrainteractive.astratemplate.gui.sample.gui
-
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import net.kyori.adventure.text.Component
-import org.bukkit.Material
-import org.bukkit.entity.Player
-import org.bukkit.event.inventory.InventoryClickEvent
-import org.bukkit.inventory.ItemStack
-import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
-import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astralibs.menu.holder.DefaultPlayerHolder
-import ru.astrainteractive.astralibs.menu.holder.PlayerHolder
-import ru.astrainteractive.astralibs.menu.inventory.PaginatedInventoryMenu
-import ru.astrainteractive.astralibs.menu.inventory.model.InventorySize
-import ru.astrainteractive.astralibs.menu.inventory.model.PageContext
-import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.getIndex
-import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.isLastPage
-import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showNextPage
-import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showPrevPage
-import ru.astrainteractive.astralibs.menu.slot.InventorySlot
-import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setDisplayName
-import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex
-import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack
-import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setLore
-import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setMaterial
-import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener
-import ru.astrainteractive.astratemplate.api.local.model.UserModel
-import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
-import ru.astrainteractive.astratemplate.gui.sample.feature.SampleGuiComponent
-import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-import ru.astrainteractive.klibs.kstorage.util.getValue
-
-internal class SampleGUI(
- player: Player,
- kyoriKrate: CachedKrate,
- translationKrate: CachedKrate,
- private val sampleComponent: SampleGuiComponent
-) : PaginatedInventoryMenu(),
- KyoriComponentSerializer by kyoriKrate.unwrap() {
- override val childComponents: List
- get() = listOf(sampleComponent)
- private val translation by translationKrate
-
- override val playerHolder: PlayerHolder = DefaultPlayerHolder(player)
- override var title: Component = translation.menu.menuTitle.let(::toComponent)
- override val inventorySize: InventorySize = InventorySize.XL
- override var pageContext: PageContext = PageContext(
- page = 0,
- maxItemsPerPage = inventorySize.size - InventorySize.XXS.size,
- maxItems = 0
- )
-
- private val changeModeButton: InventorySlot
- get() = InventorySlot.Builder()
- .setIndex(50)
- .setMaterial(Material.SUNFLOWER)
- .setDisplayName(
- when (sampleComponent.model.value) {
- is SampleGuiComponent.Model.Items -> "Items"
- SampleGuiComponent.Model.Loading -> "Loading"
- is SampleGuiComponent.Model.Users -> "Users"
- }
- )
- .setOnClickListener { sampleComponent.onModeChange() }
- .build()
-
- private val addUserButton: InventorySlot
- get() = InventorySlot.Builder()
- .setIndex(48)
- .setMaterial(Material.EMERALD)
- .setDisplayName(translation.menu.menuAddPlayer.let(::toComponent))
- .setOnClickListener { sampleComponent.onAddUserClicked() }
- .build()
-
- private val backPageButton: InventorySlot
- get() = InventorySlot.Builder()
- .setIndex(49)
- .setMaterial(Material.PAPER)
- .setDisplayName(translation.menu.menuClose.let(::toComponent))
- .setOnClickListener { inventory.close() }
- .build()
-
- override val nextPageButton: InventorySlot
- get() = InventorySlot.Builder()
- .setIndex(53)
- .setMaterial(Material.PAPER)
- .setDisplayName(translation.menu.menuNextPage.let(::toComponent))
- .setOnClickListener { showNextPage() }
- .build()
-
- override val prevPageButton: InventorySlot
- get() = InventorySlot.Builder()
- .setIndex(45)
- .setMaterial(Material.PAPER)
- .setDisplayName(translation.menu.menuPrevPage.let(::toComponent))
- .setOnClickListener { showPrevPage() }
- .build()
-
- override fun onInventoryClicked(e: InventoryClickEvent) {
- super.onInventoryClicked(e)
- e.isCancelled = true
- }
-
- override fun onInventoryCreated() {
- sampleComponent.onUiCreated()
- sampleComponent.model
- .onEach { state ->
- val maxItems = when (state) {
- is SampleGuiComponent.Model.Items -> state.items.size
- is SampleGuiComponent.Model.Users -> state.users.size
- SampleGuiComponent.Model.Loading -> 0
- }
- pageContext = pageContext.copy(maxItems = maxItems)
- }
- .onEach { render() }
- .launchIn(menuScope)
- }
-
- override fun render() {
- super.render()
- val state: SampleGuiComponent.Model = sampleComponent.model.value
- inventory.clear()
- changeModeButton.setInventorySlot()
- prevPageButton.setInventorySlotIf { pageContext.page > 0 }
- nextPageButton.setInventorySlotIf { !pageContext.isLastPage }
- backPageButton.setInventorySlot()
-
- when (state) {
- is SampleGuiComponent.Model.Items -> {
- setItemStacks(state.items)
- }
-
- is SampleGuiComponent.Model.Users -> {
- addUserButton.setInventorySlot()
- setUsers(state.users)
- }
-
- SampleGuiComponent.Model.Loading -> Unit
- }
- }
-
- private fun setUsers(list: List) {
- for (i in 0 until pageContext.maxItemsPerPage) {
- val index = pageContext.getIndex(i)
- if (index >= list.size) {
- continue
- }
- val user = list[index]
-
- InventorySlot.Builder()
- .setIndex(i)
- .setMaterial(Material.PLAYER_HEAD)
- .setDisplayName(user.id.toString())
- .setLore(
- listOf(
- "${sampleComponent.randomColor}discordID: ${user.discordId}",
- "${sampleComponent.randomColor}minecraftUUID: ${user.minecraftUUID}",
- "${sampleComponent.randomColor}Press LeftClick to delete user",
- "${sampleComponent.randomColor}Press MiddleClick to delete user",
- "${sampleComponent.randomColor}Press RightClick to Add Relation"
- ).map(Component::text)
- )
- .setOnClickListener { sampleComponent.onItemClicked(index, it.click) }
- .build()
- .setInventorySlot()
- }
- }
-
- private fun setItemStacks(list: List) {
- for (i in 0 until pageContext.maxItemsPerPage) {
- val index = pageContext.getIndex(i)
- if (index >= list.size) {
- continue
- }
- val itemStack = list[index]
- InventorySlot.Builder()
- .setIndex(i)
- .setItemStack(itemStack)
- .setOnClickListener { sampleComponent.onItemClicked(index, it.click) }
- .build()
- .setInventorySlot()
- }
- }
-}
diff --git a/instances/bukkit/src/main/resources/plugin.yml b/instances/bukkit/src/main/resources/plugin.yml
index e9a683e..189b985 100644
--- a/instances/bukkit/src/main/resources/plugin.yml
+++ b/instances/bukkit/src/main/resources/plugin.yml
@@ -4,12 +4,12 @@ description: '${description}'
name: '${name}'
version: '${version}'
authors: [ "${authors}" ]
-#author: "${author}"
+author: "${author}"
website: "${url}"
api-version: 1.18
-softdepend: [ ]
+softdepend: [ DiscordSRV, LuckPerms, Vault, Esssentials, Towny, PlaceholderAPI, packetevents ]
depend: [ ]
-loadbefore: [ ]
+loadbefore: [ Towny ]
libraries: ${libraries}
commands:
add:
diff --git a/instances/fabric/build.gradle.kts b/instances/fabric/build.gradle.kts
deleted file mode 100644
index 0780230..0000000
--- a/instances/fabric/build.gradle.kts
+++ /dev/null
@@ -1,145 +0,0 @@
-import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-import net.fabricmc.loom.task.RemapJarTask
-import ru.astrainteractive.gradleplugin.property.extension.ModelPropertyValueExt.requireProjectInfo
-
-plugins {
- kotlin("jvm")
- alias(libs.plugins.fabric.loom)
- alias(libs.plugins.klibs.gradle.java.core)
- alias(libs.plugins.klibs.minecraft.resource.processor)
- alias(libs.plugins.gradle.shadow)
-}
-
-dependencies {
- minecraft(libs.minecraft.fabric.mojang.get())
- mappings("net.fabricmc:yarn:${libs.versions.minecraft.fabric.yarn.get()}:v2")
- modImplementation(libs.minecraft.fabric.kotlin.get())
- modImplementation(libs.minecraft.fabric.loader.get())
- modImplementation(libs.minecraft.fabric.api.get())
- // AstraLibs
- implementation(libs.minecraft.astralibs.core)
- implementation(libs.minecraft.astralibs.command)
- // klibs
- implementation(libs.klibs.kstorage)
- implementation(libs.klibs.mikro.core)
- // Kotlin
- implementation(libs.kotlin.coroutines.core)
- implementation(libs.kotlin.serialization.json)
- implementation(libs.kotlin.serialization.kaml)
- // Driver
- implementation(libs.driver.jdbc)
- // Local
- implementation(projects.modules.apiLocal)
- implementation(projects.modules.apiRemote)
- implementation(projects.modules.core)
-}
-
-minecraftProcessResource {
- fabric()
-}
-
-val destination = rootDir.resolve("build")
- .resolve("fabric")
- .resolve("mods")
- .takeIf { it.exists() }
- ?: File(rootDir, "jars")
-
-val shadowJar by tasks.getting(ShadowJar::class) {
- mergeServiceFiles()
-// mustRunAfter(minecraftProcessResource.task)
-// dependsOn(minecraftProcessResource.task)
- configurations = listOf(project.configurations.shadow.get())
- isReproducibleFileOrder = true
- archiveClassifier = null as String?
- archiveVersion = requireProjectInfo.versionString
- archiveBaseName = "${requireProjectInfo.name}-fabric"
- dependencies {
- // deps
- exclude(dependency("org.jetbrains:annotations"))
- // deps paths
- exclude("co/touchlab/stately/**")
- exclude("club/minnced/opus/**")
- exclude("com/google/**")
- exclude("com/sun/**")
- exclude("google/protobuf/**")
- exclude("io/github/**")
- exclude("io/javalin/**")
- exclude("jakarta/servlet/**")
- exclude("javax/annotation/**")
- exclude("javax/servlet/**")
- exclude("natives/**")
- exclude("nl/altindag/**")
- exclude("org/eclipse/**")
- exclude("org/bouncycastle/**")
- exclude("org/checkerframework/**")
- exclude("org/conscrypt/**")
- exclude("tomp2p/opuswrapper/**")
- exclude("DebugProbesKt.bin")
- exclude("_COROUTINE/**")
- // meta
-// exclude("META-INF/services/**")
- exclude("META-INF/*.kotlin_module")
- exclude("META-INF/com.android.tools/**")
- exclude("META-INF/gradle-plugins/**")
- exclude("META-INF/maven/**")
- exclude("META-INF/proguard/**")
- exclude("META-INF/versions/**")
- exclude("META-INF/native/**")
- exclude("META-INF/**LICENCE**")
- }
- // Be sure to relocate EXACT PACKAGES!!
- // For example, relocate org.some.package instead of org
- // Becuase relocation org will break other non-relocated dependencies such as org.minecraft
- listOf(
- "com.fasterxml",
- "net.kyori",
- "org.h2",
- "com.neovisionaries",
- "gnu.trove",
- "org.json",
- "org.apache",
- "org.telegram",
- "okhttp3",
- "net.dv8tion",
- "okio",
- "org.slf4j",
- "kotlin",
- "kotlinx",
- "it.krzeminski",
- "net.thauvin",
- "org.jetbrains.exposed.dao",
- "org.jetbrains.exposed.exceptions",
- "org.jetbrains.exposed.sql",
- "org.jetbrains.exposed.jdbc",
- "org.jetbrains.kotlin",
- "org.jetbrains.kotlinx",
- "com.charleskorn.kaml",
- "ru.astrainteractive.klibs",
- "ru.astrainteractive.astralibs",
- "club.minnced.discord",
- "club.minnced.opus"
- ).forEach { pattern -> relocate(pattern, "${requireProjectInfo.group}.shade.$pattern") }
-}
-
-val remapJar = tasks.getByName("remapJar") {
- dependsOn(shadowJar)
- mustRunAfter(shadowJar)
- inputFile = shadowJar.archiveFile
- addNestedDependencies.set(true)
- archiveBaseName.set("${requireProjectInfo.name}-fabric")
- destinationDirectory.set(destination)
- archiveVersion = requireProjectInfo.versionString
-}
-
-tasks.assemble {
- dependsOn(remapJar)
-}
-
-artifacts {
- archives(remapJar)
- shadow(shadowJar)
-}
-
-minecraftProcessResource {
- fabric()
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/ClientEntryPoint.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/ClientEntryPoint.kt
deleted file mode 100644
index 3647d1d..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/ClientEntryPoint.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package ru.astrainteractive.astratemplate
-
-import net.fabricmc.api.ClientModInitializer
-import ru.astrainteractive.astratemplate.di.RootModule
-
-class ClientEntryPoint : ClientModInitializer {
-
- private val rootModule: RootModule by lazy {
- RootModule()
- }
-
- override fun onInitializeClient() {
- println("AstraTemplate: onInitializeClient")
- rootModule.lifecycle.onEnable()
- }
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/FabricEntryPoint.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/FabricEntryPoint.kt
deleted file mode 100644
index 4a39dd5..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/FabricEntryPoint.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package ru.astrainteractive.astratemplate
-
-import net.fabricmc.api.ModInitializer
-import ru.astrainteractive.astratemplate.di.RootModule
-
-class FabricEntryPoint : ModInitializer {
-
- private val rootModule: RootModule by lazy {
- RootModule()
- }
-
- override fun onInitialize() {
- println("AstraTemplate: onInitialize")
- rootModule.lifecycle.onEnable()
- }
-
- fun reload() {
- rootModule.lifecycle.onReload()
- }
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/Command.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/Command.kt
deleted file mode 100644
index 218acf4..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/Command.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package ru.astrainteractive.astratemplate.command.core
-
-import com.mojang.brigadier.context.CommandContext
-import net.minecraft.server.command.ServerCommandSource
-
-interface Command {
- val alias: String
- fun onCommand(commandContext: CommandContext): Int
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/CommandRegistry.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/CommandRegistry.kt
deleted file mode 100644
index a23559b..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/CommandRegistry.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package ru.astrainteractive.astratemplate.command.core
-
-interface CommandRegistry {
- fun register(command: Command)
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/DefaultCommandRegistry.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/DefaultCommandRegistry.kt
deleted file mode 100644
index 4a398ed..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/DefaultCommandRegistry.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package ru.astrainteractive.astratemplate.command.core
-
-import com.mojang.brigadier.CommandDispatcher
-import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback
-import net.minecraft.command.CommandRegistryAccess
-import net.minecraft.server.command.CommandManager
-import net.minecraft.server.command.ServerCommandSource
-
-object DefaultCommandRegistry : CommandRegistry {
-
- private class CommandRegistrationCallbackImpl(
- private val literal: String,
- private val command: Command
- ) : CommandRegistrationCallback {
- override fun register(
- dispatcher: CommandDispatcher?,
- registryAccess: CommandRegistryAccess?,
- environment: CommandManager.RegistrationEnvironment?
- ) {
- dispatcher?.register(CommandManager.literal(literal).executes(command::onCommand))
- }
- }
-
- override fun register(command: Command) {
- val callback = CommandRegistrationCallbackImpl(literal = command.alias, command = command)
- CommandRegistrationCallback.EVENT.register(callback)
- }
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/DslCommand.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/DslCommand.kt
deleted file mode 100644
index 876b97f..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/core/DslCommand.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package ru.astrainteractive.astratemplate.command.core
-
-import com.mojang.brigadier.context.CommandContext
-import net.minecraft.server.command.ServerCommandSource
-
-class DslCommand(
- override val alias: String,
- private val block: (commandContext: CommandContext) -> Unit
-) : Command {
- override fun onCommand(commandContext: CommandContext): Int {
- block.invoke(commandContext)
- return 1
- }
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/di/CommandModule.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/di/CommandModule.kt
deleted file mode 100644
index 53279e7..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/di/CommandModule.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package ru.astrainteractive.astratemplate.command.di
-
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
-import ru.astrainteractive.astratemplate.command.core.Command
-import ru.astrainteractive.astratemplate.command.core.DefaultCommandRegistry
-import ru.astrainteractive.astratemplate.command.helloworld.HelloWorldCommand
-import ru.astrainteractive.astratemplate.command.rickmorty.RickMortyCommand
-import ru.astrainteractive.astratemplate.core.di.CoreModule
-
-class CommandModule(
- private val coreModule: CoreModule,
- private val apiRemoteModule: ApiRemoteModule
-) {
- private val commands: List = listOf(
- HelloWorldCommand(),
- RickMortyCommand(
- rickMortyApi = apiRemoteModule.rickMortyApi,
- ioScope = coreModule.ioScope,
- dispatchers = coreModule.dispatchers
- )
- )
- val lifecycle: Lifecycle by lazy {
- Lifecycle.Lambda(
- onEnable = {
- commands.forEach(DefaultCommandRegistry::register)
- }
- )
- }
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/helloworld/HelloWorldCommand.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/helloworld/HelloWorldCommand.kt
deleted file mode 100644
index df4657d..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/helloworld/HelloWorldCommand.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package ru.astrainteractive.astratemplate.command.helloworld
-
-import ru.astrainteractive.astratemplate.command.core.Command
-import ru.astrainteractive.astratemplate.command.core.DslCommand
-
-class HelloWorldCommand : Command by DslCommand(
- alias = "helloworld",
- block = {
- println("Hello world")
- }
-)
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/rickmorty/RickMortyCommand.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/rickmorty/RickMortyCommand.kt
deleted file mode 100644
index b7fe9d9..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/command/rickmorty/RickMortyCommand.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ru.astrainteractive.astratemplate.command.rickmorty
-
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import ru.astrainteractive.astratemplate.api.remote.RickMortyApi
-import ru.astrainteractive.astratemplate.command.core.Command
-import ru.astrainteractive.astratemplate.command.core.DslCommand
-import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
-
-class RickMortyCommand(
- private val rickMortyApi: RickMortyApi,
- private val ioScope: CoroutineScope,
- private val dispatchers: KotlinDispatchers
-) : Command by DslCommand(
- alias = "rickmorty",
- block = {
- ioScope.launch(dispatchers.IO) {
- println("Getting character...")
- println(rickMortyApi.getRandomCharacter(1))
- }
- }
-)
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/di/FabricModule.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/di/FabricModule.kt
deleted file mode 100644
index 8e40211..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/di/FabricModule.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package ru.astrainteractive.astratemplate.di
-
-import net.fabricmc.loader.api.FabricLoader
-import java.io.File
-
-class FabricModule {
- val fabricLoader: FabricLoader
- get() = FabricLoader.getInstance()
- val configDir: File
- get() = fabricLoader.configDir.toFile()
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
deleted file mode 100644
index ec639c1..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package ru.astrainteractive.astratemplate.di
-
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.api.local.di.ApiLocalModule
-import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
-import ru.astrainteractive.astratemplate.command.di.CommandModule
-import ru.astrainteractive.astratemplate.core.di.CoreModule
-import ru.astrainteractive.klibs.mikro.core.dispatchers.DefaultKotlinDispatchers
-
-class RootModule {
- val fabricModule: FabricModule by lazy {
- FabricModule()
- }
-
- val coreModule: CoreModule by lazy {
- CoreModule(
- dataFolder = fabricModule.configDir,
- dispatchers = DefaultKotlinDispatchers
- )
- }
-
- val apiLocalModule: ApiLocalModule by lazy {
- ApiLocalModule(
- dataFolder = fabricModule.configDir,
- configFlow = coreModule.configKrate.cachedStateFlow,
- scope = coreModule.ioScope
- )
- }
-
- val apiRemoteModule: ApiRemoteModule by lazy {
- ApiRemoteModule()
- }
-
- val commandModule: CommandModule by lazy {
- CommandModule(
- coreModule = coreModule,
- apiRemoteModule = apiRemoteModule
- )
- }
-
- private val lifecycles: List
- get() = listOf(
- commandModule.lifecycle
- )
-
- val lifecycle: Lifecycle by lazy {
- Lifecycle.Lambda(
- onEnable = {
- lifecycles.forEach(Lifecycle::onEnable)
- },
- onDisable = {
- lifecycles.forEach(Lifecycle::onDisable)
- },
- onReload = {
- lifecycles.forEach(Lifecycle::onReload)
- }
- )
- }
-}
diff --git a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/mixin/FabricMixin.kt b/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/mixin/FabricMixin.kt
deleted file mode 100644
index 8072c08..0000000
--- a/instances/fabric/src/main/kotlin/ru/astrainteractive/astratemplate/mixin/FabricMixin.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-@file:Suppress("UnusedPrivateMember")
-
-package ru.astrainteractive.astratemplate.mixin
-
-import net.minecraft.client.gui.screen.TitleScreen
-import org.spongepowered.asm.mixin.Mixin
-import org.spongepowered.asm.mixin.injection.At
-import org.spongepowered.asm.mixin.injection.Inject
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
-
-@Mixin(TitleScreen::class)
-class FabricMixin {
- @Inject(at = [At("HEAD")], method = ["init()V"])
- private fun init(info: CallbackInfo) {
- println("This line is printed by an example mod mixin!")
- }
-}
diff --git a/instances/fabric/src/main/resources/assets/modid/icon.png b/instances/fabric/src/main/resources/assets/modid/icon.png
deleted file mode 100644
index 00201e8..0000000
Binary files a/instances/fabric/src/main/resources/assets/modid/icon.png and /dev/null differ
diff --git a/instances/fabric/src/main/resources/astratemplate.mixins.json b/instances/fabric/src/main/resources/astratemplate.mixins.json
deleted file mode 100644
index bba95c4..0000000
--- a/instances/fabric/src/main/resources/astratemplate.mixins.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "required": true,
- "minVersion": "0.8",
- "package": "ru.astrainteractive.astratemplate.mixin",
- "compatibilityLevel": "JAVA_17",
- "mixins": [
- ],
- "client": [
- "FabricMixin"
- ],
- "injectors": {
- "defaultRequire": 1
- }
-}
\ No newline at end of file
diff --git a/instances/fabric/src/main/resources/fabric.mod.json b/instances/fabric/src/main/resources/fabric.mod.json
deleted file mode 100644
index a6aca48..0000000
--- a/instances/fabric/src/main/resources/fabric.mod.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "schemaVersion": 1,
- "id": "astratemplate",
- "version": "${version}",
-
- "name": "Astra Template Fabric Mod",
- "description": "Template Fabric mod made for EmpireProjekt.ru",
- "authors": [
- "Makeev Roman"
- ],
- "contact": {
- "homepage": "https://t.me/empiresmp",
- "sources": "https://github.com/Astra-Interactive/AstraTemplate"
- },
-
- "license": "CC0-1.0",
- "icon": "assets/modid/icon.png",
-
- "environment": "*",
- "entrypoints": {
- "main": [
- {
- "adapter": "kotlin",
- "value": "ru.astrainteractive.astratemplate.FabricEntryPoint"
- }
- ],
- "client": [
- {
- "adapter": "kotlin",
- "value": "ru.astrainteractive.astratemplate.ClientEntryPoint"
- }
- ]
- },
- "mixins": [
- "astratemplate.mixins.json"
- ],
-
- "depends": {
- "fabricloader": ">=0.14.24",
- "fabric": "*",
- "minecraft": "1.20.x",
- "java": ">=17",
- "fabric-language-kotlin": ">=1.10.17+kotlin.1.9.22"
- },
- "suggests": {
- "another-mod": "*"
- }
-}
\ No newline at end of file
diff --git a/instances/forge/build.gradle.kts b/instances/forge/build.gradle.kts
index d261acd..f483fb2 100644
--- a/instances/forge/build.gradle.kts
+++ b/instances/forge/build.gradle.kts
@@ -1,76 +1,117 @@
-@file:Suppress("UnusedPrivateMember")
-
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-import ru.astrainteractive.gradleplugin.property.extension.ModelPropertyValueExt.requireProjectInfo
+import net.minecraftforge.jarjar.gradle.JarJar
+import net.minecraftforge.jarjar.gradle.JarJarDependencyMethods
+import ru.astrainteractive.gradleplugin.property.util.requireProjectInfo
plugins {
- kotlin("jvm")
- alias(libs.plugins.klibs.gradle.java.core)
- alias(libs.plugins.forgegradle)
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+ alias(libs.plugins.gradle.forgegradle)
+ alias(libs.plugins.gradle.forgerenamer)
alias(libs.plugins.gradle.shadow)
alias(libs.plugins.klibs.minecraft.resource.processor)
+ alias(libs.plugins.gradle.forge.jarjar)
}
-dependencies {
- minecraft(
- "net.minecraftforge",
- "forge",
- "${libs.versions.minecraft.mojang.version.get()}-${libs.versions.minecraft.forgeversion.get()}"
- )
- implementation(libs.minecraft.astralibs.core)
- implementation(libs.minecraft.astralibs.core.forge)
- implementation(libs.minecraft.astralibs.command)
-
- implementation(libs.klibs.kstorage)
- implementation(libs.klibs.mikro.core)
-
- implementation(libs.kotlin.coroutines.core)
- implementation(libs.kotlin.serialization.json)
- implementation(libs.kotlin.serialization.kaml)
-
- implementation(projects.modules.apiLocal)
- implementation(projects.modules.apiRemote)
- implementation(projects.modules.core)
- implementation(projects.modules.buildKonfig)
+repositories {
+ minecraft.mavenizer(this)
+ mavenCentral()
+ mavenLocal()
+ maven(fg.forgeMaven)
+ maven(fg.minecraftLibsMaven)
}
-minecraft {
- mappings("official", libs.versions.minecraft.mojang.version.get())
- accessTransformer(rootProject.file("build").resolve("accesstransformer.cfg"))
+dependencies {
+ shadow(libs.kotlin.coroutines.core)
+ shadow(libs.minecraft.astralibs.core)
+ shadow(libs.minecraft.astralibs.core.forge)
+ shadow(libs.minecraft.astralibs.command)
+ shadow(libs.kotlin.serialization.kaml)
+ shadow(libs.klibs.mikro.core)
+ shadow(libs.klibs.kstorage)
+ shadow(libs.minecraft.kyori.plain)
+ shadow(libs.minecraft.kyori.legacy)
+ shadow(libs.minecraft.kyori.gson)
+ shadow(projects.modules.api.local)
+ shadow(projects.modules.api.remote)
+ shadow(projects.modules.buildKonfig)
+ shadow(projects.modules.core)
+ shadow(projects.modules.featureEvent.forge)
+ shadow(projects.modules.featureCommand)
+ shadow(projects.modules.featureGui.api)
}
-configurations {
- apiElements {
- artifacts.clear()
- }
- runtimeElements {
- setExtendsFrom(emptySet())
- // Publish the jarJar
- artifacts.clear()
- outgoing.artifact(tasks.jarJar)
- }
+minecraftProcessResource {
+ forge(
+ customProperties = mapOf(
+ "minecraft_version" to libs.versions.minecraft.forgeversion.get().split("-")[0],
+ "forge_version" to libs.versions.minecraft.forgeversion.get().split("-")[1],
+ "mod_license" to "MIT License"
+ )
+ )
}
-val destination = File("/home/makeevrsergh/Desktop/server/mods/")
- .takeIf(File::exists)
- ?: File(rootDir, "jars")
-
-val reobfShadowJar = reobf.create("shadowJar")
+jarJar {
+ register()
+}
val shadowJar by tasks.getting(ShadowJar::class) {
mergeServiceFiles()
- finalizedBy(reobfShadowJar)
+ dependsOn(tasks.named("processResources"))
+ dependsOn(tasks.withType())
+ from(
+ tasks.withType()
+ .map { task -> task.archiveFile }
+ .map { archiveFile -> project.zipTree(archiveFile) },
+ { include("META-INF/jarjar/**") }
+ )
+
configurations = listOf(project.configurations.shadow.get())
isReproducibleFileOrder = true
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveClassifier = null as String?
archiveVersion = requireProjectInfo.versionString
- archiveBaseName = "${requireProjectInfo.name}-forge"
- destinationDirectory = destination
+ archiveBaseName = "${requireProjectInfo.name}-${project.name}"
+ destinationDirectory = rootProject.layout.buildDirectory.get()
+ .asFile
+ .resolve(project.name)
+ .resolve("mods")
+ .takeIf(File::exists)
+ ?: rootDir.resolve("jars")
dependencies {
+ // Dependencies
exclude(dependency("org.jetbrains:annotations"))
- exclude("co/touchlab/stately/**")
+ exclude("ch/qos/logback/**")
+ exclude("com/ibm/icu/**")
+ exclude("it/unimi/dsi/**")
+ exclude("javax/**")
+ exclude("mozilla/**")
+ exclude("org/apache/batik/**")
+ exclude("org/apache/commons/logging/**")
+ exclude("org/apache/xmlgraphics/**")
+ exclude("org/intellij/lang/annotations/**")
+ exclude("org/jetbrains/annotations/**")
+ exclude("org/slf4j/**")
+ exclude("org/w3c/dom/**")
+ exclude("org/jspecify/annotations/**")
+ // Root
+ if (project.name == "forge" || project.name == "neoforge") {
+ // Use kotlin-neoforge or kotlin-forge
+ exclude("kotlin/**")
+ }
+ exclude("_COROUTINE/**")
+ exclude("DebugProbesKt.bin")
+ exclude("jetty-dir.css")
+ exclude("license/**")
+ exclude("**LICENCE**")
+ exclude("**LICENSE**")
+ // Other dependencies
exclude("club/minnced/opus/**")
+ exclude("co/touchlab/stately/**")
exclude("com/google/**")
+ exclude("com/ibm/icu/**")
exclude("com/sun/**")
exclude("google/protobuf/**")
exclude("io/github/**")
@@ -79,54 +120,139 @@ val shadowJar by tasks.getting(ShadowJar::class) {
exclude("javax/annotation/**")
exclude("javax/servlet/**")
exclude("natives/**")
+ exclude("net/luckperms/**")
exclude("nl/altindag/**")
- exclude("org/eclipse/**")
exclude("org/bouncycastle/**")
exclude("org/checkerframework/**")
exclude("org/conscrypt/**")
+ exclude("org/apache/batik/**")
+ exclude("org/apache/xmlgraphics/**")
+ exclude("org/apache/xmlcommons/**")
+ exclude("org/eclipse/**")
+ exclude("jdk/xml/**")
+ exclude("org/w3c/**")
exclude("tomp2p/opuswrapper/**")
- exclude("DebugProbesKt.bin")
- exclude("_COROUTINE/**")
- exclude("META-INF/*.kotlin_module")
+ exclude("org/slf4j/**")
+ exclude("javax/xml/**")
+ exclude("org/xml/**")
+ exclude("org/sqlite/**")
+ // META
+ exclude("META-INF/**.md")
+ exclude("META-INF/**.MD")
+ exclude("META-INF/**.txt**")
+ exclude("META-INF/**LICENCE**")
exclude("META-INF/com.android.tools/**")
exclude("META-INF/gradle-plugins/**")
+ exclude("META-INF/imports/**")
+ exclude("META-INF/kotlin-reflection.kotlin_module")
+ exclude("META-INF/license/**")
exclude("META-INF/maven/**")
- exclude("META-INF/proguard/**")
- exclude("META-INF/versions/**")
+ exclude("META-INF/native-image/**")
exclude("META-INF/native/**")
- exclude("META-INF/**LICENCE**")
+ exclude("META-INF/proguard/**")
+ exclude("META-INF/rewrite/**")
+ exclude("META-INF/services/kotlin.reflect.**")
+ if (project.name != "forge") {
+ // Don't remove in: [forge]
+ exclude("META-INF/versions/**")
+ }
+ // DEPENDENCIES
+ if (project.name == "bukkit") {
+ exclude(dependency("com.fasterxml.jackson.core:.*"))
+ exclude(dependency("com.google.code.gson:.*"))
+ exclude(dependency("com.google.crypto.tink:.*"))
+ exclude(dependency("com.google.errorprone:.*"))
+ exclude(dependency("com.mojang:brigadier"))
+ exclude(dependency("com.mysql:mysql-connector-j"))
+ exclude(dependency("mysql:mysql-connector-java"))
+ exclude(dependency("net.java.dev.jna:.*"))
+ exclude(dependency("net.kyori:.*"))
+ exclude(dependency("org.apache.xmlgraphics:.*"))
+ exclude(dependency("org.bouncycastle:.*"))
+ exclude(dependency("org.checkerframework:.*"))
+ exclude(dependency("org.conscrypt:.*"))
+ exclude(dependency("org.eclipse.jetty.toolchain:.*"))
+ exclude(dependency("org.eclipse.jetty:.*"))
+ exclude(dependency("org.xerial:sqlite-jdbc"))
+ }
}
+ relocate("org.bstats", requireProjectInfo.group)
// Be sure to relocate EXACT PACKAGES!!
// For example, relocate org.some.package instead of org
// Becuase relocation org will break other non-relocated dependencies such as org.minecraft
- listOf(
- "com.fasterxml",
- "net.kyori",
- "org.h2",
- "com.neovisionaries",
- "gnu.trove",
- "org.json",
- "org.apache",
- "org.telegram",
- "okhttp3",
- "net.dv8tion",
- "okio",
- "org.slf4j",
- "kotlin",
- "kotlinx",
- "it.krzeminski",
- "net.thauvin",
- "org.jetbrains.exposed.dao",
- "org.jetbrains.exposed.exceptions",
- "org.jetbrains.exposed.sql",
- "org.jetbrains.exposed.jdbc",
- "org.jetbrains.kotlin",
- "org.jetbrains.kotlinx",
- "com.charleskorn.kaml",
- "ru.astrainteractive.klibs",
- "ru.astrainteractive.astralibs",
- "club.minnced.discord",
- "club.minnced.opus"
- ).forEach { pattern -> relocate(pattern, "${requireProjectInfo.group}.shade.$pattern") }
+ // Don't relocate `org.jetbrains.exposed` and `kotlin`
+ buildList {
+ add("ch.qos.logback")
+ add("club.minnced.discord")
+ add("club.minnced.opus")
+ add("co.touchlab.stately")
+ add("com.arkivanov")
+ add("com.charleskorn.kaml")
+ if (project.name != "bukkit") {
+ // Don't relocate on: [bukkit]
+ add("com.fasterxml")
+ }
+ add("com.ibm.icu")
+ add("com.neovisionaries")
+ add("dev.icerock")
+ add("gnu.trove")
+ add("google.protobuf")
+ add("io.github.reactivecircus")
+ add("it.krzeminski")
+ add("it.krzeminski.snakeyaml")
+ if (project.name != "bukkit") {
+ // Is present on: [bukkit]
+ add("javax.xml")
+ }
+ add("kotlinx")
+ add("net.dv8tion")
+ if (project.name != "bukkit") {
+ // Don't relocate on: [bukkit]
+ add("net.kyori")
+ }
+ add("net.thauvin")
+ add("okhttp3")
+ add("okio")
+ add("org.apache")
+ add("org.intellij")
+ add("org.jetbrains.annotations")
+ add("org.jetbrains.exposed") // Don't relocate on: [*]
+ add("org.jetbrains.kotlinx")
+ add("org.json")
+ add("org.json")
+ add("org.telegram")
+ add("org.telegram.telegrambots")
+ add("org.w3c.css")
+ add("org.w3c.dom")
+ add("ru.astrainteractive.astralibs")
+ add("ru.astrainteractive.klibs")
+ add("tomp2p.opuswrapper")
+ }.forEach { pattern -> relocate(pattern, "${requireProjectInfo.group}.shade.$pattern") }
+}
+
+minecraft {
+ mappings("official", libs.versions.minecraft.mojang.version.get())
+ useDefaultAccessTransformer()
}
+
+dependencies {
+ compileOnly(minecraft.dependency(libs.minecraft.forgeversion.get()))
+ "jarJar"(libs.driver.jdbc) {
+ JarJarDependencyMethods.getJarJar(this).setVersion(libs.versions.driver.jdbc)
+ }
+ "jarJar"(libs.driver.h2) {
+ JarJarDependencyMethods.getJarJar(this).setVersion(libs.versions.driver.h2)
+ }
+}
+
+renamer {
+ mappings.from(minecraft.dependency.toSrgFile)
+}
+
+val reobfShadowJar by renamer.classes(tasks.named("shadowJar")) {
+ output = input
+}
+
+shadowJar.finalizedBy(reobfShadowJar)
+reobfShadowJar.mustRunAfter(shadowJar)
diff --git a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/ForgeEntryPoint.kt b/instances/forge/src/main/java/ru/astrainteractive/astratemplate/ForgeEntryPoint.kt
deleted file mode 100644
index abca934..0000000
--- a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/ForgeEntryPoint.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-@file:Suppress("UnusedPrivateMember")
-
-package ru.astrainteractive.astratemplate
-
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import net.minecraftforge.event.RegisterCommandsEvent
-import net.minecraftforge.event.server.ServerStartedEvent
-import net.minecraftforge.event.server.ServerStoppingEvent
-import net.minecraftforge.eventbus.api.EventPriority
-import net.minecraftforge.fml.common.Mod
-import ru.astrainteractive.astralibs.event.flowEvent
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.di.RootModule
-import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
-import ru.astrainteractive.klibs.mikro.core.logging.Logger
-import javax.annotation.ParametersAreNonnullByDefault
-
-@Mod(BuildKonfig.id)
-@ParametersAreNonnullByDefault
-class ForgeEntryPoint :
- Lifecycle,
- Logger by JUtiltLogger("ForgeEntryPoint") {
- private val rootModule: RootModule = RootModule()
-
- override fun onEnable() {
- rootModule.lifecycle.onEnable()
- }
-
- override fun onDisable() {
- info { "#onDisable" }
- rootModule.lifecycle.onDisable()
- }
-
- override fun onReload() {
- rootModule.lifecycle.onReload()
- }
-
- val serverStartedEvent = flowEvent(EventPriority.HIGHEST)
- .onEach {
- info { "#serverStartedEvent" }
- onEnable()
- }.launchIn(rootModule.coreModule.ioScope)
-
- val serverStoppingEvent = flowEvent(EventPriority.HIGHEST)
- .onEach {
- info { "#serverStoppingEvent" }
- onDisable()
- }.launchIn(rootModule.coreModule.ioScope)
-
- val registerCommandsEvent = flowEvent(EventPriority.HIGHEST)
- .onEach { e ->
- info { "#registerCommandsEvent" }
- }.launchIn(rootModule.coreModule.ioScope)
-}
diff --git a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/command/di/CommandModule.kt b/instances/forge/src/main/java/ru/astrainteractive/astratemplate/command/di/CommandModule.kt
deleted file mode 100644
index 9c0bb86..0000000
--- a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/command/di/CommandModule.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ru.astrainteractive.astratemplate.command.di
-
-import ru.astrainteractive.astralibs.command.registrar.ForgeCommandRegistrarContext
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.command.helloworld.HelloCommandRegistrar
-
-class CommandModule(
- private val commandRegistrarContext: ForgeCommandRegistrarContext
-) {
- private val nodes = buildList {
- HelloCommandRegistrar().createNode().run(::add)
- }
-
- val lifecycle: Lifecycle = Lifecycle.Lambda(
- onEnable = {
- nodes.onEach(commandRegistrarContext::registerWhenReady)
- }
- )
-}
diff --git a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/command/helloworld/HelloCommandRegistrar.kt b/instances/forge/src/main/java/ru/astrainteractive/astratemplate/command/helloworld/HelloCommandRegistrar.kt
deleted file mode 100644
index 3554c1d..0000000
--- a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/command/helloworld/HelloCommandRegistrar.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package ru.astrainteractive.astratemplate.command.helloworld
-
-import com.mojang.brigadier.builder.LiteralArgumentBuilder
-import net.minecraft.commands.CommandSourceStack
-import net.minecraft.network.chat.Component
-import ru.astrainteractive.astralibs.command.util.command
-import ru.astrainteractive.astralibs.command.util.runs
-
-class HelloCommandRegistrar {
- fun createNode(): LiteralArgumentBuilder {
- return command("helloworld") {
- runs { ctx ->
- val component = Component.literal("Hello world!")
- ctx.source.source.sendSystemMessage(component)
- }
- }
- }
-}
diff --git a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/di/RootModule.kt b/instances/forge/src/main/java/ru/astrainteractive/astratemplate/di/RootModule.kt
deleted file mode 100644
index 022c0fa..0000000
--- a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/di/RootModule.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package ru.astrainteractive.astratemplate.di
-
-import kotlinx.coroutines.CoroutineDispatcher
-import net.minecraftforge.fml.loading.FMLPaths
-import ru.astrainteractive.astralibs.command.registrar.ForgeCommandRegistrarContext
-import ru.astrainteractive.astralibs.coroutine.ForgeMainDispatcher
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.api.local.di.ApiLocalModule
-import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
-import ru.astrainteractive.astratemplate.command.di.CommandModule
-import ru.astrainteractive.astratemplate.core.di.CoreModule
-import ru.astrainteractive.astratemplate.event.di.EventModule
-import ru.astrainteractive.klibs.mikro.core.dispatchers.DefaultKotlinDispatchers
-import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
-
-class RootModule {
-
- val coreModule: CoreModule by lazy {
- CoreModule(
- dataFolder = FMLPaths.CONFIGDIR.get()
- .resolve("AstraTemplate")
- .toAbsolutePath()
- .toFile(),
- dispatchers = object : KotlinDispatchers by DefaultKotlinDispatchers {
- override val Main: CoroutineDispatcher = ForgeMainDispatcher
- }
- )
- }
-
- val apiLocalModule: ApiLocalModule by lazy {
- ApiLocalModule(
- dataFolder = coreModule.dataFolder,
- configFlow = coreModule.configKrate.cachedStateFlow,
- scope = coreModule.ioScope
- )
- }
-
- val apiRemoteModule: ApiRemoteModule by lazy {
- ApiRemoteModule()
- }
-
- val commandModule by lazy {
- CommandModule(
- commandRegistrarContext = ForgeCommandRegistrarContext(
- mainScope = coreModule.mainScope
- )
- )
- }
-
- val eventsModule: EventModule by lazy {
- EventModule(coreModule = coreModule)
- }
-
- private val lifecycles: List
- get() = listOf(
- eventsModule.lifecycle,
- commandModule.lifecycle
- )
-
- val lifecycle: Lifecycle by lazy {
- Lifecycle.Lambda(
- onEnable = {
- lifecycles.forEach(Lifecycle::onEnable)
- },
- onDisable = {
- lifecycles.forEach(Lifecycle::onDisable)
- },
- onReload = {
- lifecycles.forEach(Lifecycle::onReload)
- }
- )
- }
-}
diff --git a/instances/forge/src/main/kotlin/ru/astrainteractive/astratemplate/ForgeEntryPoint.kt b/instances/forge/src/main/kotlin/ru/astrainteractive/astratemplate/ForgeEntryPoint.kt
new file mode 100644
index 0000000..633bf36
--- /dev/null
+++ b/instances/forge/src/main/kotlin/ru/astrainteractive/astratemplate/ForgeEntryPoint.kt
@@ -0,0 +1,31 @@
+package ru.astrainteractive.astratemplate
+
+import net.minecraftforge.fml.common.Mod
+import ru.astrainteractive.astralibs.lifecycle.ForgeLifecycleServer
+import ru.astrainteractive.astratemplate.di.RootModule
+import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
+import ru.astrainteractive.klibs.mikro.core.logging.Logger
+import javax.annotation.ParametersAreNonnullByDefault
+
+@Mod(BuildKonfig.id)
+@ParametersAreNonnullByDefault
+class ForgeEntryPoint :
+ Logger by JUtiltLogger("${BuildKonfig.id}-ForgeEntryPoint"),
+ ForgeLifecycleServer() {
+ private val rootModule = RootModule(this)
+
+ override fun onEnable() {
+ info { "#onEnable" }
+ rootModule.lifecycle.onEnable()
+ }
+
+ override fun onDisable() {
+ info { "#onDisable" }
+ rootModule.lifecycle.onDisable()
+ }
+
+ override fun onReload() {
+ info { "#onReaload" }
+ rootModule.lifecycle.onReload()
+ }
+}
diff --git a/instances/forge/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt b/instances/forge/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
new file mode 100644
index 0000000..20de9bc
--- /dev/null
+++ b/instances/forge/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
@@ -0,0 +1,79 @@
+package ru.astrainteractive.astratemplate.di
+
+import net.minecraftforge.fml.loading.FMLPaths
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.brigadier.command.MinecraftMultiplatformCommands
+import ru.astrainteractive.astralibs.command.registrar.ForgeCommandRegistrarContext
+import ru.astrainteractive.astralibs.coroutines.MinecraftDispatchers
+import ru.astrainteractive.astralibs.lifecycle.ForgeLifecycleServer
+import ru.astrainteractive.astralibs.lifecycle.Lifecycle
+import ru.astrainteractive.astratemplate.api.local.di.ApiLocalModule
+import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
+import ru.astrainteractive.astratemplate.core.di.CoreModule
+import ru.astrainteractive.astratemplate.feature.command.di.CommandModule
+import ru.astrainteractive.astratemplate.feature.event.di.di.EventModule
+import ru.astrainteractive.astratemplate.feature.gui.di.StubGuiModule
+import java.io.File
+
+class RootModule(forgeLifecycleServer: ForgeLifecycleServer) {
+
+ private val dataFolder = FMLPaths.CONFIGDIR.get()
+ .resolve("AstraTemplate")
+ .toAbsolutePath()
+ .toFile()
+ .also(File::mkdirs)
+ private val coreModule: CoreModule = CoreModule(
+ dataFolder = dataFolder,
+ dispatchers = MinecraftDispatchers(),
+ commandRegistrarContextFactory = ::ForgeCommandRegistrarContext
+ )
+
+ private val apiLocalModule: ApiLocalModule by lazy {
+ ApiLocalModule(
+ configFlow = coreModule.configKrate.cachedStateFlow,
+ ioScope = coreModule.ioScope
+ )
+ }
+
+ private val apiRemoteModule: ApiRemoteModule by lazy {
+ ApiRemoteModule()
+ }
+ private val guiModule = StubGuiModule()
+
+ private val commandModule by lazy {
+ CommandModule(
+ coreModule = coreModule,
+ guiModule = guiModule,
+ apiRemoteModule = apiRemoteModule,
+ lifecyclePlugin = forgeLifecycleServer,
+ commandRegistrarContext = coreModule.commandRegistrarContext,
+ multiplatformCommand = MultiplatformCommand(MinecraftMultiplatformCommands())
+ )
+ }
+
+ val eventsModule by lazy {
+ EventModule(coreModule = coreModule)
+ }
+
+ private val lifecycles: List
+ get() = listOf(
+ coreModule.lifecycle,
+ eventsModule.lifecycle,
+ commandModule.lifecycle,
+ apiLocalModule.lifecycle
+ )
+
+ val lifecycle: Lifecycle by lazy {
+ Lifecycle.Lambda(
+ onEnable = {
+ lifecycles.forEach(Lifecycle::onEnable)
+ },
+ onDisable = {
+ lifecycles.reversed().forEach(Lifecycle::onDisable)
+ },
+ onReload = {
+ lifecycles.forEach(Lifecycle::onReload)
+ }
+ )
+ }
+}
diff --git a/instances/forge/src/main/resources/META-INF/mods.toml b/instances/forge/src/main/resources/META-INF/mods.toml
index 96a5ab0..b87a680 100644
--- a/instances/forge/src/main/resources/META-INF/mods.toml
+++ b/instances/forge/src/main/resources/META-INF/mods.toml
@@ -1,10 +1,24 @@
-license="MIT"
modLoader="javafml"
-loaderVersion="[34,)"
+loaderVersion="[47,)"
+license="${mod_license}"
+
[[mods]]
-modId="${modId}"
-version="${version}"
-displayName="${displayName}"
-authors="${authors}"
-logoFile="icon.png"
-description="${description}"
\ No newline at end of file
+modId="${mod_id}"
+version="${mod_version}"
+displayName="${mod_name}"
+authors="${mod_authors}"
+description='''${mod_description}'''
+
+[[dependencies.${mod_id}]]
+modId="forge"
+mandatory=true
+versionRange="[${forge_version},)"
+ordering="NONE"
+side="BOTH"
+
+[[dependencies.${mod_id}]]
+modId="minecraft"
+mandatory=true
+versionRange="[${minecraft_version},)"
+ordering="NONE"
+side="BOTH"
diff --git a/instances/forge/src/main/resources/icon.png b/instances/forge/src/main/resources/icon.png
deleted file mode 100644
index 00201e8..0000000
Binary files a/instances/forge/src/main/resources/icon.png and /dev/null differ
diff --git a/instances/forge/src/main/resources/pack.mcmeta b/instances/forge/src/main/resources/pack.mcmeta
deleted file mode 100644
index 6fcf1b0..0000000
--- a/instances/forge/src/main/resources/pack.mcmeta
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "pack": {
- "description": "AstraTemplate pack.mcmeta",
- "pack_format": 8,
- "_comment": "Some random comment"
- }
-}
\ No newline at end of file
diff --git a/instances/neoforge/build.gradle.kts b/instances/neoforge/build.gradle.kts
new file mode 100644
index 0000000..b8879f4
--- /dev/null
+++ b/instances/neoforge/build.gradle.kts
@@ -0,0 +1,175 @@
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+import ru.astrainteractive.gradleplugin.property.util.requireProjectInfo
+
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+ alias(libs.plugins.gradle.neoforgegradle)
+ alias(libs.plugins.klibs.minecraft.resource.processor)
+ alias(libs.plugins.gradle.shadow)
+}
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+dependencies {
+ shadow(libs.kotlin.coroutines.core)
+ shadow(libs.minecraft.astralibs.core)
+ shadow(libs.minecraft.astralibs.core.neoforge)
+ shadow(libs.minecraft.astralibs.command)
+ shadow(libs.kotlin.serialization.kaml)
+ shadow(libs.klibs.mikro.core)
+ shadow(libs.klibs.kstorage)
+ shadow(libs.minecraft.kyori.plain)
+ shadow(libs.minecraft.kyori.legacy)
+ shadow(libs.minecraft.kyori.gson)
+ shadow(projects.modules.api.local)
+ shadow(projects.modules.api.remote)
+ shadow(projects.modules.buildKonfig)
+ shadow(projects.modules.core)
+ shadow(projects.modules.featureEvent.neoforge)
+ shadow(projects.modules.featureCommand)
+ shadow(projects.modules.featureGui.api)
+}
+
+minecraftProcessResource {
+ neoForge(
+ customProperties = mapOf(
+ "minecraft_version" to libs.versions.minecraft.mojang.version.get(),
+ "minecraft_version_range" to listOf(libs.versions.minecraft.mojang.version.get())
+ .joinToString(","),
+ "neo_version" to "neo_version",
+ "neo_version_range" to "[${libs.versions.minecraft.neoforgeversion.get()},)",
+ )
+ )
+}
+
+val shadowJar by tasks.getting(ShadowJar::class) {
+ mergeServiceFiles()
+ dependsOn(tasks.named("processResources"))
+ configurations = listOf(project.configurations.shadow.get())
+ isReproducibleFileOrder = true
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ archiveClassifier = null as String?
+ archiveVersion = requireProjectInfo.versionString
+ archiveBaseName = "${requireProjectInfo.name}-${project.name}"
+ destinationDirectory = rootProject.layout.buildDirectory.get()
+ .asFile
+ .resolve(project.name)
+ .resolve("mods")
+ .takeIf(File::exists)
+ ?: rootDir.resolve("jars")
+ dependencies {
+ // Dependencies
+ exclude(dependency("org.jetbrains:annotations"))
+ // Root
+// exclude("kotlin/**") // Use kotlin-neoforge or kotlin-forge
+ exclude("_COROUTINE/**")
+ exclude("DebugProbesKt.bin")
+ exclude("jetty-dir.css")
+ exclude("license/**")
+ exclude("**LICENCE**")
+ exclude("**LICENSE**")
+ // Other dependencies
+ exclude("club/minnced/opus/**")
+ exclude("co/touchlab/stately/**")
+ exclude("com/google/**")
+ exclude("com/ibm/icu/**")
+ exclude("com/sun/**")
+ exclude("google/protobuf/**")
+ exclude("io/github/**")
+ exclude("io/javalin/**")
+ exclude("jakarta/servlet/**")
+ exclude("javax/annotation/**")
+ exclude("javax/servlet/**")
+ exclude("natives/**")
+ exclude("net/luckperms/**")
+ exclude("nl/altindag/**")
+ exclude("org/bouncycastle/**")
+ exclude("org/checkerframework/**")
+ exclude("org/conscrypt/**")
+ exclude("org/apache/batik/**")
+ exclude("org/apache/xmlgraphics/**")
+ exclude("org/apache/xmlcommons/**")
+ exclude("org/eclipse/**")
+ exclude("jdk/xml/**")
+ exclude("org/w3c/**")
+ exclude("tomp2p/opuswrapper/**")
+ exclude("org/slf4j/**")
+ exclude("javax/xml/**")
+ exclude("org/xml/**")
+ // META
+ exclude("META-INF/**.md")
+ exclude("META-INF/**.MD")
+ exclude("META-INF/**.txt**")
+ exclude("META-INF/**LICENCE**")
+ exclude("META-INF/com.android.tools/**")
+ exclude("META-INF/gradle-plugins/**")
+ exclude("META-INF/imports/**")
+ exclude("META-INF/kotlin-reflection.kotlin_module")
+ exclude("META-INF/license/**")
+ exclude("META-INF/maven/**")
+ exclude("META-INF/native-image/**")
+ exclude("META-INF/native/**")
+ exclude("META-INF/proguard/**")
+ exclude("META-INF/rewrite/**")
+ exclude("META-INF/services/kotlin.reflect.**")
+// exclude("META-INF/versions/**") // Don't remove in Forge
+ }
+
+ // Be sure to relocate EXACT PACKAGES!!
+ // For example, relocate org.some.package instead of org
+ // Becuase relocation org will break other non-relocated dependencies such as org.minecraft
+ // Don't relocate `org.jetbrains.exposed` and `kotlin`
+ listOf(
+ "ch.qos.logback",
+ "club.minnced.discord",
+ "club.minnced.opus",
+ "co.touchlab.stately",
+ "com.arkivanov",
+ "com.charleskorn.kaml",
+ "com.fasterxml",
+ "com.ibm.icu",
+ "com.neovisionaries",
+ "dev.icerock",
+ "gnu.trove",
+ "google.protobuf",
+ "io.github.reactivecircus",
+ "it.krzeminski",
+ "it.krzeminski.snakeyaml",
+// "javax.xml", // Is present
+ "kotlinx",
+ "net.dv8tion",
+ "net.kyori",
+ "net.thauvin",
+ "okhttp3",
+ "okio",
+ "org.apache",
+ "org.h2",
+ "org.intellij",
+ "org.jetbrains.annotations",
+ "org.jetbrains.exposed", // Don't relocate
+// "org.jetbrains.kotlin", // Don't relocate
+ "org.jetbrains.kotlinx",
+ "org.json",
+ "org.json",
+// "org.slf4j", // Is present
+ "org.sqlite",
+ "org.telegram",
+ "org.telegram.telegrambots",
+ "org.w3c.css",
+ "org.w3c.dom",
+// "org.xml.sax", // Is present
+ "ru.astrainteractive.astralibs",
+ "ru.astrainteractive.klibs",
+ "tomp2p.opuswrapper",
+ ).forEach { pattern -> relocate(pattern, "${requireProjectInfo.group}.shade.$pattern") }
+}
+
+dependencies {
+ compileOnly(libs.minecraft.neoforgeversion)
+}
diff --git a/instances/neoforge/gradle.properties b/instances/neoforge/gradle.properties
new file mode 100644
index 0000000..4de3651
--- /dev/null
+++ b/instances/neoforge/gradle.properties
@@ -0,0 +1 @@
+klibs.java.source=21
diff --git a/instances/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/NeoForgeEntryPoint.kt b/instances/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/NeoForgeEntryPoint.kt
new file mode 100755
index 0000000..9df2130
--- /dev/null
+++ b/instances/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/NeoForgeEntryPoint.kt
@@ -0,0 +1,31 @@
+package ru.astrainteractive.astratemplate
+
+import net.neoforged.fml.common.Mod
+import ru.astrainteractive.astralibs.lifecycle.ForgeLifecycleServer
+import ru.astrainteractive.astratemplate.di.RootModule
+import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
+import ru.astrainteractive.klibs.mikro.core.logging.Logger
+import javax.annotation.ParametersAreNonnullByDefault
+
+@Mod(BuildKonfig.id)
+@ParametersAreNonnullByDefault
+class NeoForgeEntryPoint :
+ Logger by JUtiltLogger("${BuildKonfig.id}-NeoForgeEntryPoint"),
+ ForgeLifecycleServer() {
+ private val rootModule = RootModule(this)
+
+ override fun onEnable() {
+ info { "#onEnable" }
+ rootModule.lifecycle.onEnable()
+ }
+
+ override fun onDisable() {
+ info { "#onDisable" }
+ rootModule.lifecycle.onDisable()
+ }
+
+ override fun onReload() {
+ info { "#onReaload" }
+ rootModule.lifecycle.onReload()
+ }
+}
diff --git a/instances/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt b/instances/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
new file mode 100644
index 0000000..2436b50
--- /dev/null
+++ b/instances/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
@@ -0,0 +1,76 @@
+package ru.astrainteractive.astratemplate.di
+
+import net.neoforged.fml.loading.FMLPaths
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.brigadier.command.MinecraftMultiplatformCommands
+import ru.astrainteractive.astralibs.command.registrar.NeoForgeCommandRegistrarContext
+import ru.astrainteractive.astralibs.coroutines.MinecraftDispatchers
+import ru.astrainteractive.astralibs.lifecycle.ForgeLifecycleServer
+import ru.astrainteractive.astralibs.lifecycle.Lifecycle
+import ru.astrainteractive.astratemplate.api.local.di.ApiLocalModule
+import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
+import ru.astrainteractive.astratemplate.core.di.CoreModule
+import ru.astrainteractive.astratemplate.feature.command.di.CommandModule
+import ru.astrainteractive.astratemplate.feature.event.di.di.EventModule
+import ru.astrainteractive.astratemplate.feature.gui.di.StubGuiModule
+import java.io.File
+
+class RootModule(forgeLifecycleServer: ForgeLifecycleServer) {
+ private val dataFolder = FMLPaths.CONFIGDIR.get()
+ .resolve("AstraTemplate")
+ .toAbsolutePath()
+ .toFile()
+ .also(File::mkdirs)
+ private val coreModule: CoreModule = CoreModule(
+ dataFolder = dataFolder,
+ dispatchers = MinecraftDispatchers(),
+ commandRegistrarContextFactory = ::NeoForgeCommandRegistrarContext
+ )
+
+ val apiLocalModule: ApiLocalModule by lazy {
+ ApiLocalModule(
+ configFlow = coreModule.configKrate.cachedStateFlow,
+ ioScope = coreModule.ioScope
+ )
+ }
+
+ val apiRemoteModule: ApiRemoteModule by lazy {
+ ApiRemoteModule()
+ }
+ private val guiModule = StubGuiModule()
+ private val commandModule by lazy {
+ CommandModule(
+ coreModule = coreModule,
+ guiModule = guiModule,
+ apiRemoteModule = apiRemoteModule,
+ lifecyclePlugin = forgeLifecycleServer,
+ commandRegistrarContext = coreModule.commandRegistrarContext,
+ multiplatformCommand = MultiplatformCommand(MinecraftMultiplatformCommands())
+ )
+ }
+
+ private val eventsModule by lazy {
+ EventModule(coreModule = coreModule)
+ }
+
+ private val lifecycles: List
+ get() = listOf(
+ coreModule.lifecycle,
+ eventsModule.lifecycle,
+ commandModule.lifecycle,
+ )
+
+ val lifecycle: Lifecycle by lazy {
+ Lifecycle.Lambda(
+ onEnable = {
+ lifecycles.forEach(Lifecycle::onEnable)
+ },
+ onDisable = {
+ lifecycles.forEach(Lifecycle::onDisable)
+ },
+ onReload = {
+ lifecycles.forEach(Lifecycle::onReload)
+ }
+ )
+ }
+}
diff --git a/instances/neoforge/src/main/resources/META-INF/mods.toml b/instances/neoforge/src/main/resources/META-INF/mods.toml
new file mode 100644
index 0000000..2c93186
--- /dev/null
+++ b/instances/neoforge/src/main/resources/META-INF/mods.toml
@@ -0,0 +1,23 @@
+modLoader = "javafml"
+loaderVersion = "[1,)"
+
+[[mods]]
+modId = "${mod_id}"
+version = "${mod_version}"
+displayName = "${mod_name}"
+authors = "[${mod_authors}]"
+description = '''${mod_description}'''
+
+[[dependencies.${ mod_id }]]
+modId = "neoforge"
+type = "required"
+versionRange = "${neo_version_range}"
+ordering = "NONE"
+side = "BOTH"
+
+[[dependencies.${ mod_id }]]
+modId = "minecraft"
+type = "required"
+versionRange = "[${minecraft_version_range}]"
+ordering = "NONE"
+side = "BOTH"
diff --git a/instances/velocity/build.gradle.kts b/instances/velocity/build.gradle.kts
deleted file mode 100644
index 8a7ca3f..0000000
--- a/instances/velocity/build.gradle.kts
+++ /dev/null
@@ -1,89 +0,0 @@
-import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-import org.gradle.kotlin.dsl.named
-import ru.astrainteractive.gradleplugin.property.extension.ModelPropertyValueExt.requireProjectInfo
-
-plugins {
- kotlin("jvm")
- alias(libs.plugins.gradle.shadow)
- alias(libs.plugins.gradle.buildconfig)
- alias(libs.plugins.klibs.minecraft.resource.processor)
-}
-
-dependencies {
- implementation(libs.kotlin.coroutines.core)
- implementation(libs.kotlin.serialization.json)
- implementation(libs.kotlin.serialization.kaml)
- implementation(libs.minecraft.astralibs.core)
- implementation(libs.minecraft.astralibs.command)
-
- implementation(libs.klibs.kstorage)
- implementation(libs.klibs.mikro.core)
-
- compileOnly(libs.minecraft.velocity.api)
- annotationProcessor(libs.minecraft.velocity.api)
-
- implementation(projects.modules.core)
- implementation(projects.modules.buildKonfig)
-}
-
-minecraftProcessResource {
- velocity(
- customProperties = mapOf(
- "dependencies" to listOf(
- libs.driver.h2.get(),
- libs.driver.jdbc.get(),
- libs.driver.mysql.get(),
- ).joinToString("\",\"", "[\"", "\"]")
- )
- )
-}
-
-val shadowJar = tasks.named("shadowJar")
-shadowJar.configure {
-
- val projectInfo = requireProjectInfo
- isReproducibleFileOrder = true
- mergeServiceFiles()
- dependsOn(configurations)
- archiveClassifier.set(null as String?)
-
- minimize {
- exclude(dependency(libs.exposed.jdbc.get()))
- exclude(dependency(libs.exposed.dao.get()))
- }
- archiveVersion.set(projectInfo.versionString)
- archiveBaseName.set("${projectInfo.name}-velocity")
- destinationDirectory = rootDir.resolve("build")
- .resolve("velocity")
- .resolve("plugins")
- .takeIf(File::exists)
- ?: File(rootDir, "jars").also(File::mkdirs)
-
- relocate("org.bstats", projectInfo.group)
- listOf(
- "co.touchlab",
- "com.mysql",
- "google.protobuf",
- "io.github.reactivecircus",
- "ch.qos.logback",
- "com.charleskorn.kaml",
- "com.ibm.icu",
- "it.krzeminski.snakeyaml",
- "net.thauvin.erik",
- "okio",
- "org.apache",
- "org.intellij",
- "org.slf4j",
- "org.jetbrains.annotations",
- "ru.astrainteractive.klibs",
- "ru.astrainteractive.astralibs"
- ).forEach { pattern -> relocate(pattern, "${projectInfo.group}.$pattern") }
- listOf(
- "org.jetbrains.exposed",
- "kotlinx",
- ).forEach { pattern ->
- relocate(pattern, "${projectInfo.group}.$pattern") {
- exclude("kotlin/kotlin.kotlin_builtins")
- }
- }
-}
diff --git a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/AstraTemplate.kt b/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/AstraTemplate.kt
deleted file mode 100644
index 1e8c41e..0000000
--- a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/AstraTemplate.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-@file:Suppress("UnusedPrivateMember")
-
-package ru.astrainteractive.astratemplate
-
-import com.google.inject.Inject
-import com.google.inject.Injector
-import com.velocitypowered.api.event.Subscribe
-import com.velocitypowered.api.event.proxy.ProxyInitializeEvent
-import com.velocitypowered.api.plugin.Plugin
-import com.velocitypowered.api.plugin.annotation.DataDirectory
-import com.velocitypowered.api.proxy.ProxyServer
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.command.api.VelocityCommandRegistryContext
-import ru.astrainteractive.astratemplate.command.reload.ReloadCommandRegistry
-import ru.astrainteractive.astratemplate.di.RootModule
-import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
-import ru.astrainteractive.klibs.mikro.core.logging.Logger
-import java.nio.file.Path
-import org.slf4j.Logger as Slf4jLogger
-
-@Plugin(
- id = BuildKonfig.id,
- name = BuildKonfig.name,
- version = BuildKonfig.version,
- url = BuildKonfig.url,
- description = BuildKonfig.description,
- authors = [BuildKonfig.author_0],
- dependencies = []
-)
-class AstraTemplate @Inject constructor(
- private val injector: Injector,
- private val server: ProxyServer,
- private val logger: Slf4jLogger,
- @DataDirectory
- private val dataDirectory: Path
-) : Logger by JUtiltLogger("AstraTemplate").withoutParentHandlers() {
- private val rootModule = RootModule()
- private val lifecycles: List
- get() = listOf(
- rootModule.coreModule.lifecycle,
- )
-
- @Subscribe
- fun onProxyInitialization(event: ProxyInitializeEvent?) {
- // Do some operation demanding access to the Velocity API here.
- // For instance, we could register an event:
- rootModule.velocityModule.apply {
- this.injector = (this@AstraTemplate.injector)
- this.server = (this@AstraTemplate.server)
- this.logger = (this@AstraTemplate.logger)
- this.dataDirectory = (this@AstraTemplate.dataDirectory)
- this.plugin = (this@AstraTemplate)
- }
-
- info { "Hello there! I made my first plugin with Velocity" }
- info { "Here's your configuration: ${rootModule.coreModule.configKrate}." }
-
- ReloadCommandRegistry(
- registryContext = VelocityCommandRegistryContext(
- proxyServer = rootModule.velocityModule.server,
- plugin = rootModule.velocityModule.plugin
- )
- ).register()
- }
-
- fun reload() {
- lifecycles.forEach(Lifecycle::onReload)
- }
-}
diff --git a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/api/VelocityCommandRegistry.kt b/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/api/VelocityCommandRegistry.kt
deleted file mode 100644
index d18d6f7..0000000
--- a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/api/VelocityCommandRegistry.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ru.astrainteractive.astratemplate.command.api
-
-import com.velocitypowered.api.command.SimpleCommand
-
-fun VelocityCommandRegistryContext.registerCommand(
- alias: String,
- parse: (SimpleCommand.Invocation) -> Result,
- onSuccess: (T) -> Unit,
- onFailure: (SimpleCommand.Invocation, Throwable) -> Unit = { _, _ -> }
-) {
- val commandMeta = this.proxyServer.commandManager
- .metaBuilder(alias)
- .aliases(alias)
- .plugin(this.plugin)
- .build()
- val velocityCommand = SimpleCommand { ctx ->
- parse.invoke(ctx)
- .onFailure { throwable -> onFailure.invoke(ctx, throwable) }
- .onSuccess { result -> onSuccess.invoke(result) }
- }
- proxyServer.commandManager.register(commandMeta, velocityCommand)
-}
diff --git a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/api/VelocityCommandRegistryContext.kt b/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/api/VelocityCommandRegistryContext.kt
deleted file mode 100644
index 9cd7b9d..0000000
--- a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/api/VelocityCommandRegistryContext.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package ru.astrainteractive.astratemplate.command.api
-
-import com.velocitypowered.api.proxy.ProxyServer
-import ru.astrainteractive.astratemplate.AstraTemplate
-
-class VelocityCommandRegistryContext(
- val proxyServer: ProxyServer,
- val plugin: AstraTemplate
-)
diff --git a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/reload/ReloadCommandRegistry.kt b/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/reload/ReloadCommandRegistry.kt
deleted file mode 100644
index c6aa8b5..0000000
--- a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/command/reload/ReloadCommandRegistry.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package ru.astrainteractive.astratemplate.command.reload
-
-import com.velocitypowered.api.command.CommandSource
-import com.velocitypowered.api.command.SimpleCommand
-import net.kyori.adventure.text.Component
-import ru.astrainteractive.astralibs.command.api.exception.CommandException
-import ru.astrainteractive.astratemplate.command.api.VelocityCommandRegistryContext
-import ru.astrainteractive.astratemplate.command.api.registerCommand
-import ru.astrainteractive.astratemplate.core.plugin.PluginPermission
-
-class ReloadCommandRegistry(private val registryContext: VelocityCommandRegistryContext) {
-
- private interface ReloadCommand {
- sealed interface Result {
- data class Success(val source: CommandSource) : Result
- }
-
- sealed class ReloadException(message: String) : CommandException(message) {
- class WrongUsage : ReloadException("WrongUsage")
- class NoPermission : ReloadException("NoPermission")
- }
- }
-
- private fun execute(result: ReloadCommand.Result) {
- if (result !is ReloadCommand.Result.Success) return
- result.source.sendMessage(Component.text("Success"))
- registryContext.plugin.reload()
- }
-
- private fun parse(ctx: SimpleCommand.Invocation): Result {
- return runCatching {
- if (!ctx.source().hasPermission(PluginPermission.Reload.value)) {
- throw ReloadCommand.ReloadException.NoPermission()
- }
- if (ctx.arguments().isNotEmpty()) {
- throw ReloadCommand.ReloadException.WrongUsage()
- }
- ReloadCommand.Result.Success(ctx.source())
- }
- }
-
- private fun handle(ctx: SimpleCommand.Invocation, throwable: Throwable) {
- when (throwable) {
- is ReloadCommand.ReloadException.NoPermission -> {
- ctx.source().sendMessage(Component.text("No permission"))
- }
-
- is ReloadCommand.ReloadException.WrongUsage -> {
- ctx.source().sendMessage(Component.text("Wrong usage"))
- }
- }
- }
-
- fun register() {
- registryContext.registerCommand(
- alias = "reload",
- parse = ::parse,
- onSuccess = ::execute,
- onFailure = ::handle
- )
- }
-}
diff --git a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt b/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
deleted file mode 100644
index d2351a1..0000000
--- a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/di/RootModule.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package ru.astrainteractive.astratemplate.di
-
-import ru.astrainteractive.astratemplate.core.di.CoreModule
-import ru.astrainteractive.klibs.mikro.core.dispatchers.DefaultKotlinDispatchers
-
-class RootModule {
- val velocityModule = VelocityModule()
-
- val coreModule: CoreModule by lazy {
- CoreModule(
- dataFolder = velocityModule.dataDirectory.toFile(),
- dispatchers = DefaultKotlinDispatchers
- )
- }
-}
diff --git a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/di/VelocityModule.kt b/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/di/VelocityModule.kt
deleted file mode 100644
index 1bf152e..0000000
--- a/instances/velocity/src/main/kotlin/ru/astrainteractive/astratemplate/di/VelocityModule.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package ru.astrainteractive.astratemplate.di
-
-import com.google.inject.Injector
-import com.velocitypowered.api.proxy.ProxyServer
-import org.slf4j.Logger
-import ru.astrainteractive.astratemplate.AstraTemplate
-import java.nio.file.Path
-
-class VelocityModule {
- lateinit var injector: Injector
- lateinit var server: ProxyServer
- lateinit var plugin: AstraTemplate
- lateinit var logger: Logger
- lateinit var dataDirectory: Path
-}
diff --git a/instances/velocity/src/main/resources/velocity-plugin.json b/instances/velocity/src/main/resources/velocity-plugin.json
deleted file mode 100644
index 576630e..0000000
--- a/instances/velocity/src/main/resources/velocity-plugin.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "id": "${id}",
- "name": "${name}",
- "version": "${version}",
- "url": "${url}",
- "authors": [
- "${authors}"
- ],
- "dependencies": [
- "${dependencies}"
- ],
- "main": "${main}"
-}
\ No newline at end of file
diff --git a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/di/ApiLocalModule.kt b/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/di/ApiLocalModule.kt
deleted file mode 100644
index 0b6a420..0000000
--- a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/di/ApiLocalModule.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package ru.astrainteractive.astratemplate.api.local.di
-
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.firstOrNull
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.launch
-import org.jetbrains.exposed.sql.Database
-import org.jetbrains.exposed.sql.SchemaUtils
-import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger
-import org.jetbrains.exposed.sql.addLogger
-import org.jetbrains.exposed.sql.transactions.TransactionManager
-import org.jetbrains.exposed.sql.transactions.transaction
-import ru.astrainteractive.astralibs.lifecycle.Lifecycle
-import ru.astrainteractive.astratemplate.api.local.dao.LocalDao
-import ru.astrainteractive.astratemplate.api.local.dao.LocalDaoImpl
-import ru.astrainteractive.astratemplate.api.local.entity.UserRatingTable
-import ru.astrainteractive.astratemplate.api.local.entity.UserTable
-import ru.astrainteractive.astratemplate.core.plugin.PluginConfiguration
-import ru.astrainteractive.klibs.mikro.core.coroutines.mapCached
-import ru.astrainteractive.klibs.mikro.exposed.model.DatabaseConfiguration
-import ru.astrainteractive.klibs.mikro.exposed.util.connect
-import java.io.File
-
-class ApiLocalModule(
- dataFolder: File,
- configFlow: Flow,
- scope: CoroutineScope
-) {
- private val databaseFlow = configFlow
- .map { it.database }
- .distinctUntilChanged()
- .mapCached(scope) { config, previous: Database? ->
- previous?.connector?.invoke()?.close()
- previous?.run(TransactionManager::closeAndUnregister)
- val database = dataFolder.resolve("users_db").absolutePath
- .let(DatabaseConfiguration::H2)
- .connect()
- TransactionManager.manager.defaultIsolationLevel = java.sql.Connection.TRANSACTION_SERIALIZABLE
- transaction(database) {
- addLogger(Slf4jSqlDebugLogger)
- SchemaUtils.create(
- UserRatingTable,
- UserTable
- )
- }
- database
- }
-
- val localDao: LocalDao = LocalDaoImpl(
- databaseFlow = databaseFlow,
- )
-
- val lifecycle: Lifecycle by lazy {
- Lifecycle.Lambda(
- onDisable = {
- GlobalScope.launch {
- databaseFlow.firstOrNull()?.let { database ->
- database.connector.invoke().close()
- database.run(TransactionManager::closeAndUnregister)
- }
- }
- }
- )
- }
-}
diff --git a/modules/api-remote/build.gradle.kts b/modules/api-remote/build.gradle.kts
deleted file mode 100644
index c1abea4..0000000
--- a/modules/api-remote/build.gradle.kts
+++ /dev/null
@@ -1,10 +0,0 @@
-plugins {
- kotlin("jvm")
- alias(libs.plugins.kotlin.serialization)
-}
-
-dependencies {
- implementation(libs.kotlin.coroutines.core)
- implementation(libs.kotlin.serialization.json)
- implementation(libs.kotlin.serialization.kaml)
-}
diff --git a/modules/api-local/build.gradle.kts b/modules/api/local/build.gradle.kts
similarity index 62%
rename from modules/api-local/build.gradle.kts
rename to modules/api/local/build.gradle.kts
index 267b141..d458285 100644
--- a/modules/api-local/build.gradle.kts
+++ b/modules/api/local/build.gradle.kts
@@ -1,5 +1,8 @@
plugins {
- kotlin("jvm")
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
}
dependencies {
@@ -14,6 +17,8 @@ dependencies {
implementation(libs.klibs.mikro.extensions)
implementation(libs.exposed.core)
+ implementation(libs.exposed.dao)
+ implementation(libs.exposed.jdbc)
implementation(projects.modules.core)
}
diff --git a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDao.kt b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDao.kt
similarity index 100%
rename from modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDao.kt
rename to modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDao.kt
diff --git a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDaoImpl.kt b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDaoImpl.kt
similarity index 88%
rename from modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDaoImpl.kt
rename to modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDaoImpl.kt
index bb8e7ac..1b0028a 100644
--- a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDaoImpl.kt
+++ b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/dao/LocalDaoImpl.kt
@@ -2,13 +2,13 @@ package ru.astrainteractive.astratemplate.api.local.dao
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
-import org.jetbrains.exposed.sql.Database
-import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
-import org.jetbrains.exposed.sql.deleteWhere
-import org.jetbrains.exposed.sql.insertAndGetId
-import org.jetbrains.exposed.sql.selectAll
-import org.jetbrains.exposed.sql.transactions.transaction
-import org.jetbrains.exposed.sql.update
+import org.jetbrains.exposed.v1.core.eq
+import org.jetbrains.exposed.v1.jdbc.Database
+import org.jetbrains.exposed.v1.jdbc.deleteWhere
+import org.jetbrains.exposed.v1.jdbc.insertAndGetId
+import org.jetbrains.exposed.v1.jdbc.selectAll
+import org.jetbrains.exposed.v1.jdbc.transactions.transaction
+import org.jetbrains.exposed.v1.jdbc.update
import ru.astrainteractive.astratemplate.api.local.entity.UserRatingTable
import ru.astrainteractive.astratemplate.api.local.entity.UserTable
import ru.astrainteractive.astratemplate.api.local.model.RatingModel
diff --git a/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/di/ApiLocalModule.kt b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/di/ApiLocalModule.kt
new file mode 100644
index 0000000..073f2e8
--- /dev/null
+++ b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/di/ApiLocalModule.kt
@@ -0,0 +1,45 @@
+package ru.astrainteractive.astratemplate.api.local.di
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+import org.jetbrains.exposed.v1.jdbc.SchemaUtils
+import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
+import org.jetbrains.exposed.v1.jdbc.transactions.transaction
+import ru.astrainteractive.astralibs.lifecycle.Lifecycle
+import ru.astrainteractive.astratemplate.api.local.dao.LocalDao
+import ru.astrainteractive.astratemplate.api.local.dao.LocalDaoImpl
+import ru.astrainteractive.astratemplate.api.local.entity.UserRatingTable
+import ru.astrainteractive.astratemplate.api.local.entity.UserTable
+import ru.astrainteractive.astratemplate.core.plugin.PluginConfiguration
+import ru.astrainteractive.klibs.mikro.exposed.util.connectAsFlow
+
+class ApiLocalModule(
+ configFlow: Flow,
+ ioScope: CoroutineScope
+) {
+ private val databaseFlow = configFlow
+ .map { pluginConfiguration -> pluginConfiguration.database }
+ .distinctUntilChanged()
+ .flatMapLatest { configuration -> configuration.connectAsFlow() }
+ .onEach { database ->
+ TransactionManager.manager.defaultIsolationLevel = java.sql.Connection.TRANSACTION_SERIALIZABLE
+ transaction(database) {
+ SchemaUtils.create(UserRatingTable, UserTable)
+ }
+ }
+ .shareIn(ioScope, SharingStarted.Eagerly, 1)
+
+ val localDao: LocalDao = LocalDaoImpl(
+ databaseFlow = databaseFlow,
+ )
+
+ val lifecycle: Lifecycle by lazy {
+ Lifecycle.Lambda()
+ }
+}
diff --git a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserRatingTable.kt b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserRatingTable.kt
similarity index 77%
rename from modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserRatingTable.kt
rename to modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserRatingTable.kt
index 69f92e4..15ffaf4 100644
--- a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserRatingTable.kt
+++ b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserRatingTable.kt
@@ -1,6 +1,6 @@
package ru.astrainteractive.astratemplate.api.local.entity
-import org.jetbrains.exposed.dao.id.IntIdTable
+import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
internal object UserRatingTable : IntIdTable("rating") {
val userID = integer("user_id")
diff --git a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserTable.kt b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserTable.kt
similarity index 78%
rename from modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserTable.kt
rename to modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserTable.kt
index 8ada3da..3de3c8e 100644
--- a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserTable.kt
+++ b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/entity/UserTable.kt
@@ -1,6 +1,6 @@
package ru.astrainteractive.astratemplate.api.local.entity
-import org.jetbrains.exposed.dao.id.IntIdTable
+import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
internal object UserTable : IntIdTable("users") {
val discordId = text("discord_id")
diff --git a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/RatingModel.kt b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/RatingModel.kt
similarity index 100%
rename from modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/RatingModel.kt
rename to modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/RatingModel.kt
diff --git a/modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/UserModel.kt b/modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/UserModel.kt
similarity index 100%
rename from modules/api-local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/UserModel.kt
rename to modules/api/local/src/main/kotlin/ru/astrainteractive/astratemplate/api/local/model/UserModel.kt
diff --git a/modules/api/remote/build.gradle.kts b/modules/api/remote/build.gradle.kts
new file mode 100644
index 0000000..f1907ab
--- /dev/null
+++ b/modules/api/remote/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+}
+
+dependencies {
+ implementation(libs.kotlin.coroutines.core)
+ implementation(libs.kotlin.serialization.json)
+ implementation(libs.kotlin.serialization.kaml)
+}
diff --git a/modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/RickMortyApi.kt b/modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/api/RickMortyApi.kt
similarity index 81%
rename from modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/RickMortyApi.kt
rename to modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/api/RickMortyApi.kt
index 736daf8..cf90067 100644
--- a/modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/RickMortyApi.kt
+++ b/modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/api/RickMortyApi.kt
@@ -1,4 +1,4 @@
-package ru.astrainteractive.astratemplate.api.remote
+package ru.astrainteractive.astratemplate.api.remote.api
import ru.astrainteractive.astratemplate.api.remote.model.RMResponse
diff --git a/modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/RickMortyApiImpl.kt b/modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/api/RickMortyApiImpl.kt
similarity index 87%
rename from modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/RickMortyApiImpl.kt
rename to modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/api/RickMortyApiImpl.kt
index 049f996..34a31e1 100644
--- a/modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/RickMortyApiImpl.kt
+++ b/modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/api/RickMortyApiImpl.kt
@@ -1,6 +1,5 @@
-package ru.astrainteractive.astratemplate.api.remote
+package ru.astrainteractive.astratemplate.api.remote.api
-import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import ru.astrainteractive.astratemplate.api.remote.model.RMResponse
import java.net.URI
@@ -19,7 +18,7 @@ internal class RickMortyApiImpl(
isLenient = true
}
- override suspend fun getRandomCharacter(id: Int): Result = kotlin.runCatching {
+ override suspend fun getRandomCharacter(id: Int): Result = runCatching {
val request: HttpRequest = HttpRequest.newBuilder()
.uri(URI("https://rickandmortyapi.com/api/character/$id"))
.GET()
diff --git a/modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/di/ApiRemoteModule.kt b/modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/di/ApiRemoteModule.kt
similarity index 66%
rename from modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/di/ApiRemoteModule.kt
rename to modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/di/ApiRemoteModule.kt
index 2773ab4..bf46912 100644
--- a/modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/di/ApiRemoteModule.kt
+++ b/modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/di/ApiRemoteModule.kt
@@ -1,7 +1,7 @@
package ru.astrainteractive.astratemplate.api.remote.di
-import ru.astrainteractive.astratemplate.api.remote.RickMortyApi
-import ru.astrainteractive.astratemplate.api.remote.RickMortyApiImpl
+import ru.astrainteractive.astratemplate.api.remote.api.RickMortyApi
+import ru.astrainteractive.astratemplate.api.remote.api.RickMortyApiImpl
import java.net.http.HttpClient
class ApiRemoteModule {
diff --git a/modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/model/RMResponse.kt b/modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/model/RMResponse.kt
similarity index 100%
rename from modules/api-remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/model/RMResponse.kt
rename to modules/api/remote/src/main/kotlin/ru/astrainteractive/astratemplate/api/remote/model/RMResponse.kt
diff --git a/modules/build-konfig/build.gradle.kts b/modules/build-konfig/build.gradle.kts
index 278f4df..ebe873c 100644
--- a/modules/build-konfig/build.gradle.kts
+++ b/modules/build-konfig/build.gradle.kts
@@ -1,7 +1,10 @@
-import ru.astrainteractive.gradleplugin.property.extension.ModelPropertyValueExt.requireProjectInfo
+import ru.astrainteractive.gradleplugin.property.util.requireProjectInfo
plugins {
- kotlin("jvm")
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
alias(libs.plugins.gradle.buildconfig)
}
diff --git a/modules/core/build.gradle.kts b/modules/core/build.gradle.kts
index 9de3469..77d9efc 100644
--- a/modules/core/build.gradle.kts
+++ b/modules/core/build.gradle.kts
@@ -1,6 +1,8 @@
plugins {
- kotlin("jvm")
- alias(libs.plugins.kotlin.serialization)
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
}
dependencies {
@@ -9,8 +11,11 @@ dependencies {
implementation(libs.kotlin.serialization.kaml)
implementation(libs.minecraft.astralibs.core)
+ implementation(libs.minecraft.astralibs.command)
implementation(libs.klibs.mikro.core)
implementation(libs.klibs.mikro.extensions)
api(libs.klibs.kstorage)
+
+ implementation(projects.modules.buildKonfig)
}
diff --git a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/di/CoreModule.kt b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/di/CoreModule.kt
index 508195e..7108aba 100644
--- a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/di/CoreModule.kt
+++ b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/di/CoreModule.kt
@@ -1,34 +1,68 @@
package ru.astrainteractive.astratemplate.core.di
+import com.charleskorn.kaml.PolymorphismStyle
+import com.charleskorn.kaml.Yaml
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
-import ru.astrainteractive.astralibs.async.withTimings
+import kotlinx.serialization.StringFormat
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.coroutines.withTimings
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
import ru.astrainteractive.astralibs.lifecycle.Lifecycle
import ru.astrainteractive.astralibs.util.YamlStringFormat
import ru.astrainteractive.astralibs.util.parseOrWriteIntoDefault
+import ru.astrainteractive.astratemplate.BuildKonfig
import ru.astrainteractive.astratemplate.core.plugin.PluginConfiguration
import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.klibs.kstorage.api.asCachedKrate
+import ru.astrainteractive.klibs.kstorage.api.asStateFlowKrate
import ru.astrainteractive.klibs.kstorage.api.impl.DefaultMutableKrate
-import ru.astrainteractive.klibs.kstorage.util.asCachedKrate
-import ru.astrainteractive.klibs.kstorage.util.asStateFlowKrate
import ru.astrainteractive.klibs.mikro.core.coroutines.CoroutineFeature
import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
+import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
import java.io.File
class CoreModule(
val dataFolder: File,
- val dispatchers: KotlinDispatchers
+ val dispatchers: KotlinDispatchers,
+ commandRegistrarContextFactory: (mainScope: CoroutineScope) -> CommandRegistrarContext
) {
- val yamlStringFormat = YamlStringFormat()
+ private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
+ val logger = JUtiltLogger("${BuildKonfig.id}-CoroutineExceptionHandler")
+ logger.error(throwable) { "Error happened inside global coroutine scope!" }
+ }
+
+ val ioScope = CoroutineFeature
+ .Default(dispatchers.IO + SupervisorJob() + coroutineExceptionHandler)
+ .withTimings()
+
+ // Main dispatcher is initialized later after server onCreate
+ val mainScope: CoroutineScope by lazy {
+ CoroutineFeature
+ .Default(dispatchers.Main + SupervisorJob() + coroutineExceptionHandler)
+ .withTimings()
+ }
- val ioScope = CoroutineFeature.IO.withTimings()
- val mainScope = CoroutineFeature
- .Default(dispatchers.Main)
+ val unconfinedScope = CoroutineFeature
+ .Default(dispatchers.Unconfined + SupervisorJob() + coroutineExceptionHandler)
.withTimings()
+ val commandRegistrarContext = commandRegistrarContextFactory.invoke(unconfinedScope)
+
+ val yamlFormat: StringFormat = YamlStringFormat(
+ configuration = Yaml.default.configuration.copy(
+ encodeDefaults = true,
+ strictMode = false,
+ polymorphismStyle = PolymorphismStyle.Property
+ ),
+ )
+
val translationKrate = DefaultMutableKrate(
factory = ::PluginTranslation,
loader = {
- yamlStringFormat.parseOrWriteIntoDefault(
+ yamlFormat.parseOrWriteIntoDefault(
file = dataFolder.resolve("translation.yml"),
default = ::PluginTranslation
)
@@ -38,13 +72,18 @@ class CoreModule(
val configKrate = DefaultMutableKrate(
factory = ::PluginConfiguration,
loader = {
- yamlStringFormat.parseOrWriteIntoDefault(
+ yamlFormat.parseOrWriteIntoDefault(
file = dataFolder.resolve("translation.yml"),
default = ::PluginConfiguration
)
}
).asStateFlowKrate()
+ val kyoriKrate = DefaultMutableKrate(
+ loader = { null },
+ factory = { KyoriComponentSerializer.Legacy }
+ ).asCachedKrate()
+
val lifecycle: Lifecycle by lazy {
Lifecycle.Lambda(
onReload = {
@@ -53,6 +92,8 @@ class CoreModule(
},
onDisable = {
ioScope.cancel()
+ unconfinedScope.cancel()
+ mainScope.cancel()
}
)
}
diff --git a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginConfiguration.kt b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginConfiguration.kt
index f99b311..e39c884 100644
--- a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginConfiguration.kt
+++ b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginConfiguration.kt
@@ -8,21 +8,29 @@ import ru.astrainteractive.klibs.mikro.exposed.model.DatabaseConfiguration
@Serializable
data class PluginConfiguration(
@YamlComment("First line description for config1", "Second line description for config2")
+ @SerialName("config_1")
val config1: String = "NONE",
@YamlComment("Some description for config2")
+ @SerialName("config_2")
val config2: Int = 0,
+ @SerialName("config_3")
val config3: Boolean = false,
+ @SerialName("section")
val section: Section = Section(),
+ @SerialName("list")
val list: List = listOf(),
@SerialName("another_list")
@YamlComment("Description for list")
val anotherList: List = listOf(),
+ @SerialName("database")
val database: DatabaseConfiguration = DatabaseConfiguration.H2("db")
) {
@Serializable
data class Section(
@YamlComment("Description for value1")
+ @SerialName("value_1")
val value1: String = "NONE",
+ @SerialName("value_2")
val value2: Int = -1
)
}
diff --git a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginPermission.kt b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginPermission.kt
index 6f70e86..61bb30e 100644
--- a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginPermission.kt
+++ b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginPermission.kt
@@ -1,6 +1,6 @@
package ru.astrainteractive.astratemplate.core.plugin
-import ru.astrainteractive.astralibs.permission.Permission
+import ru.astrainteractive.astralibs.server.permission.Permission
sealed class PluginPermission(override val value: String) : Permission {
data object Reload : PluginPermission("astra_template.reload")
diff --git a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginTranslation.kt b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginTranslation.kt
index 4769307..0c26b8e 100644
--- a/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginTranslation.kt
+++ b/modules/core/src/main/kotlin/ru/astrainteractive/astratemplate/core/plugin/PluginTranslation.kt
@@ -5,7 +5,9 @@ package ru.astrainteractive.astratemplate.core.plugin
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import ru.astrainteractive.astralibs.string.StringDesc
+import ru.astrainteractive.astralibs.string.plus
import ru.astrainteractive.astralibs.string.replace
+import ru.astrainteractive.astralibs.string.toRaw
/**
* All translation stored here
@@ -13,42 +15,63 @@ import ru.astrainteractive.astralibs.string.replace
*/
@Serializable
class PluginTranslation(
+ @SerialName("database")
val database: Database = Database(),
+ @SerialName("menu")
val menu: Menu = Menu(),
+ @SerialName("custom")
val custom: Custom = Custom(),
+ @SerialName("general")
val general: General = General(),
+ @SerialName("fault")
val fault: Fault = Fault()
) {
@Serializable
data class Fault(
@SerialName("no_permission")
- val noPermission: StringDesc.Raw = StringDesc.Raw("db2c18У вас нет прав!"),
+ val noPermission: StringDesc.Raw = PREFIX
+ .plus("db2c18У вас нет прав!")
+ .toRaw(),
@SerialName("not_player")
- val notPlayer: StringDesc.Raw = StringDesc.Raw("db2c18Вы не игрок"),
+ val notPlayer: StringDesc.Raw = PREFIX
+ .plus("db2c18Вы не игрок")
+ .toRaw(),
@SerialName("player_not_exists")
- val playerNotExists: StringDesc.Raw = StringDesc.Raw("db2c18Игрока нет!"),
+ val playerNotExists: StringDesc.Raw = PREFIX
+ .plus("db2c18Игрока нет!")
+ .toRaw(),
@SerialName("item_not_found")
- val itemNotFound: StringDesc.Raw = StringDesc.Raw("db2c18Предмет не найден"),
+ val itemNotFound: StringDesc.Raw = PREFIX
+ .plus("db2c18Предмет не найден")
+ .toRaw(),
)
@Serializable
class Database(
@SerialName("success")
- val dbSuccess: StringDesc.Raw = StringDesc.Raw("dbd1Успешно подключено к базе данных"),
+ val dbSuccess: StringDesc.Raw = PREFIX
+ .plus("dbd1Успешно подключено к базе данных")
+ .toRaw(),
@SerialName("fail")
- val dbFail: StringDesc.Raw = StringDesc.Raw("db2c18Нет подключения к базе данных"),
+ val dbFail: StringDesc.Raw = PREFIX
+ .plus("db2c18Нет подключения к базе данных")
+ .toRaw(),
)
@Serializable
class General(
- @SerialName("prefix")
- val prefix: StringDesc.Raw = StringDesc.Raw("dbd1[EmpireItems]"),
@SerialName("reload")
- val reload: StringDesc.Raw = StringDesc.Raw("dbbb18Перезагрузка плагина"),
+ val reload: StringDesc.Raw = PREFIX
+ .plus("dbbb18Перезагрузка плагина")
+ .toRaw(),
@SerialName("reload_complete")
- val reloadComplete: StringDesc.Raw = StringDesc.Raw("*f596Перезагрузка успешно завершена"),
+ val reloadComplete: StringDesc.Raw = PREFIX
+ .plus("*f596Перезагрузка успешно завершена")
+ .toRaw(),
@SerialName("getByByCheck")
- val getByByCheck: StringDesc.Raw = StringDesc.Raw("db2c18getByByCheck")
+ val getByByCheck: StringDesc.Raw = PREFIX
+ .plus(StringDesc.Raw("db2c18getByByCheck"))
+ .toRaw()
)
@Serializable
@@ -74,14 +97,42 @@ class PluginTranslation(
@Serializable
class Custom(
@SerialName("block_placed")
- val blockPlaced: StringDesc.Raw = StringDesc.Raw("dbd1Блок поставлен!"),
+ val blockPlaced: StringDesc.Raw = PREFIX
+ .plus("dbd1Блок поставлен!")
+ .toRaw(),
@SerialName("no_player_name")
- val noPlayerName: StringDesc.Raw = StringDesc.Raw("db2c18Вы не ввели имя игрока!"),
+ val noPlayerName: StringDesc.Raw = PREFIX
+ .plus("db2c18Вы не ввели имя игрока!")
+ .toRaw(),
@SerialName("damaged")
- private val damaged: StringDesc.Raw = StringDesc.Raw("db2c18Вас продамажил игрок %player%!"),
+ private val damaged: StringDesc.Raw = PREFIX
+ .plus("db2c18Вас продамажил игрок %player%!")
+ .toRaw(),
@SerialName("damage_hint")
- val damageHint: StringDesc.Raw = StringDesc.Raw("")
+ val damageHint: StringDesc.Raw = StringDesc.Raw(""),
+ @SerialName("add_item_success")
+ private val addItemSuccess: StringDesc.Raw = PREFIX
+ .plus("dbd1Добавлено %amount%x %item%!")
+ .toRaw(),
+ @SerialName("rick_morty_success")
+ private val rickMortySuccess: StringDesc.Raw = PREFIX
+ .plus("dbd1Получен ответ: %result%")
+ .toRaw(),
+ @SerialName("rick_morty_fail")
+ private val rickMortyFail: StringDesc.Raw = PREFIX
+ .plus("db2c18Ошибка: %error%")
+ .toRaw()
) {
fun damaged(player: String) = damaged.replace("%player%", player)
+ fun addItemSuccess(amount: Int, item: String) = addItemSuccess
+ .replace("%amount%", amount.toString())
+ .replace("%item%", item)
+
+ fun rickMortySuccess(result: String) = rickMortySuccess.replace("%result%", result)
+ fun rickMortyFail(error: String) = rickMortyFail.replace("%error%", error)
+ }
+
+ companion object {
+ private val PREFIX = StringDesc.Raw("&7[DBB72BTEMPLATE&7] ")
}
}
diff --git a/modules/feature-command/build.gradle.kts b/modules/feature-command/build.gradle.kts
new file mode 100644
index 0000000..7e758bf
--- /dev/null
+++ b/modules/feature-command/build.gradle.kts
@@ -0,0 +1,29 @@
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+}
+
+dependencies {
+ compileOnly(libs.guava)
+ compileOnly(libs.minecraft.brigadier)
+ compileOnly(libs.minecraft.kyori.plain)
+
+ implementation(libs.klibs.kstorage)
+ implementation(libs.klibs.mikro.core)
+ implementation(libs.klibs.mikro.extensions)
+ implementation(libs.kotlin.coroutines.core)
+ implementation(libs.kotlin.serialization.json)
+ implementation(libs.minecraft.astralibs.command)
+ implementation(libs.minecraft.astralibs.core)
+
+ implementation(projects.modules.core)
+ implementation(projects.modules.featureGui.api)
+ implementation(projects.modules.api.remote)
+ implementation(projects.modules.api.local)
+ implementation(projects.modules.buildKonfig)
+
+ testImplementation(libs.kotlin.coroutines.test)
+ testImplementation(libs.tests.kotlin.test)
+}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemCommand.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemCommand.kt
similarity index 64%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemCommand.kt
rename to modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemCommand.kt
index a3dae44..470eeb8 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/additem/AddItemCommand.kt
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemCommand.kt
@@ -1,14 +1,13 @@
-package ru.astrainteractive.astratemplate.command.additem
+package ru.astrainteractive.astratemplate.feature.command.additem
-import org.bukkit.Material
-import org.bukkit.entity.Player
import ru.astrainteractive.astralibs.command.api.exception.CommandException
+import ru.astrainteractive.astralibs.server.player.OnlineKPlayer
internal interface AddItemCommand {
class Result(
- val player: Player,
+ val player: OnlineKPlayer,
val amount: Int,
- val item: Material
+ val itemName: String
)
sealed class Error(message: String) : CommandException(message) {
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemCommandRegistry.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemCommandRegistry.kt
new file mode 100644
index 0000000..1898527
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemCommandRegistry.kt
@@ -0,0 +1,55 @@
+package ru.astrainteractive.astratemplate.feature.command.additem
+
+import com.mojang.brigadier.arguments.IntegerArgumentType
+import com.mojang.brigadier.arguments.StringArgumentType
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import ru.astrainteractive.astralibs.command.api.argumenttype.ArgumentConverter
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astratemplate.feature.command.errorhandler.DefaultErrorHandler
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+
+internal class AddItemCommandRegistry(
+ kyoriKrate: CachedKrate,
+ private val registrarContext: CommandRegistrarContext,
+ private val multiplatformCommand: MultiplatformCommand,
+ private val errorHandler: DefaultErrorHandler,
+ private val executor: AddItemExecutor
+) : KyoriComponentSerializer by kyoriKrate.unwrap() {
+
+ private object ItemNameArgumentConverter : ArgumentConverter {
+ override fun transform(argument: String): String {
+ return argument.takeIf { it.isNotBlank() } ?: throw AddItemCommand.Error.ItemNotfound()
+ }
+ }
+
+ @Suppress("MagicNumber")
+ private fun createNode(): LiteralArgumentBuilder<*> {
+ return with(multiplatformCommand) {
+ command("add") {
+ argument("material", StringArgumentType.word()) { materialArg ->
+ argument("amount", IntegerArgumentType.integer(1, 64)) { amountArg ->
+ runs(errorHandler::handle) { ctx ->
+ val player = ctx.requirePlayer()
+ val itemName = ctx.requireArgument(materialArg, ItemNameArgumentConverter)
+ val amount = ctx.requireArgument(amountArg)
+ executor.execute(
+ AddItemCommand.Result(
+ player = player,
+ amount = amount,
+ itemName = itemName
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun register() {
+ registrarContext.registerWhenReady(createNode())
+ }
+}
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemExecutor.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemExecutor.kt
new file mode 100644
index 0000000..cae1114
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/additem/AddItemExecutor.kt
@@ -0,0 +1,20 @@
+package ru.astrainteractive.astratemplate.feature.command.additem
+
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+import ru.astrainteractive.klibs.kstorage.api.getValue
+
+internal class AddItemExecutor(
+ translationKrate: CachedKrate,
+ kyoriKrate: CachedKrate
+) : KyoriComponentSerializer by kyoriKrate.unwrap() {
+ private val translation by translationKrate
+
+ fun execute(input: AddItemCommand.Result) {
+ input.player.sendMessage(
+ translation.custom.addItemSuccess(input.amount, input.itemName).component
+ )
+ }
+}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/common/CommonCommands.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/common/CommonCommands.kt
similarity index 54%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/common/CommonCommands.kt
rename to modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/common/CommonCommands.kt
index 060ddac..75a7928 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/command/common/CommonCommands.kt
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/common/CommonCommands.kt
@@ -1,4 +1,4 @@
-package ru.astrainteractive.astratemplate.command.common
+package ru.astrainteractive.astratemplate.feature.command.common
internal interface CommonCommands {
data object Stub : CommonCommands
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/common/CommonCommandsRegistry.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/common/CommonCommandsRegistry.kt
new file mode 100644
index 0000000..54bbcd2
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/common/CommonCommandsRegistry.kt
@@ -0,0 +1,33 @@
+package ru.astrainteractive.astratemplate.feature.command.common
+
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+import ru.astrainteractive.klibs.kstorage.api.getValue
+
+internal class CommonCommandsRegistry(
+ translationKrate: CachedKrate,
+ kyoriKrate: CachedKrate,
+ private val registrarContext: CommandRegistrarContext,
+ private val multiplatformCommand: MultiplatformCommand
+) : KyoriComponentSerializer by kyoriKrate.unwrap() {
+ private val translation by translationKrate
+
+ private fun createNode(): LiteralArgumentBuilder<*> {
+ return with(multiplatformCommand) {
+ command("translation") {
+ runs { ctx ->
+ ctx.getSender().sendMessage(translation.general.getByByCheck.component)
+ }
+ }
+ }
+ }
+
+ fun register() {
+ registrarContext.registerWhenReady(createNode())
+ }
+}
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/damage/DamageCommandRegistry.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/damage/DamageCommandRegistry.kt
new file mode 100644
index 0000000..bd5cf06
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/damage/DamageCommandRegistry.kt
@@ -0,0 +1,48 @@
+package ru.astrainteractive.astratemplate.feature.command.damage
+
+import com.mojang.brigadier.arguments.DoubleArgumentType
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astratemplate.core.plugin.PluginPermission
+import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.astratemplate.feature.command.errorhandler.DefaultErrorHandler
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+import ru.astrainteractive.klibs.kstorage.api.getValue
+
+internal class DamageCommandRegistry(
+ translationKrate: CachedKrate,
+ kyoriKrate: CachedKrate,
+ private val registrarContext: CommandRegistrarContext,
+ private val multiplatformCommand: MultiplatformCommand,
+ private val errorHandler: DefaultErrorHandler
+) : KyoriComponentSerializer by kyoriKrate.unwrap() {
+ private val translation by translationKrate
+
+ private fun createNode(): LiteralArgumentBuilder<*> {
+ return with(multiplatformCommand) {
+ command("adamage") {
+ runs(errorHandler::handle) { ctx ->
+ ctx.requirePermission(PluginPermission.Damage)
+ val player = ctx.requirePlayer()
+ player.sendMessage(translation.custom.damaged(player.name).component)
+ }
+ argument("damage", DoubleArgumentType.doubleArg(0.0)) { damageArg ->
+ runs(errorHandler::handle) { ctx ->
+ ctx.requirePermission(PluginPermission.Damage)
+ val player = ctx.requirePlayer()
+ ctx.requireArgument(damageArg)
+ player.sendMessage(translation.custom.damaged(player.name).component)
+ player.sendMessage(translation.custom.damageHint.component)
+ }
+ }
+ }
+ }
+ }
+
+ fun register() {
+ registrarContext.registerWhenReady(createNode())
+ }
+}
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/di/CommandModule.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/di/CommandModule.kt
new file mode 100644
index 0000000..9e3b125
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/di/CommandModule.kt
@@ -0,0 +1,86 @@
+package ru.astrainteractive.astratemplate.feature.command.di
+
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.lifecycle.Lifecycle
+import ru.astrainteractive.astratemplate.api.remote.di.ApiRemoteModule
+import ru.astrainteractive.astratemplate.core.di.CoreModule
+import ru.astrainteractive.astratemplate.feature.command.additem.AddItemCommandRegistry
+import ru.astrainteractive.astratemplate.feature.command.additem.AddItemExecutor
+import ru.astrainteractive.astratemplate.feature.command.common.CommonCommandsRegistry
+import ru.astrainteractive.astratemplate.feature.command.damage.DamageCommandRegistry
+import ru.astrainteractive.astratemplate.feature.command.errorhandler.DefaultErrorHandler
+import ru.astrainteractive.astratemplate.feature.command.gui.GuiCommandRegistry
+import ru.astrainteractive.astratemplate.feature.command.reload.ReloadCommandRegistry
+import ru.astrainteractive.astratemplate.feature.command.rickmorty.RickMortyCommandRegistrar
+import ru.astrainteractive.astratemplate.feature.gui.di.GuiModule
+
+class CommandModule(
+ private val coreModule: CoreModule,
+ private val guiModule: GuiModule,
+ private val apiRemoteModule: ApiRemoteModule,
+ private val lifecyclePlugin: Lifecycle,
+ private val commandRegistrarContext: CommandRegistrarContext,
+ private val multiplatformCommand: MultiplatformCommand
+) {
+ private val errorHandler by lazy {
+ DefaultErrorHandler(
+ multiplatformCommand = multiplatformCommand,
+ translationKrate = coreModule.translationKrate,
+ kyoriKrate = coreModule.kyoriKrate
+ )
+ }
+
+ val lifecycle: Lifecycle = Lifecycle.Lambda(
+ onEnable = {
+ AddItemCommandRegistry(
+ kyoriKrate = coreModule.kyoriKrate,
+ registrarContext = commandRegistrarContext,
+ multiplatformCommand = multiplatformCommand,
+ errorHandler = errorHandler,
+ executor = AddItemExecutor(
+ translationKrate = coreModule.translationKrate,
+ kyoriKrate = coreModule.kyoriKrate
+ )
+ ).register()
+ CommonCommandsRegistry(
+ kyoriKrate = coreModule.kyoriKrate,
+ translationKrate = coreModule.translationKrate,
+ registrarContext = commandRegistrarContext,
+ multiplatformCommand = multiplatformCommand
+ ).register()
+ DamageCommandRegistry(
+ kyoriKrate = coreModule.kyoriKrate,
+ translationKrate = coreModule.translationKrate,
+ registrarContext = commandRegistrarContext,
+ multiplatformCommand = multiplatformCommand,
+ errorHandler = errorHandler
+ ).register()
+ GuiCommandRegistry(
+ kyoriKrate = coreModule.kyoriKrate,
+ registrarContext = commandRegistrarContext,
+ multiplatformCommand = multiplatformCommand,
+ router = guiModule.router,
+ errorHandler = errorHandler
+ ).register()
+ ReloadCommandRegistry(
+ kyoriKrate = coreModule.kyoriKrate,
+ translationKrate = coreModule.translationKrate,
+ lifecyclePlugin = lifecyclePlugin,
+ registrarContext = commandRegistrarContext,
+ multiplatformCommand = multiplatformCommand,
+ errorHandler = errorHandler
+ ).register()
+ RickMortyCommandRegistrar(
+ scope = coreModule.ioScope,
+ dispatchers = coreModule.dispatchers,
+ rmApi = apiRemoteModule.rickMortyApi,
+ registrarContext = commandRegistrarContext,
+ multiplatformCommand = multiplatformCommand,
+ errorHandler = errorHandler,
+ translationKrate = coreModule.translationKrate,
+ kyoriKrate = coreModule.kyoriKrate
+ ).register()
+ }
+ )
+}
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/errorhandler/DefaultErrorHandler.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/errorhandler/DefaultErrorHandler.kt
new file mode 100644
index 0000000..011e4ee
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/errorhandler/DefaultErrorHandler.kt
@@ -0,0 +1,54 @@
+package ru.astrainteractive.astratemplate.feature.command.errorhandler
+
+import com.mojang.brigadier.context.CommandContext
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.exception.ArgumentConverterException
+import ru.astrainteractive.astralibs.command.api.exception.BadArgumentException
+import ru.astrainteractive.astralibs.command.api.exception.NoPermissionException
+import ru.astrainteractive.astralibs.command.api.exception.NoPotionEffectTypeException
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.astratemplate.feature.command.additem.AddItemCommand
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+import ru.astrainteractive.klibs.kstorage.api.getValue
+import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
+import ru.astrainteractive.klibs.mikro.core.logging.Logger
+
+internal class DefaultErrorHandler(
+ private val multiplatformCommand: MultiplatformCommand,
+ translationKrate: CachedKrate,
+ kyoriKrate: CachedKrate
+) : Logger by JUtiltLogger("AstraTemplate-DefaultErrorHandler"),
+ KyoriComponentSerializer by kyoriKrate.unwrap() {
+ private val translation by translationKrate
+
+ fun handle(ctx: CommandContext, throwable: Throwable) {
+ with(multiplatformCommand) {
+ val sender = ctx.getSender()
+ when (throwable) {
+ is AddItemCommand.Error -> {
+ when (throwable) {
+ is AddItemCommand.Error.ItemNotfound -> {
+ sender.sendMessage(translation.fault.itemNotFound.component)
+ }
+ is AddItemCommand.Error.SenderNotPlayer -> {
+ sender.sendMessage(translation.fault.notPlayer.component)
+ }
+ }
+ }
+ is NoPermissionException -> {
+ sender.sendMessage(translation.fault.noPermission.component)
+ }
+ is NoPotionEffectTypeException,
+ is BadArgumentException,
+ is ArgumentConverterException -> {
+ error { "#handle intentionally unhandled error: $throwable" }
+ }
+ else -> {
+ error { "#handle unhandled error: $throwable" }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/gui/GuiCommandRegistry.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/gui/GuiCommandRegistry.kt
new file mode 100644
index 0000000..313fb57
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/gui/GuiCommandRegistry.kt
@@ -0,0 +1,34 @@
+package ru.astrainteractive.astratemplate.feature.command.gui
+
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astratemplate.feature.command.errorhandler.DefaultErrorHandler
+import ru.astrainteractive.astratemplate.feature.gui.router.Router
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+
+internal class GuiCommandRegistry(
+ kyoriKrate: CachedKrate,
+ private val registrarContext: CommandRegistrarContext,
+ private val multiplatformCommand: MultiplatformCommand,
+ private val router: Router,
+ private val errorHandler: DefaultErrorHandler
+) : KyoriComponentSerializer by kyoriKrate.unwrap() {
+
+ private fun createNode(): LiteralArgumentBuilder<*> {
+ return with(multiplatformCommand) {
+ command("atempgui") {
+ runs(errorHandler::handle) { ctx ->
+ val player = ctx.requirePlayer()
+ router.open(player, Router.Route.Sample)
+ }
+ }
+ }
+ }
+
+ fun register() {
+ registrarContext.registerWhenReady(createNode())
+ }
+}
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/reload/ReloadCommandRegistry.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/reload/ReloadCommandRegistry.kt
new file mode 100644
index 0000000..c53181d
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/reload/ReloadCommandRegistry.kt
@@ -0,0 +1,41 @@
+package ru.astrainteractive.astratemplate.feature.command.reload
+
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astralibs.lifecycle.Lifecycle
+import ru.astrainteractive.astratemplate.core.plugin.PluginPermission
+import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.astratemplate.feature.command.errorhandler.DefaultErrorHandler
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+import ru.astrainteractive.klibs.kstorage.api.getValue
+
+internal class ReloadCommandRegistry(
+ translationKrate: CachedKrate,
+ kyoriKrate: CachedKrate,
+ private val lifecyclePlugin: Lifecycle,
+ private val registrarContext: CommandRegistrarContext,
+ private val multiplatformCommand: MultiplatformCommand,
+ private val errorHandler: DefaultErrorHandler
+) : KyoriComponentSerializer by kyoriKrate.unwrap() {
+ private val translation by translationKrate
+
+ private fun createNode(): LiteralArgumentBuilder<*> {
+ return with(multiplatformCommand) {
+ command("atempreload") {
+ runs(errorHandler::handle) { ctx ->
+ ctx.requirePermission(PluginPermission.Reload)
+ ctx.getSender().sendMessage(translation.general.reload.component)
+ lifecyclePlugin.onReload()
+ ctx.getSender().sendMessage(translation.general.reloadComplete.component)
+ }
+ }
+ }
+ }
+
+ fun register() {
+ registrarContext.registerWhenReady(createNode())
+ }
+}
diff --git a/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/rickmorty/RickMortyCommandRegistrar.kt b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/rickmorty/RickMortyCommandRegistrar.kt
new file mode 100644
index 0000000..55492c6
--- /dev/null
+++ b/modules/feature-command/src/main/kotlin/ru/astrainteractive/astratemplate/feature/command/rickmorty/RickMortyCommandRegistrar.kt
@@ -0,0 +1,63 @@
+package ru.astrainteractive.astratemplate.feature.command.rickmorty
+
+import com.mojang.brigadier.arguments.IntegerArgumentType
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommand
+import ru.astrainteractive.astralibs.command.api.brigadier.sender.KCommandSender
+import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.kyori.unwrap
+import ru.astrainteractive.astratemplate.api.remote.api.RickMortyApi
+import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.astratemplate.feature.command.errorhandler.DefaultErrorHandler
+import ru.astrainteractive.klibs.kstorage.api.CachedKrate
+import ru.astrainteractive.klibs.kstorage.api.getValue
+import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
+import kotlin.random.Random
+
+internal class RickMortyCommandRegistrar(
+ private val scope: CoroutineScope,
+ private val dispatchers: KotlinDispatchers,
+ private val rmApi: RickMortyApi,
+ private val registrarContext: CommandRegistrarContext,
+ private val multiplatformCommand: MultiplatformCommand,
+ private val errorHandler: DefaultErrorHandler,
+ translationKrate: CachedKrate,
+ kyoriKrate: CachedKrate
+) : KyoriComponentSerializer by kyoriKrate.unwrap() {
+ private val translation by translationKrate
+
+ private fun send(sender: KCommandSender, number: Int) {
+ scope.launch(dispatchers.IO) {
+ rmApi.getRandomCharacter(number)
+ .onSuccess { sender.sendMessage(translation.custom.rickMortySuccess(it.toString()).component) }
+ .onFailure { sender.sendMessage(translation.custom.rickMortyFail(it.message.orEmpty()).component) }
+ }
+ }
+
+ @Suppress("MagicNumber")
+ private fun createNode(): LiteralArgumentBuilder<*> {
+ return with(multiplatformCommand) {
+ command("rickandmorty") {
+ literal("random") {
+ runs(errorHandler::handle) { ctx ->
+ send(ctx.getSender(), Random.nextInt(0, 100))
+ }
+ }
+ literal("specific") {
+ argument("number", IntegerArgumentType.integer()) { numberArg ->
+ runs(errorHandler::handle) { ctx ->
+ send(ctx.getSender(), ctx.requireArgument(numberArg))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun register() {
+ registrarContext.registerWhenReady(createNode())
+ }
+}
diff --git a/modules/feature-event/bukkit/build.gradle.kts b/modules/feature-event/bukkit/build.gradle.kts
new file mode 100644
index 0000000..e23aeaa
--- /dev/null
+++ b/modules/feature-event/bukkit/build.gradle.kts
@@ -0,0 +1,24 @@
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+}
+
+dependencies {
+ compileOnly(libs.minecraft.paper.api)
+ compileOnly(libs.minecraft.vaultapi)
+
+ implementation(libs.klibs.kstorage)
+ implementation(libs.klibs.mikro.core)
+ implementation(libs.klibs.mikro.extensions)
+ implementation(libs.kotlin.coroutines.core)
+ implementation(libs.kotlin.serialization.json)
+ implementation(libs.minecraft.astralibs.menu.bukkit)
+ implementation(libs.minecraft.astralibs.core)
+ implementation(libs.minecraft.astralibs.core.bukkit)
+
+ implementation(projects.modules.featureGui.api)
+ implementation(projects.modules.core)
+ implementation(projects.modules.api.local)
+}
diff --git a/modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/EventModule.kt b/modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/EventModule.kt
new file mode 100644
index 0000000..ed4fcc2
--- /dev/null
+++ b/modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/EventModule.kt
@@ -0,0 +1,38 @@
+package ru.astrainteractive.astratemplate.feature.event.di
+
+import org.bukkit.event.HandlerList
+import org.bukkit.plugin.java.JavaPlugin
+import ru.astrainteractive.astralibs.event.EventListener
+import ru.astrainteractive.astralibs.lifecycle.Lifecycle
+import ru.astrainteractive.astralibs.menu.event.DefaultInventoryClickEvent
+import ru.astrainteractive.astratemplate.core.di.CoreModule
+import ru.astrainteractive.astratemplate.feature.event.event.BetterAnotherEvent
+import ru.astrainteractive.astratemplate.feature.event.event.TemplateEvent
+
+class EventModule(
+ coreModule: CoreModule,
+ plugin: JavaPlugin
+) {
+ val eventListener: EventListener = EventListener.Default()
+ val inventoryClickListener: EventListener = DefaultInventoryClickEvent()
+
+ private val events = buildList {
+ eventListener.run(::add)
+ inventoryClickListener.run(::add)
+ TemplateEvent(
+ kyoriKrate = coreModule.kyoriKrate,
+ translationKrate = coreModule.translationKrate
+ ).run(::add)
+ BetterAnotherEvent().run(::add)
+ }
+
+ val lifecycle: Lifecycle = Lifecycle.Lambda(
+ onEnable = {
+ events.forEach { listener -> listener.onEnable(plugin) }
+ },
+ onDisable = {
+ events.forEach(EventListener::onDisable)
+ HandlerList.unregisterAll(plugin)
+ }
+ )
+}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/BetterAnotherEvent.kt b/modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/event/BetterAnotherEvent.kt
similarity index 73%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/BetterAnotherEvent.kt
rename to modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/event/BetterAnotherEvent.kt
index 2c1b995..a7e306b 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/BetterAnotherEvent.kt
+++ b/modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/event/BetterAnotherEvent.kt
@@ -1,5 +1,6 @@
-package ru.astrainteractive.astratemplate.event.event
+package ru.astrainteractive.astratemplate.feature.event.event
+import org.bukkit.event.EventHandler
import org.bukkit.event.block.BlockBreakEvent
import ru.astrainteractive.astralibs.event.EventListener
@@ -7,7 +8,7 @@ import ru.astrainteractive.astralibs.event.EventListener
* This is a more convenient way with base class
*/
internal class BetterAnotherEvent : EventListener {
- @org.bukkit.event.EventHandler
+ @EventHandler
fun blockBreakEvent(e: BlockBreakEvent) {
println("blockBreakEvent ${e.player.name}")
}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/TemplateEvent.kt b/modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/event/TemplateEvent.kt
similarity index 84%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/TemplateEvent.kt
rename to modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/event/TemplateEvent.kt
index f4cd935..dc72060 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/event/event/TemplateEvent.kt
+++ b/modules/feature-event/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/event/TemplateEvent.kt
@@ -1,4 +1,4 @@
-package ru.astrainteractive.astratemplate.event.event
+package ru.astrainteractive.astratemplate.feature.event.event
import org.bukkit.event.EventHandler
import org.bukkit.event.HandlerList
@@ -6,14 +6,12 @@ import org.bukkit.event.block.BlockPlaceEvent
import ru.astrainteractive.astralibs.event.EventListener
import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
import ru.astrainteractive.astralibs.kyori.unwrap
-import ru.astrainteractive.astratemplate.AstraTemplate
import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
import ru.astrainteractive.klibs.kstorage.api.CachedKrate
-import ru.astrainteractive.klibs.kstorage.util.getValue
+import ru.astrainteractive.klibs.kstorage.api.getValue
/**
* Template event class
- * @see [MultipleEventsDSL]
*/
internal class TemplateEvent(
kyoriKrate: CachedKrate,
@@ -34,7 +32,7 @@ internal class TemplateEvent(
/**
* As said in EventHandler, every Event must have onDisable method, which disabling events
* Here BlockPlaceEvent is unregistering
- * It's okay to not write anything here, since you call [HandlerList.unregister] in [AstraTemplate.onDisable]
+ * It's okay to not write anything here, since you call [HandlerList.unregister] in [ru.astrainteractive.astralibs.lifecycle.Lifecycle.onDisable]
*/
public override fun onDisable() {
super.onDisable()
diff --git a/modules/feature-event/forge/build.gradle.kts b/modules/feature-event/forge/build.gradle.kts
new file mode 100644
index 0000000..3067543
--- /dev/null
+++ b/modules/feature-event/forge/build.gradle.kts
@@ -0,0 +1,43 @@
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+}
+
+dependencies {
+ implementation(libs.klibs.kstorage)
+ implementation(libs.klibs.mikro.core)
+ implementation(libs.klibs.mikro.extensions)
+ implementation(libs.kotlin.coroutines.core)
+ implementation(libs.kotlin.serialization.json)
+ implementation(libs.minecraft.astralibs.core)
+ implementation(libs.minecraft.astralibs.core.forge)
+
+ implementation(projects.modules.featureGui.api)
+ implementation(projects.modules.core)
+ implementation(projects.modules.api.local)
+}
+
+dependencies {
+ // We use this because forge plugin waste a lot of resources when enabled
+ compileOnly(
+ files(
+ rootProject
+ .file(".gradle")
+ .resolve("mavenizer")
+ .resolve("repo")
+ .resolve("net")
+ .resolve("minecraftforge")
+ .resolve("forge")
+ .resolve(libs.versions.minecraft.forgeversion.get())
+ .resolve("forge-${libs.versions.minecraft.forgeversion.get()}.jar")
+ )
+ )
+ compileOnly(libs.minecraft.brigadier)
+ compileOnly(libs.minecraft.authlib)
+ compileOnly(libs.minecraft.forgeversion)
+ compileOnly(libs.minecraft.datafixerupper)
+ compileOnly(libs.minecraft.forge.bus)
+ compileOnly(libs.joml)
+}
diff --git a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/event/di/EventModule.kt b/modules/feature-event/forge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/di/EventModule.kt
similarity index 69%
rename from instances/forge/src/main/java/ru/astrainteractive/astratemplate/event/di/EventModule.kt
rename to modules/feature-event/forge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/di/EventModule.kt
index 992ddf9..ea9397e 100644
--- a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/event/di/EventModule.kt
+++ b/modules/feature-event/forge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/di/EventModule.kt
@@ -1,8 +1,8 @@
-package ru.astrainteractive.astratemplate.event.di
+package ru.astrainteractive.astratemplate.feature.event.di.di
import ru.astrainteractive.astralibs.lifecycle.Lifecycle
import ru.astrainteractive.astratemplate.core.di.CoreModule
-import ru.astrainteractive.astratemplate.event.TickEvent
+import ru.astrainteractive.astratemplate.feature.event.di.event.TickEvent
class EventModule(coreModule: CoreModule) {
@Suppress("UnusedPrivateProperty")
diff --git a/modules/feature-event/forge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/event/TickEvent.kt b/modules/feature-event/forge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/event/TickEvent.kt
new file mode 100644
index 0000000..5d4f003
--- /dev/null
+++ b/modules/feature-event/forge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/event/TickEvent.kt
@@ -0,0 +1,18 @@
+package ru.astrainteractive.astratemplate.feature.event.di.event
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import net.minecraftforge.event.TickEvent
+import ru.astrainteractive.astralibs.event.flowEvent
+import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
+import ru.astrainteractive.klibs.mikro.core.logging.Logger
+import kotlin.time.Duration.Companion.seconds
+
+class TickEvent(mainScope: CoroutineScope) : Logger by JUtiltLogger("AstraTemplate-TickEvent") {
+ val tickEvent = flowEvent()
+ .debounce(1.seconds)
+ .onEach { info { "#tickEvent -> flow onEach with 1.second debounce" } }
+ .launchIn(mainScope)
+}
diff --git a/modules/feature-event/neoforge/build.gradle.kts b/modules/feature-event/neoforge/build.gradle.kts
new file mode 100644
index 0000000..9ba8540
--- /dev/null
+++ b/modules/feature-event/neoforge/build.gradle.kts
@@ -0,0 +1,43 @@
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+}
+
+dependencies {
+ implementation(libs.klibs.kstorage)
+ implementation(libs.klibs.mikro.core)
+ implementation(libs.klibs.mikro.extensions)
+ implementation(libs.kotlin.coroutines.core)
+ implementation(libs.kotlin.serialization.json)
+ implementation(libs.minecraft.astralibs.core)
+ implementation(libs.minecraft.astralibs.core.neoforge)
+
+ implementation(projects.modules.featureGui.api)
+ implementation(projects.modules.core)
+ implementation(projects.modules.api.local)
+}
+
+dependencies {
+ // We use this because forge plugin waste a lot of resources when enabled
+ compileOnly(
+ files(
+ rootProject.project(projects.instances.neoforge.path)
+ .file(".gradle")
+ .resolve("repositories")
+ .resolve("ng_dummy_ng")
+ .resolve("net")
+ .resolve("neoforged")
+ .resolve("neoforge")
+ .resolve(libs.versions.minecraft.neoforgeversion.get())
+ .resolve("neoforge-${libs.versions.minecraft.neoforgeversion.get()}.jar")
+ )
+ )
+ compileOnly(libs.minecraft.neoforgeversion)
+ compileOnly(libs.joml)
+ compileOnly(libs.minecraft.datafixerupper)
+ compileOnly(libs.minecraft.brigadier)
+ compileOnly(libs.minecraft.neoforged.bus)
+ compileOnly(libs.minecraft.authlib)
+}
diff --git a/modules/feature-event/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/di/EventModule.kt b/modules/feature-event/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/di/EventModule.kt
new file mode 100644
index 0000000..ea9397e
--- /dev/null
+++ b/modules/feature-event/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/di/EventModule.kt
@@ -0,0 +1,11 @@
+package ru.astrainteractive.astratemplate.feature.event.di.di
+
+import ru.astrainteractive.astralibs.lifecycle.Lifecycle
+import ru.astrainteractive.astratemplate.core.di.CoreModule
+import ru.astrainteractive.astratemplate.feature.event.di.event.TickEvent
+
+class EventModule(coreModule: CoreModule) {
+ @Suppress("UnusedPrivateProperty")
+ private val tickEvent = TickEvent(coreModule.mainScope)
+ val lifecycle: Lifecycle = Lifecycle.Empty
+}
diff --git a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/event/TickEvent.kt b/modules/feature-event/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/event/TickEvent.kt
similarity index 50%
rename from instances/forge/src/main/java/ru/astrainteractive/astratemplate/event/TickEvent.kt
rename to modules/feature-event/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/event/TickEvent.kt
index 9679a8f..1e066e5 100644
--- a/instances/forge/src/main/java/ru/astrainteractive/astratemplate/event/TickEvent.kt
+++ b/modules/feature-event/neoforge/src/main/kotlin/ru/astrainteractive/astratemplate/feature/event/di/event/TickEvent.kt
@@ -1,18 +1,15 @@
-package ru.astrainteractive.astratemplate.event
+package ru.astrainteractive.astratemplate.feature.event.di.event
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import net.minecraftforge.event.TickEvent
+import net.neoforged.neoforge.event.tick.ServerTickEvent
import ru.astrainteractive.astralibs.event.flowEvent
import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
import ru.astrainteractive.klibs.mikro.core.logging.Logger
-class TickEvent(
- mainScope: CoroutineScope
-) : Logger by JUtiltLogger("AstraTemplate-TickEvent") {
-
- val serverStartedEvent = flowEvent()
- .onEach { info { "#serverStartedEvent" } }
+class TickEvent(mainScope: CoroutineScope) : Logger by JUtiltLogger("AstraTemplate-TickEvent") {
+ val tickEvent = flowEvent()
+ .onEach { info { "#tickEvent -> flow onEach" } }
.launchIn(mainScope)
}
diff --git a/modules/feature-gui/api/build.gradle.kts b/modules/feature-gui/api/build.gradle.kts
new file mode 100644
index 0000000..5a08eb3
--- /dev/null
+++ b/modules/feature-gui/api/build.gradle.kts
@@ -0,0 +1,10 @@
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+}
+
+dependencies {
+ implementation(libs.minecraft.astralibs.core)
+}
diff --git a/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/GuiModule.kt b/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/GuiModule.kt
new file mode 100644
index 0000000..4f84d03
--- /dev/null
+++ b/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/GuiModule.kt
@@ -0,0 +1,7 @@
+package ru.astrainteractive.astratemplate.feature.gui.di
+
+import ru.astrainteractive.astratemplate.feature.gui.router.Router
+
+interface GuiModule {
+ val router: Router
+}
diff --git a/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/StubGuiModule.kt b/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/StubGuiModule.kt
new file mode 100644
index 0000000..39c5bbc
--- /dev/null
+++ b/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/StubGuiModule.kt
@@ -0,0 +1,13 @@
+package ru.astrainteractive.astratemplate.feature.gui.di
+
+import ru.astrainteractive.astralibs.server.player.OnlineKPlayer
+import ru.astrainteractive.astratemplate.feature.gui.router.Router
+
+class StubGuiModule : GuiModule {
+ override val router: Router = object : Router {
+ override fun open(
+ player: OnlineKPlayer,
+ route: Router.Route
+ ) = Unit
+ }
+}
diff --git a/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/router/Router.kt b/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/router/Router.kt
new file mode 100644
index 0000000..ea3ea48
--- /dev/null
+++ b/modules/feature-gui/api/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/router/Router.kt
@@ -0,0 +1,11 @@
+package ru.astrainteractive.astratemplate.feature.gui.router
+
+import ru.astrainteractive.astralibs.server.player.OnlineKPlayer
+
+interface Router {
+ sealed interface Route {
+ data object Sample : Route
+ }
+
+ fun open(player: OnlineKPlayer, route: Route)
+}
diff --git a/modules/feature-gui/bukkit/build.gradle.kts b/modules/feature-gui/bukkit/build.gradle.kts
new file mode 100644
index 0000000..e23aeaa
--- /dev/null
+++ b/modules/feature-gui/bukkit/build.gradle.kts
@@ -0,0 +1,24 @@
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("org.jetbrains.kotlin.plugin.serialization")
+ id("ru.astrainteractive.gradleplugin.detekt")
+ id("ru.astrainteractive.gradleplugin.java.version")
+}
+
+dependencies {
+ compileOnly(libs.minecraft.paper.api)
+ compileOnly(libs.minecraft.vaultapi)
+
+ implementation(libs.klibs.kstorage)
+ implementation(libs.klibs.mikro.core)
+ implementation(libs.klibs.mikro.extensions)
+ implementation(libs.kotlin.coroutines.core)
+ implementation(libs.kotlin.serialization.json)
+ implementation(libs.minecraft.astralibs.menu.bukkit)
+ implementation(libs.minecraft.astralibs.core)
+ implementation(libs.minecraft.astralibs.core.bukkit)
+
+ implementation(projects.modules.featureGui.api)
+ implementation(projects.modules.core)
+ implementation(projects.modules.api.local)
+}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/api/ItemStackSpigotAPI.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/api/ItemStackSpigotAPI.kt
similarity index 87%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/api/ItemStackSpigotAPI.kt
rename to modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/api/ItemStackSpigotAPI.kt
index 759f282..48cc2a7 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/api/ItemStackSpigotAPI.kt
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/api/ItemStackSpigotAPI.kt
@@ -1,4 +1,4 @@
-package ru.astrainteractive.astratemplate.gui.api
+package ru.astrainteractive.astratemplate.feature.gui.api
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/AddUserButton.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/AddUserButton.kt
new file mode 100644
index 0000000..35db909
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/AddUserButton.kt
@@ -0,0 +1,20 @@
+package ru.astrainteractive.astratemplate.feature.gui.button
+
+import org.bukkit.Material
+import ru.astrainteractive.astralibs.menu.clicker.Click
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astralibs.menu.slot.setDisplayName
+import ru.astrainteractive.astralibs.menu.slot.setIndex
+import ru.astrainteractive.astralibs.menu.slot.setMaterial
+import ru.astrainteractive.astralibs.menu.slot.setOnClickListener
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+
+internal fun ButtonContext.addUser(
+ index: Int,
+ click: Click
+) = InventorySlot.Builder()
+ .setIndex(index)
+ .setMaterial(Material.EMERALD)
+ .setDisplayName(toComponent(translation.menu.menuAddPlayer))
+ .setOnClickListener(click)
+ .build()
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/BackButton.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/BackButton.kt
new file mode 100644
index 0000000..6ec0425
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/BackButton.kt
@@ -0,0 +1,20 @@
+package ru.astrainteractive.astratemplate.feature.gui.button
+
+import org.bukkit.Material
+import ru.astrainteractive.astralibs.menu.clicker.Click
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astralibs.menu.slot.setDisplayName
+import ru.astrainteractive.astralibs.menu.slot.setIndex
+import ru.astrainteractive.astralibs.menu.slot.setMaterial
+import ru.astrainteractive.astralibs.menu.slot.setOnClickListener
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+
+internal fun ButtonContext.back(
+ index: Int,
+ click: Click
+) = InventorySlot.Builder()
+ .setIndex(index)
+ .setMaterial(Material.BARRIER)
+ .setDisplayName(toComponent(translation.menu.menuBack))
+ .setOnClickListener(click)
+ .build()
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/BorderButton.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/BorderButton.kt
new file mode 100644
index 0000000..3b22fc9
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/BorderButton.kt
@@ -0,0 +1,14 @@
+package ru.astrainteractive.astratemplate.feature.gui.button
+
+import org.bukkit.Material
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astralibs.menu.slot.setDisplayName
+import ru.astrainteractive.astralibs.menu.slot.setIndex
+import ru.astrainteractive.astralibs.menu.slot.setMaterial
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+
+internal fun ButtonContext.border(index: Int) = InventorySlot.Builder()
+ .setIndex(index)
+ .setMaterial(Material.GRAY_STAINED_GLASS_PANE)
+ .setDisplayName(" ")
+ .build()
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/ChangeModeButton.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/ChangeModeButton.kt
new file mode 100644
index 0000000..77afbb4
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/ChangeModeButton.kt
@@ -0,0 +1,21 @@
+package ru.astrainteractive.astratemplate.feature.gui.button
+
+import org.bukkit.Material
+import ru.astrainteractive.astralibs.menu.clicker.Click
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astralibs.menu.slot.setDisplayName
+import ru.astrainteractive.astralibs.menu.slot.setIndex
+import ru.astrainteractive.astralibs.menu.slot.setMaterial
+import ru.astrainteractive.astralibs.menu.slot.setOnClickListener
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+
+internal fun ButtonContext.changeMode(
+ index: Int,
+ label: String,
+ click: Click
+) = InventorySlot.Builder()
+ .setIndex(index)
+ .setMaterial(Material.SUNFLOWER)
+ .setDisplayName(label)
+ .setOnClickListener(click)
+ .build()
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/ContentSlotButton.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/ContentSlotButton.kt
new file mode 100644
index 0000000..8ddcda0
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/ContentSlotButton.kt
@@ -0,0 +1,47 @@
+package ru.astrainteractive.astratemplate.feature.gui.button
+
+import net.kyori.adventure.text.Component
+import org.bukkit.ChatColor
+import org.bukkit.Material
+import org.bukkit.inventory.ItemStack
+import ru.astrainteractive.astralibs.menu.clicker.Click
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astralibs.menu.slot.setDisplayName
+import ru.astrainteractive.astralibs.menu.slot.setIndex
+import ru.astrainteractive.astralibs.menu.slot.setItemStack
+import ru.astrainteractive.astralibs.menu.slot.setLore
+import ru.astrainteractive.astralibs.menu.slot.setMaterial
+import ru.astrainteractive.astralibs.menu.slot.setOnClickListener
+import ru.astrainteractive.astratemplate.api.local.model.UserModel
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+
+internal fun ButtonContext.itemSlot(
+ index: Int,
+ itemStack: ItemStack,
+ click: Click
+) = InventorySlot.Builder()
+ .setIndex(index)
+ .setItemStack(itemStack)
+ .setOnClickListener(click)
+ .build()
+
+internal fun ButtonContext.userSlot(
+ index: Int,
+ user: UserModel,
+ randomColor: ChatColor,
+ click: Click
+) = InventorySlot.Builder()
+ .setIndex(index)
+ .setMaterial(Material.PLAYER_HEAD)
+ .setDisplayName(user.id.toString())
+ .setLore(
+ listOf(
+ "${randomColor}discordID: ${user.discordId}",
+ "${randomColor}minecraftUUID: ${user.minecraftUUID}",
+ "${randomColor}Press LeftClick to delete user",
+ "${randomColor}Press MiddleClick to update user",
+ "${randomColor}Press RightClick to add relation"
+ ).map(Component::text)
+ )
+ .setOnClickListener(click)
+ .build()
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/NextPageButton.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/NextPageButton.kt
new file mode 100644
index 0000000..07aa792
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/NextPageButton.kt
@@ -0,0 +1,20 @@
+package ru.astrainteractive.astratemplate.feature.gui.button
+
+import org.bukkit.Material
+import ru.astrainteractive.astralibs.menu.clicker.Click
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astralibs.menu.slot.setDisplayName
+import ru.astrainteractive.astralibs.menu.slot.setIndex
+import ru.astrainteractive.astralibs.menu.slot.setMaterial
+import ru.astrainteractive.astralibs.menu.slot.setOnClickListener
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+
+internal fun ButtonContext.nextPage(
+ index: Int,
+ click: Click
+) = InventorySlot.Builder()
+ .setIndex(index)
+ .setMaterial(Material.PAPER)
+ .setDisplayName(toComponent(translation.menu.menuNextPage))
+ .setOnClickListener(click)
+ .build()
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/PrevPageButton.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/PrevPageButton.kt
new file mode 100644
index 0000000..501aca8
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/PrevPageButton.kt
@@ -0,0 +1,20 @@
+package ru.astrainteractive.astratemplate.feature.gui.button
+
+import org.bukkit.Material
+import ru.astrainteractive.astralibs.menu.clicker.Click
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astralibs.menu.slot.setDisplayName
+import ru.astrainteractive.astralibs.menu.slot.setIndex
+import ru.astrainteractive.astralibs.menu.slot.setMaterial
+import ru.astrainteractive.astralibs.menu.slot.setOnClickListener
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+
+internal fun ButtonContext.prevPage(
+ index: Int,
+ click: Click
+) = InventorySlot.Builder()
+ .setIndex(index)
+ .setMaterial(Material.PAPER)
+ .setDisplayName(toComponent(translation.menu.menuPrevPage))
+ .setOnClickListener(click)
+ .build()
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/di/ButtonContext.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/di/ButtonContext.kt
new file mode 100644
index 0000000..9c87393
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/button/di/ButtonContext.kt
@@ -0,0 +1,20 @@
+package ru.astrainteractive.astratemplate.feature.gui.button.di
+
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astratemplate.core.di.CoreModule
+import ru.astrainteractive.astratemplate.core.plugin.PluginTranslation
+import ru.astrainteractive.klibs.kstorage.api.getValue
+import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
+import ru.astrainteractive.klibs.mikro.core.logging.Logger
+
+internal interface ButtonContext : KyoriComponentSerializer, Logger {
+ val translation: PluginTranslation
+
+ class Default(
+ coreModule: CoreModule
+ ) : ButtonContext,
+ Logger by JUtiltLogger("AstraTemplate-ButtonContext"),
+ KyoriComponentSerializer by coreModule.kyoriKrate.cachedValue {
+ override val translation by coreModule.translationKrate
+ }
+}
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/BukkitGuiModule.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/BukkitGuiModule.kt
new file mode 100644
index 0000000..10c6217
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/di/BukkitGuiModule.kt
@@ -0,0 +1,28 @@
+package ru.astrainteractive.astratemplate.feature.gui.di
+
+import ru.astrainteractive.astratemplate.api.local.di.ApiLocalModule
+import ru.astrainteractive.astratemplate.core.di.CoreModule
+import ru.astrainteractive.astratemplate.feature.gui.api.ItemStackSpigotAPI
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+import ru.astrainteractive.astratemplate.feature.gui.domain.GetRandomColorUseCaseImpl
+import ru.astrainteractive.astratemplate.feature.gui.domain.SetDisplayNameUseCaseImpl
+import ru.astrainteractive.astratemplate.feature.gui.router.Router
+import ru.astrainteractive.astratemplate.feature.gui.router.RouterImpl
+
+class BukkitGuiModule(
+ coreModule: CoreModule,
+ apiLocalModule: ApiLocalModule
+) : GuiModule {
+ private val getRandomColorUseCase = GetRandomColorUseCaseImpl()
+ private val buttonContext = ButtonContext.Default(coreModule)
+
+ override val router: Router = RouterImpl(
+ ioScope = coreModule.ioScope,
+ dispatchers = coreModule.dispatchers,
+ buttonContext = buttonContext,
+ localDao = apiLocalModule.localDao,
+ itemStackSpigotAPi = ItemStackSpigotAPI,
+ getRandomColorUseCase = getRandomColorUseCase,
+ setDisplayNameUseCase = SetDisplayNameUseCaseImpl(getRandomColorUseCase)
+ )
+}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/domain/GetRandomColorUseCase.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/domain/GetRandomColorUseCase.kt
similarity index 70%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/domain/GetRandomColorUseCase.kt
rename to modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/domain/GetRandomColorUseCase.kt
index 4e34036..17b1f88 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/domain/GetRandomColorUseCase.kt
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/domain/GetRandomColorUseCase.kt
@@ -1,7 +1,7 @@
-package ru.astrainteractive.astratemplate.gui.domain
+package ru.astrainteractive.astratemplate.feature.gui.domain
import org.bukkit.ChatColor
-import ru.astrainteractive.astratemplate.gui.domain.GetRandomColorUseCase.Output
+import ru.astrainteractive.astratemplate.feature.gui.domain.GetRandomColorUseCase.Output
import kotlin.random.Random
internal interface GetRandomColorUseCase {
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/domain/SetDisplayNameUseCase.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/domain/SetDisplayNameUseCase.kt
similarity index 77%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/domain/SetDisplayNameUseCase.kt
rename to modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/domain/SetDisplayNameUseCase.kt
index 2263f3d..c6990ba 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/domain/SetDisplayNameUseCase.kt
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/domain/SetDisplayNameUseCase.kt
@@ -1,8 +1,8 @@
-package ru.astrainteractive.astratemplate.gui.domain
+package ru.astrainteractive.astratemplate.feature.gui.domain
import org.bukkit.inventory.ItemStack
-import ru.astrainteractive.astratemplate.gui.domain.SetDisplayNameUseCase.Input
-import ru.astrainteractive.astratemplate.gui.domain.SetDisplayNameUseCase.Output
+import ru.astrainteractive.astratemplate.feature.gui.domain.SetDisplayNameUseCase.Input
+import ru.astrainteractive.astratemplate.feature.gui.domain.SetDisplayNameUseCase.Output
internal interface SetDisplayNameUseCase {
class Input(val items: List, val index: Int)
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/layout/DefaultSampleInventoryLayoutFactory.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/layout/DefaultSampleInventoryLayoutFactory.kt
new file mode 100644
index 0000000..1031e32
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/layout/DefaultSampleInventoryLayoutFactory.kt
@@ -0,0 +1,57 @@
+package ru.astrainteractive.astratemplate.feature.gui.layout
+
+import ru.astrainteractive.astralibs.menu.layout.slotInventoryLayout
+
+@Suppress("MagicNumber")
+internal object DefaultSampleInventoryLayoutFactory {
+
+ private fun createCompactLayout() = slotInventoryLayout {
+ repeat(5) {
+ row(9, SampleSlotKey.CONTENT_ITEM)
+ }
+ row(
+ SampleSlotKey.PREV_PAGE,
+ SampleSlotKey.EMPTY,
+ SampleSlotKey.EMPTY,
+ SampleSlotKey.ADD_USER,
+ SampleSlotKey.BACK,
+ SampleSlotKey.CHANGE_MODE,
+ SampleSlotKey.EMPTY,
+ SampleSlotKey.EMPTY,
+ SampleSlotKey.NEXT_PAGE
+ )
+ }
+
+ private fun createBorderedLayout() = slotInventoryLayout {
+ row(9, SampleSlotKey.BORDER)
+ repeat(4) {
+ row(
+ SampleSlotKey.BORDER,
+ SampleSlotKey.CONTENT_ITEM,
+ SampleSlotKey.CONTENT_ITEM,
+ SampleSlotKey.CONTENT_ITEM,
+ SampleSlotKey.CONTENT_ITEM,
+ SampleSlotKey.CONTENT_ITEM,
+ SampleSlotKey.CONTENT_ITEM,
+ SampleSlotKey.CONTENT_ITEM,
+ SampleSlotKey.BORDER
+ )
+ }
+ row(
+ SampleSlotKey.BORDER,
+ SampleSlotKey.PREV_PAGE,
+ SampleSlotKey.EMPTY,
+ SampleSlotKey.ADD_USER,
+ SampleSlotKey.BACK,
+ SampleSlotKey.CHANGE_MODE,
+ SampleSlotKey.EMPTY,
+ SampleSlotKey.NEXT_PAGE,
+ SampleSlotKey.BORDER
+ )
+ }
+
+ fun create(isCompact: Boolean) = when {
+ isCompact -> createCompactLayout()
+ else -> createBorderedLayout()
+ }
+}
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/layout/SampleSlotKey.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/layout/SampleSlotKey.kt
new file mode 100644
index 0000000..bff5e7f
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/layout/SampleSlotKey.kt
@@ -0,0 +1,12 @@
+package ru.astrainteractive.astratemplate.feature.gui.layout
+
+internal enum class SampleSlotKey {
+ BORDER,
+ PREV_PAGE,
+ NEXT_PAGE,
+ BACK,
+ CHANGE_MODE,
+ ADD_USER,
+ CONTENT_ITEM,
+ EMPTY
+}
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/router/RouterImpl.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/router/RouterImpl.kt
new file mode 100644
index 0000000..e1da5a1
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/router/RouterImpl.kt
@@ -0,0 +1,57 @@
+package ru.astrainteractive.astratemplate.feature.gui.router
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.bukkit.inventory.Inventory
+import ru.astrainteractive.astralibs.server.player.BukkitOnlineKPlayer
+import ru.astrainteractive.astralibs.server.player.OnlineKPlayer
+import ru.astrainteractive.astratemplate.api.local.dao.LocalDao
+import ru.astrainteractive.astratemplate.feature.gui.api.ItemStackSpigotAPI
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+import ru.astrainteractive.astratemplate.feature.gui.domain.GetRandomColorUseCase
+import ru.astrainteractive.astratemplate.feature.gui.domain.SetDisplayNameUseCase
+import ru.astrainteractive.astratemplate.feature.gui.sample.feature.DefaultSampleGUIComponent
+import ru.astrainteractive.astratemplate.feature.gui.sample.gui.SampleGUI
+import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
+import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
+import ru.astrainteractive.klibs.mikro.core.logging.Logger
+import ru.astrainteractive.klibs.mikro.core.util.tryCast
+
+@Suppress("LongParameterList")
+internal class RouterImpl(
+ private val ioScope: CoroutineScope,
+ private val dispatchers: KotlinDispatchers,
+ private val buttonContext: ButtonContext,
+ private val localDao: LocalDao,
+ private val itemStackSpigotAPi: ItemStackSpigotAPI,
+ private val getRandomColorUseCase: GetRandomColorUseCase,
+ private val setDisplayNameUseCase: SetDisplayNameUseCase
+) : Router, Logger by JUtiltLogger("Router") {
+ private fun buildRoute(route: Router.Route): Inventory {
+ return when (route) {
+ Router.Route.Sample -> SampleGUI(
+ buttonContext = buttonContext,
+ dispatchers = dispatchers,
+ sampleComponent = DefaultSampleGUIComponent(
+ localDao = localDao,
+ itemStackSpigotAPi = itemStackSpigotAPi,
+ getRandomColorUseCase = getRandomColorUseCase,
+ setDisplayNameUseCase = setDisplayNameUseCase
+ )
+ ).inventory
+ }
+ }
+
+ override fun open(player: OnlineKPlayer, route: Router.Route) {
+ ioScope.launch {
+ val bukkitPlayer = player.tryCast()?.instance
+ if (bukkitPlayer == null) {
+ error { "#open Could not cast OnlineKPlayer to BukkitOnlineKPlayer" }
+ return@launch
+ }
+ val inventory = buildRoute(route)
+ withContext(dispatchers.Main) { bukkitPlayer.openInventory(inventory) }
+ }
+ }
+}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/feature/DefaultSampleGUIComponent.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/feature/DefaultSampleGUIComponent.kt
similarity index 89%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/feature/DefaultSampleGUIComponent.kt
rename to modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/feature/DefaultSampleGUIComponent.kt
index 3ff7ca7..1ec84b3 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/feature/DefaultSampleGUIComponent.kt
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/feature/DefaultSampleGUIComponent.kt
@@ -1,4 +1,4 @@
-package ru.astrainteractive.astratemplate.gui.sample.feature
+package ru.astrainteractive.astratemplate.feature.gui.sample.feature
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -7,12 +7,12 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.bukkit.ChatColor
import org.bukkit.event.inventory.ClickType
-import ru.astrainteractive.astralibs.async.withTimings
+import ru.astrainteractive.astralibs.coroutines.withTimings
import ru.astrainteractive.astratemplate.api.local.dao.LocalDao
import ru.astrainteractive.astratemplate.api.local.model.UserModel
-import ru.astrainteractive.astratemplate.gui.api.ItemStackSpigotAPI
-import ru.astrainteractive.astratemplate.gui.domain.GetRandomColorUseCase
-import ru.astrainteractive.astratemplate.gui.domain.SetDisplayNameUseCase
+import ru.astrainteractive.astratemplate.feature.gui.api.ItemStackSpigotAPI
+import ru.astrainteractive.astratemplate.feature.gui.domain.GetRandomColorUseCase
+import ru.astrainteractive.astratemplate.feature.gui.domain.SetDisplayNameUseCase
import ru.astrainteractive.klibs.mikro.core.coroutines.CoroutineFeature
import ru.astrainteractive.klibs.mikro.core.logging.JUtiltLogger
import ru.astrainteractive.klibs.mikro.core.logging.Logger
@@ -35,6 +35,7 @@ internal class DefaultSampleGUIComponent(
override val randomColor: ChatColor
get() = getRandomColorUseCase.invoke().color
+ @Suppress("MagicNumber")
private fun getRandomUser(): UserModel {
return UserModel(
id = -1,
@@ -110,6 +111,7 @@ internal class DefaultSampleGUIComponent(
override fun onUiCreated() {
launch(Dispatchers.IO) {
+ @Suppress("MagicNumber")
delay(1000)
loadItemsModel()
}
diff --git a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/feature/SampleGuiComponent.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/feature/SampleGuiComponent.kt
similarity index 91%
rename from instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/feature/SampleGuiComponent.kt
rename to modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/feature/SampleGuiComponent.kt
index b49a715..b3bb307 100644
--- a/instances/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/gui/sample/feature/SampleGuiComponent.kt
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/feature/SampleGuiComponent.kt
@@ -1,4 +1,4 @@
-package ru.astrainteractive.astratemplate.gui.sample.feature
+package ru.astrainteractive.astratemplate.feature.gui.sample.feature
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.StateFlow
diff --git a/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/gui/SampleGUI.kt b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/gui/SampleGUI.kt
new file mode 100644
index 0000000..8422497
--- /dev/null
+++ b/modules/feature-gui/bukkit/src/main/kotlin/ru/astrainteractive/astratemplate/feature/gui/sample/gui/SampleGUI.kt
@@ -0,0 +1,178 @@
+package ru.astrainteractive.astratemplate.feature.gui.sample.gui
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import net.kyori.adventure.text.Component
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.event.inventory.InventoryOpenEvent
+import ru.astrainteractive.astralibs.coroutines.withTimings
+import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
+import ru.astrainteractive.astralibs.menu.core.setInventorySlot
+import ru.astrainteractive.astralibs.menu.inventory.api.InventoryMenu
+import ru.astrainteractive.astralibs.menu.inventory.model.InventorySize
+import ru.astrainteractive.astralibs.menu.layout.mapSlotsNotNullIndexed
+import ru.astrainteractive.astralibs.menu.paginator.api.DefaultPaginator
+import ru.astrainteractive.astralibs.menu.paginator.api.context
+import ru.astrainteractive.astralibs.menu.paginator.api.openNextPage
+import ru.astrainteractive.astralibs.menu.paginator.api.openPrevPage
+import ru.astrainteractive.astralibs.menu.paginator.api.setMaxItems
+import ru.astrainteractive.astralibs.menu.paginator.model.indexOfSlot
+import ru.astrainteractive.astralibs.menu.paginator.model.isFirstPage
+import ru.astrainteractive.astralibs.menu.paginator.model.isLastPage
+import ru.astrainteractive.astralibs.menu.slot.InventorySlot
+import ru.astrainteractive.astratemplate.feature.gui.button.addUser
+import ru.astrainteractive.astratemplate.feature.gui.button.back
+import ru.astrainteractive.astratemplate.feature.gui.button.border
+import ru.astrainteractive.astratemplate.feature.gui.button.changeMode
+import ru.astrainteractive.astratemplate.feature.gui.button.di.ButtonContext
+import ru.astrainteractive.astratemplate.feature.gui.button.itemSlot
+import ru.astrainteractive.astratemplate.feature.gui.button.nextPage
+import ru.astrainteractive.astratemplate.feature.gui.button.prevPage
+import ru.astrainteractive.astratemplate.feature.gui.button.userSlot
+import ru.astrainteractive.astratemplate.feature.gui.layout.DefaultSampleInventoryLayoutFactory
+import ru.astrainteractive.astratemplate.feature.gui.layout.SampleSlotKey
+import ru.astrainteractive.astratemplate.feature.gui.sample.feature.SampleGuiComponent
+import ru.astrainteractive.klibs.mikro.core.coroutines.CoroutineFeature
+import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
+
+internal class SampleGUI(
+ private val buttonContext: ButtonContext,
+ private val dispatchers: KotlinDispatchers,
+ private val sampleComponent: SampleGuiComponent
+) : InventoryMenu(),
+ KyoriComponentSerializer by buttonContext {
+
+ override val childComponents: List
+ get() = listOf(sampleComponent)
+
+ override val title: Component = toComponent(buttonContext.translation.menu.menuTitle)
+ override val inventorySize: InventorySize = InventorySize.XL
+
+ override val menuScope: CoroutineScope = CoroutineFeature
+ .Default(dispatchers.Main)
+ .withTimings()
+
+ private val inventoryMap by lazy {
+ DefaultSampleInventoryLayoutFactory.create(isCompact = true)
+ }
+
+ private val paginator = DefaultPaginator(
+ maxItemsPerPage = inventoryMap.count(SampleSlotKey.CONTENT_ITEM)
+ )
+
+ private val borderButtons: List
+ get() = inventoryMap.mapSlotsNotNull(
+ key = SampleSlotKey.BORDER,
+ transform = buttonContext::border
+ )
+
+ private val nextPageButton: InventorySlot
+ get() = buttonContext.nextPage(
+ index = inventoryMap.firstIndexOf(SampleSlotKey.NEXT_PAGE),
+ click = { onNextPageClicked() }
+ )
+
+ private val prevPageButton: InventorySlot
+ get() = buttonContext.prevPage(
+ index = inventoryMap.firstIndexOf(SampleSlotKey.PREV_PAGE),
+ click = { onPrevPageClicked() }
+ )
+
+ private val changeModeButton: InventorySlot
+ get() {
+ val label = when (sampleComponent.model.value) {
+ is SampleGuiComponent.Model.Items -> "Items"
+ is SampleGuiComponent.Model.Users -> "Users"
+ SampleGuiComponent.Model.Loading -> "Loading"
+ }
+ return buttonContext.changeMode(
+ index = inventoryMap.firstIndexOf(SampleSlotKey.CHANGE_MODE),
+ label = label,
+ click = { sampleComponent.onModeChange() }
+ )
+ }
+
+ private val addUserButton: InventorySlot
+ get() = buttonContext.addUser(
+ index = inventoryMap.firstIndexOf(SampleSlotKey.ADD_USER),
+ click = { sampleComponent.onAddUserClicked() }
+ )
+
+ private val backButton: InventorySlot
+ get() = buttonContext.back(
+ index = inventoryMap.firstIndexOf(SampleSlotKey.BACK),
+ click = { e -> e.whoClicked.closeInventory() }
+ )
+
+ private val contentSlots: List
+ get() {
+ val state = sampleComponent.model.value
+ return inventoryMap.mapSlotsNotNullIndexed(SampleSlotKey.CONTENT_ITEM) { iterIndex, slotIndex ->
+ val dataIndex = paginator.context.indexOfSlot(iterIndex)
+ when (state) {
+ is SampleGuiComponent.Model.Items -> state.items.getOrNull(dataIndex)?.let { item ->
+ buttonContext.itemSlot(
+ index = slotIndex,
+ itemStack = item,
+ click = { sampleComponent.onItemClicked(dataIndex, it.click) }
+ )
+ }
+ is SampleGuiComponent.Model.Users -> state.users.getOrNull(dataIndex)?.let { user ->
+ buttonContext.userSlot(
+ index = slotIndex,
+ user = user,
+ randomColor = sampleComponent.randomColor,
+ click = { sampleComponent.onItemClicked(dataIndex, it.click) }
+ )
+ }
+ SampleGuiComponent.Model.Loading -> null
+ }
+ }
+ }
+
+ private fun onNextPageClicked() {
+ paginator.openNextPage()
+ render()
+ }
+
+ private fun onPrevPageClicked() {
+ paginator.openPrevPage()
+ render()
+ }
+
+ override fun onInventoryClickEvent(e: InventoryClickEvent) {
+ super.onInventoryClickEvent(e)
+ e.isCancelled = true
+ }
+
+ override fun onInventoryOpenEvent(e: InventoryOpenEvent) {
+ sampleComponent.onUiCreated()
+ sampleComponent.model
+ .onEach { state ->
+ paginator.setMaxItems(
+ when (state) {
+ is SampleGuiComponent.Model.Items -> state.items.size
+ is SampleGuiComponent.Model.Users -> state.users.size
+ SampleGuiComponent.Model.Loading -> 0
+ }
+ )
+ }
+ .onEach { render() }
+ .launchIn(menuScope)
+ }
+
+ override fun render() {
+ super.render()
+ setInventorySlot(borderButtons)
+ setInventorySlot(changeModeButton)
+ setInventorySlot(backButton)
+ if (!paginator.context.isFirstPage) setInventorySlot(prevPageButton)
+ if (!paginator.context.isLastPage) setInventorySlot(nextPageButton)
+ val state = sampleComponent.model.value
+ if (state is SampleGuiComponent.Model.Users) {
+ setInventorySlot(addUserButton)
+ }
+ setInventorySlot(contentSlots)
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 9732805..24e98e7 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,49 +1,48 @@
pluginManagement {
repositories {
- maven("https://maven.fabricmc.net/") { name = "Fabric" }
- maven("https://files.minecraftforge.net/maven")
- maven("https://dist.creeper.host/Sponge/maven")
- maven("https://maven.minecraftforge.net/")
- maven("https://plugins.gradle.org/m2/")
- maven("https://jitpack.io")
- gradlePluginPortal()
- mavenCentral()
mavenLocal()
- google()
- }
-}
-
-buildscript {
- repositories {
- maven("https://files.minecraftforge.net/maven")
- maven("https://dist.creeper.host/Sponge/maven")
- maven("https://maven.minecraftforge.net/")
- maven("https://plugins.gradle.org/m2/")
mavenCentral()
- mavenLocal()
+ gradlePluginPortal()
+ maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
+ maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
+ maven("https://papermc.io/repo/repository/maven-public/")
+ maven("https://repo.papermc.io/repository/maven-public/")
+ maven("https://oss.sonatype.org/content/groups/public/")
+ maven("https://nexus.scarsz.me/content/groups/public/")
+ maven("https://repo.dmulloy2.net/repository/public/")
+ maven("https://repo.essentialsx.net/snapshots/")
+ maven("https://files.minecraftforge.net/maven")
+ maven("https://repo.maven.apache.org/maven2/")
+ maven("https://maven.neoforged.net/releases")
+ maven("https://maven.enginehub.org/repo/")
+ maven("https://maven.minecraftforge.net")
+ maven("https://repo1.maven.org/maven2/")
+ maven("https://maven.fabricmc.net/")
+ maven("https://maven.playpro.com")
+ maven("https://jitpack.io")
}
}
dependencyResolutionManagement {
repositories {
- gradlePluginPortal()
- mavenCentral()
mavenLocal()
- maven("https://mvn.lumine.io/repository/maven-public/") { metadataSources { artifact() } }
+ mavenCentral()
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
+ maven("https://papermc.io/repo/repository/maven-public/")
+ maven("https://repo.papermc.io/repository/maven-public/")
+ maven("https://oss.sonatype.org/content/groups/public/")
maven("https://nexus.scarsz.me/content/groups/public/")
- maven {
- name = "papermc"
- url = uri("https://repo.papermc.io/repository/maven-public/")
- }
maven("https://repo.dmulloy2.net/repository/public/")
maven("https://repo.essentialsx.net/snapshots/")
+ maven("https://repo.essentialsx.net/releases/")
+ maven("https://files.minecraftforge.net/maven")
maven("https://repo.maven.apache.org/maven2/")
- maven("https://maven.minecraftforge.net/")
maven("https://maven.enginehub.org/repo/")
- maven("https://m2.dv8tion.net/releases")
+ maven("https://maven.minecraftforge.net/")
+ maven("https://libraries.minecraft.net/")
maven("https://repo1.maven.org/maven2/")
+ maven("https://maven.fabricmc.net/")
maven("https://maven.playpro.com")
maven("https://jitpack.io")
}
@@ -52,13 +51,17 @@ dependencyResolutionManagement {
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "AstraTemplate"
-// Shared
-include("modules:api-remote")
-include("modules:api-local")
-include("modules:core")
-include("modules:build-konfig")
-// Instances
include(":instances:bukkit")
-include(":instances:fabric")
-include(":instances:velocity")
include(":instances:forge")
+include(":instances:neoforge")
+
+include("modules:api:remote")
+include("modules:api:local")
+include("modules:core")
+include("modules:build-konfig")
+include("modules:feature-command")
+include("modules:feature-gui:api")
+include("modules:feature-gui:bukkit")
+include("modules:feature-event:bukkit")
+include("modules:feature-event:forge")
+include("modules:feature-event:neoforge")