{"id":47660866,"url":"https://github.com/integr-dev/backbone","last_synced_at":"2026-04-02T11:03:28.186Z","repository":{"id":334984719,"uuid":"1142804250","full_name":"integr-dev/backbone","owner":"integr-dev","description":"Backbone provides a robust and flexible framework for developing Minecraft servers. ","archived":false,"fork":false,"pushed_at":"2026-03-31T13:48:42.000Z","size":1301,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-31T15:32:06.992Z","etag":null,"topics":["bstats","bukkit-api","framework","jackson-yaml","kotlin","kotlin-coroutines","kotlin-scripting","minecraft","parser","spigot","sqlite-database"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/integr-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-26T21:55:44.000Z","updated_at":"2026-03-31T13:45:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/integr-dev/backbone","commit_stats":null,"previous_names":["integr-dev/backbone"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/integr-dev/backbone","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integr-dev%2Fbackbone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integr-dev%2Fbackbone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integr-dev%2Fbackbone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integr-dev%2Fbackbone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/integr-dev","download_url":"https://codeload.github.com/integr-dev/backbone/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integr-dev%2Fbackbone/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31305031,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T09:48:21.550Z","status":"ssl_error","status_checked_at":"2026-04-02T09:48:19.196Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bstats","bukkit-api","framework","jackson-yaml","kotlin","kotlin-coroutines","kotlin-scripting","minecraft","parser","spigot","sqlite-database"],"created_at":"2026-04-02T11:03:23.413Z","updated_at":"2026-04-02T11:03:28.169Z","avatar_url":"https://github.com/integr-dev.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--suppress CheckImageSize --\u003e\n\u003cimg alt=\"logo.png\" src=\"logo.png\" width=\"20%\"/\u003e\n\n# Backbone\n[![CI](https://github.com/integr-dev/backbone/actions/workflows/ci.yaml/badge.svg)](https://github.com/integr-dev/backbone/actions/workflows/ci.yaml)\n[![GitHub release](https://img.shields.io/github/v/release/integr-dev/backbone)](https://github.com/integr-dev/backbone/releases/latest)\n![GitHub last commit](https://img.shields.io/github/last-commit/integr-dev/backbone)\n[![GitHub license](https://img.shields.io/github/license/integr-dev/backbone)](https://github.com/integr-dev/backbone/blob/master/LICENSE)\n\nBackbone is a powerful and flexible plugin for Spigot-based Minecraft servers, designed to supercharge server customization. Its core philosophy is to enable server administrators and developers to write, test, and update server logic on a live server without requiring restarts, dramatically speeding up the development lifecycle.\n\nWhether you're a server administrator looking to add custom features with simple scripts or a developer prototyping new ideas, Backbone provides the tools you need to be more productive and creative.\n\n## Features\n\n- **Hot-Loadable Scripts:** Write and reload Kotlin scripts without restarting the server, enabling rapid development and iteration.\n- **Advanced Scripting:** Go beyond simple scripts with support for inter-script imports, Maven dependencies, and custom compiler options.\n- **Event System:** A custom event bus that complements Bukkit's event system, offering more control and flexibility within your scripts.\n- **Command Framework:** A simple yet powerful command system to create custom commands directly from your scripts.\n- **Storage Abstraction:** Manage data with a flexible storage system that supports SQLite databases and typed configuration files.\n- **GUI Framework:** A declarative GUI framework for creating complex and interactive inventories from your scripts.\n- **Text Formatting:** A flexible text formatting system with support for custom alphabets and color codes.\n- **Entity Framework:** Custom entity utility for adding custom entities via the goals api.\n- **Display Entity Rendering System:** A powerful display entity rendering system for creating custom visuals\n- **Custom Item Framework:** A stateful custom item framework for creating items with unique abilities.\n\n## Getting Started\n\nGetting started with Backbone is straightforward. The primary way to use Backbone is by installing it as a plugin and then creating your own custom features through its scripting engine.\n\n### Requirements\n- Minecraft Java Edition Server version 1.21 or higher.\n- [PlaceholderAPI](https://modrinth.com/plugin/placeholderapi) (optional, for placeholder support).\n\n### Installation\n1.  **Download:** Download the latest release from the [official releases page](https://github.com/integr-dev/backbone/releases).\n2.  **Install:** Place the downloaded `.jar` file into your server's `plugins` directory.\n3.  **Start:** Launch your server. Backbone will generate its necessary folders in your server's root directory.\n4.  **Scripting:** You can now begin writing custom logic in `.bb.kts` script files inside the `scripts/` directory. See the examples below to get started!\n\nFor advanced users who wish to build a plugin that depends on Backbone, you can add it as a dependency. However, for most use cases, the scripting engine provides all the power you need.\n\n## Management Commands\nBackbone comes with a set of powerful management commands to control its various systems. The base command is `/backbone`, which can also be accessed using the alias `/bb`.\n\n### Scripting Commands\nThe `/bb scripting` command provides tools to manage your hot-loadable scripts.\n\n-   `/bb scripting`: Lists all discovered scripts and their current status (enabled/disabled).\n-   `/bb scripting reload`: Reloads all scripts, applying any changes you've made.\n-   `/bb scripting enable \u003cscript_name\u003e`: Enables a disabled script.\n-   `/bb scripting disable \u003cscript_name\u003e`: Disables an enabled script.\n-   `/bb scripting wipe \u003cscript_name\u003e \u003cconfirmation\u003e`: Wipes the persistent state of a script. Requires the script name to be entered twice for confirmation.\n\n### Custom Item Commands\nThe `/bb item` command allows you to interact with the custom item system.\n\n-   `/bb item`: Lists all registered custom items.\n-   `/bb item give \u003citem_name\u003e`: Gives you the specified custom item.\n-   `/bb item replicate`: Creates a new instance of the custom item you are currently holding.\n-   `/bb item read`: Reads and displays the NBT tags of the item you are holding.\n\n### Custom Entity Commands\nThe `/bb entity` command provides control over custom entities.\n\n-   `/bb entity`: Lists all registered custom entities.\n-   `/bb entity spawn \u003centity_name\u003e`: Spawns the specified custom entity at your location.\n\n## Examples\n\nAll examples are designed to be placed in their own `.bb.kts` files inside the `scripts/` directory.\n\n### Hot-Loadable Scripts\n\nBackbone's most powerful feature is its hot-loadable script engine. This allows you to write Kotlin code in script files (`.bb.kts`) and load, unload, and reload them on the fly without needing to restart your server. This is incredibly useful for rapid development, prototyping, and live server updates.\n\n#### Script Location and Aggregation\n\nAll script files should be placed in the `scripts/` directory in your server's root. The script engine automatically discovers and compiles any `.bb.kts` files in this location when scripts are loaded.\n\n#### Script Structure\n\nEvery script file must use the new lifecycle DSL, which provides a concise and modern way to define script lifecycle hooks and event listeners.\n\n```kotlin\nlifecycle {\n    // 'sustained' properties persist their values across script reloads.\n    var counter by sustained(0)\n    // Standard variables are reset every time the script is reloaded.\n    var otherCounter = 0\n\n    onLoad {\n        println(\"Script loaded! Counter: $counter | Other Counter: $otherCounter\")\n    }\n\n    onUnload {\n        println(\"Script unloaded! Counter: $counter | Other Counter: $otherCounter\")\n    }\n\n    // This event fires every server tick while the script is enabled.\n    listener\u003cTickEvent\u003e { event -\u003e\n        counter++\n        otherCounter++\n        if (counter % 20 == 0) {\n            Backbone.PLUGIN.server.onlinePlayers\n                .forEach { it.sendMessage(\"Sustained Count: $counter | Volatile Count: $otherCounter\") }\n        }\n    }\n}\n```\n\n### Advanced Scripting\n\nYou can make your scripts even more powerful by using file-level annotations to manage dependencies and compiler settings.\n\n#### Sharing Code Between Scripts\nShared logic MUST be placed in utility scripts with the `.bbu.kts` extension. These scripts are automatically compiled and their classes and functions are injected into the classpath and default imports of all main `.bb.kts` scripts, allowing you to easily share code across multiple scripts.\nIf this is not done, the classes and functions defined in a script will only be available to that script. Utility scripts must not be a lifecycle object and cannot contain event listeners or lifecycle hooks. They are purely for defining shared code.\nYou can define utility scripts with the `.bbu.kts` file extension. These scripts function as shared libraries. Backbone automatically compiles them and injects their classes and functions into the classpath and default imports of all main `.bb.kts` scripts.\n\n**`utils.bbu.kts`**\n```kotlin\nclass MyUtilities {\n    fun getGreeting(): String = \"Hello from a utility script!\"\n}\n```\nThis means you can use the defined classes and methods in your main scripts as if they were in the same file.\n\n**`main.bb.kts`**\n```kotlin\n// ... inside your ManagedLifecycle\nval utils = MyUtilities()\nprintln(utils.getGreeting()) // Prints \"Hello from a utility script!\"\n```\n\n#### Managing Dependencies with `@DependsOn` and `@Repository`\n\nYou can pull in external libraries directly from Maven repositories using the `@DependsOn` and `@Repository` annotations. This lets you use powerful third-party libraries without having to manually bundle them with your server.\n\n```kotlin\n// Add a custom Maven repository\n@file:Repository(\"https://jitpack.io\")\n// Depend on a library from that repository\n@file:DependsOn(\"com.github.javafaker:javafaker:1.0.2\")\n\n// ... inside your script\nval faker = Faker()\nval randomName = faker.name().fullName()\nprintln(\"A random name: $randomName\")\n```\n\n#### Customizing with `@CompilerOptions`\n\nThe `@CompilerOptions` annotation gives you fine-grained control over the Kotlin compiler, allowing you to enable specific language features or pass other flags.\n\n```kotlin\n// Enable a specific language feature, like context receivers, or just pass any plain old compiler option\n@file:CompilerOptions(\"-Xcontext-receivers\")\n\n// Your script code can now use context receivers\ncontext(String)\nfun greet() {\n    println(\"Hello, $this\")\n}\n```\n\n### Storage and Configuration\n\nBackbone provides a simple and powerful way to manage your plugin's data and configuration through `ResourcePool`s. This system allows you to easily handle databases and configuration files in a structured manner.\n\n#### Resource Pools\n\nA `ResourcePool` is a namespaced container for your resources. It's recommended to create a separate pool for each script or feature set to avoid conflicts.\n\n```kotlin\n// Create a resource pool for your script's storage\nval myScriptStorage = ResourcePool.fromStorage(\"mystorage\")\n\n// Create a resource pool for your script's configuration\nval myScriptConfig = ResourcePool.fromConfig(\"myconfig\")\n```\n\nThis will create directories at `storage/mystorage/` and `config/myconfig/` in your server's root directory.\n\n#### Configuration\n\nYou can manage typed configuration files. Backbone handles the serialization and deserialization of your data classes automatically.\n\nFirst, define a serializable data class for your configuration:\n\n```kotlin\n@Serializable // Requires the kotlinx.serialization plugin\ndata class MyConfig(val settingA: String = \"default\", val settingB: Int = 10)\n```\n\nThen, use the `config()` function on your resource pool to get a handler for it:\n\n```kotlin\n// Get a handler for a config file named 'settings.yml'\nval configHandler = myScriptConfig.config\u003cMyConfig\u003e(\"settings.yml\")\n\n// Load the config file synchronously\nconfigHandler.updateSync()\n\n// Get the current config from the cache\nval currentConfig = configHandler.getState()\nprintln(\"Setting A: ${currentConfig?.settingA}\")\n\n// Modify and save the config asynchronously\nconfigHandler.writeState(currentConfig.copy(settingB = 20))\n```\n\n#### Databases\n\nBackbone provides a simple and efficient way to work with SQLite databases from within your scripts.\n```kotlin\n// Get a connection to a database file named 'playerdata.db'\nval dbConnection = myScriptStorage.database(\"playerdata.db\")\n\n// The useConnection block handles connection setup and teardown, preventing resource leaks.\ndbConnection.useConnection {\n    // The 'this' context is a StatementCreator instance.\n    execute(\"CREATE TABLE IF NOT EXISTS players (uuid TEXT PRIMARY KEY, name TEXT NOT NULL);\")\n\n    // Use a prepared statement to safely insert data.\n    preparedStatement(\"INSERT INTO players (uuid, name) VALUES (?, ?)\") {\n        setString(1, \"some-uuid\")\n        setString(2, \"PlayerName\")\n        executeUpdate()\n    }\n\n    val playerName = query(\"SELECT name FROM players WHERE uuid = 'some-uuid'\") { it.getString(\"name\") }\n    println(\"Found player: $playerName\")\n}\n```\n\n### Custom Events\n\nBackbone's event system allows you to create and listen to custom events, giving you more control over your script's behavior.\n\n```kotlin\n// Define a custom event\nclass MyCustomEvent(val message: String) : Event()\n\nlifecycle {\n    // Register a listener for the custom event.\n    // Priority ranges from -3 to 3, with 0 being normal. Lower values execute first.\n    listener\u003cMyCustomEvent\u003e(priority = EventPriority.THREE_BEFORE) { event -\u003e\n        println(\"Received custom event: ${event.message}\")\n        event.callback = \"yay!\"\n    }\n}\n\n// Fire the custom event from anywhere in your code\nval callback = EventBus.post(MyCustomEvent(\"Hello, world!\")) // \"yay!\"\n```\n\n### Inter-Script Communication\n\nBackbone allows scripts to communicate with each other using inter-script messages. This is useful for modular scripts, decoupled features, or sharing data/events between scripts at runtime.\n\n#### Sending a Message\n\nUse `dispatchInterScript` to send a message with a string id and a data map:\n\n```kotlin\nlifecycle {\n    listener\u003cPlayerBucketFillEvent\u003e { event -\u003e\n        dispatchInterScript(\"abc\") {\n            put(\"abc\", \"Hello from the sender script!\")\n        }\n    }\n}\n```\n\n#### Receiving a Message\n\nUse `interScript` to listen for messages with a specific id. The handler receives an `IscMap` for type-safe data access:\n\n```kotlin\nlifecycle {\n    interScriptListener(\"abc\") { map -\u003e\n        val abc = map.pull\u003cString\u003e(\"abc\")\n        println(\"Received inter-script message: $abc\")\n    }\n}\n```\n\n#### How it Works\n- Messages are delivered synchronously to all scripts listening for the given id.\n- The data is passed as an immutable `IscMap`, which supports type-safe retrieval with `pull\u003cT\u003e(key)`.\n- This system is ideal for modular scripts, cross-script events, and decoupled communication.\n\n### HTTP Requests in Scripts\n\nBackbone scripts can easily make HTTP requests and handle responses using the built-in DSL. This is useful for integrating with web APIs, fetching data, or interacting with external services directly from your scripts.\n\n#### Example: Making an HTTP Request in a Script\n\nYou can use the `requestAndThen` function inside your script's lifecycle to perform HTTP requests asynchronously. The response is provided to a callback, where you can process the result and interact with the server.\n\n```kotlin\nlifecycle {\n    onLoad {\n        requestAndThen(\"https://httpbin.org/get\", HttpMethod.GET, {\n            header(\"User-Agent\", \"Backbone Script Handler\")\n        }) { response -\u003e\n            Backbone.SERVER.broadcast(component {\n                append(\"HTTP Response: ${response.json()}\") {\n                    color(Color.GREEN)\n                }\n            })\n        }\n    }\n}\n```\n\n- `requestAndThen(url, method, { ...headers... }) { response -\u003e ... }` performs an HTTP request and provides the response to the callback.\n- The `response` object supports JSON parsing and mapping, making it easy to work with API responses.\n- You can use this in any lifecycle hook, such as `onLoad`, or in event listeners.\n\n### Commands\n\nBackbone's command framework makes it easy to create and manage commands with arguments, subcommands, and permission checks.\n\nCommands are executed asynchronously by default. For this reason, any API calls that modify server state must be wrapped in a `Backbone.dispatchMain {}` block to ensure they run on the main server thread.\n\n```kotlin\n// Define a command\nobject MyCommand : Command(\"mycommand\", \"My first command\") {\n    val perm = PermissionNode(\"myplugin\")\n\n    override fun onBuild() {\n        // Register subcommands\n        subCommands(MySubCommand)\n\n        // Define arguments for the command\n        arguments(\n            scriptArgument(\"text\", \"My string\")\n        )\n    }\n\n    override suspend fun exec(ctx: Execution) {\n        // Require permission for this command\n        ctx.requirePermission(perm.derive(\"mycommand\")) // \"myplugin.mycommand\"\n\n        val text = ctx.get\u003cString\u003e(\"text\")\n\n        ctx.respond(\"Hello ${ctx.sender.name}: $text\")\n\n        // To affect the server state, dispatch to the main thread for the next tick.\n        Backbone.dispatchMain {\n            val player = ctx.getPlayer() // Get the sender as a player (and require it to be one)\n            player.world.spawnEntity(player.location, EntityType.BEE)\n        }\n\n        // Halt execution and mark the command as failed.\n        ctx.fail(\"Something is not right!\")\n    }\n}\n\n// In your ManagedLifecycle:\nlifecycle {\n    useCommand(MyCommand)\n}\n```\n\n### Custom Arguments\n\nYou can also create your own custom argument types by extending the `Argument` class. This allows you to define custom parsing and tab-completion logic.\n\nHere is an example of a custom `DoubleArgument`:\n\n```kotlin\nclass DoubleArgument(name: String, description: String) : Argument\u003cDouble\u003e(name, description) {\n    override fun getCompletions(current: ArgumentInput): CompletionResult {\n        val arg = current.getNextSingle()\n        val completions = if (arg.text.isBlank()) mutableListOf(\"\u003c$name:double\u003e\") else mutableListOf()\n        return CompletionResult(completions, arg.end)\n    }\n\n    override fun parse(current: ArgumentInput): ParseResult\u003cDouble\u003e {\n        val arg = current.getNextSingle()\n        val value = arg.text.toDoubleOrNull() ?: throw CommandArgumentException(\"Argument '$name' must be a valid double.\")\n        return ParseResult(value, arg.end)\n    }\n}\n```\n\nYou can then use this custom argument in your command definitions:\n\n```kotlin\narguments(\n    DoubleArgument(\"amount\", \"A decimal number\")\n)\n```\n\n### Custom Items\nBackbone includes a powerful framework for creating custom items with unique behaviors and state.\n\n#### Defining a Custom Item\nTo create a custom item, you extend the `CustomItem` class. This class allows you to define the item's ID, its default state, and its behavior when interacted with.\n\n```kotlin\n// Define a custom item\nobject MyItem : CustomItem(\"my_item\", MyItemState) {\n    // This method is called when a player interacts with the item\n    override fun onInteract(event: PlayerInteractEvent) {\n        event.player.sendMessage(\"You used My Item!\")\n    }\n}\n\n// Define the state for the custom item\nobject MyItemState : CustomItemState(Material.DIAMOND_SWORD, \"default\") {\n    override fun populate(stack: ItemStack) {\n        stack.applyMeta {\n            isUnbreakable = true\n        }\n    }\n}\n\n// In your ManagedLifecycle:\nlifecycle {\n    useItem(MyItem)\n}\n```\n\nYou can then give the item to a player using a command:\n```kotlin\n// In a command's exec method\nval item = MyItem.generate()\nctx.getPlayer().inventory.addItem(item)\n```\n\n### Custom Entities\nBackbone allows you to create custom entities with unique AI goals.\n\n```kotlin\n// Define a custom entity that is a non-moving zombie\nobject GuardEntity : CustomEntity\u003cZombie\u003e(\"guard\", EntityType.ZOMBIE) {\n    override fun prepare(mob: Zombie) {\n        // Set up, for example, armor\n    }\n\n    override fun setupGoals(mob: Zombie) {\n        // Clear existing goals and add a simple look goal\n        val goals = Backbone.SERVER.mobGoals\n        goals.removeAllGoals(mob)\n        goals.addGoal(mob, 1, LookAtPlayerGoal(mob))\n    }\n}\n\n// In your ManagedLifecycle:\nlifecycle {\n    useEntity(GuardEntity)\n}\n\n// You can then spawn the entity, for example, using a command\n// In a command's exec method:\nGuardEntity.spawn(ctx.getPlayer().location, ctx.getPlayer().world)\n```\n\n### Display Entity Rendering\nBackbone provides a powerful rendering system using display entities. This allows you to create custom visuals in the world.\n\nHere is an example of how to create a glowing box around a player:\n```kotlin\n// Create a renderable object\nval playerBox = BoxRenderable()\n\nlifecycle {\n    onLoad {\n        // Spawn the box when the script loads\n        val player = Backbone.SERVER.onlinePlayers.firstOrNull()\n        if (player != null) {\n            playerBox.spawn(player.world, player.location)\n        }\n    }\n    onUnload {\n        // Despawn the box when the script unloads\n        playerBox.despawn()\n    }\n}\n\n// In a tick event, update the box's position and appearance\nlifecycle {\n    listener\u003cTickEvent\u003e { event -\u003e\n        val player = Backbone.SERVER.onlinePlayers.firstOrNull()\n        if (player != null) {\n            playerBox.update(player.location, player.location.clone().add(0.0, 1.0, 0.0), Material.GLASS.createBlockData())\n        }\n    }\n}\n```\n\n### Custom Formatting and Utilities\n\nBackbone includes a flexible text component system that allows you to customize the look and feel of your script's output.\n\n### Components\n\nBackbone has a simple component builder abstraction that is based on the papermc `net.kyori.adventure.text.Component`\n\n\n```kotlin\ncomponent {\n    append(\"Hello\") {\n        color(Color.RED)\n    }\n\n    append(\"World\") {\n        color(Color.GREEN)\n        \n        onHover(HoverEvent.showText(component {\n            append(\"Hover\")\n        }))\n    }\n\n    append(\"!\") {\n        color(Color.YELLOW)\n    }\n}\n```\n\n#### Command Feedback Format\n\nYou can create a custom `CommandFeedbackFormat` to change how command responses are displayed. Or inherit from it to unlock even more customization via the component system.\n\n```kotlin\nval myFormat = CommandFeedbackFormat(\"MyPlugin\", Color.RED)\n\n// You can then pass this format to your command.\nobject MyCommand : Command(\"mycommand\", \"My first command\", format = myFormat) {\n    // ...\n}\n```\n\nThis will format command responses with a custom prefix and color. The `CommandFeedbackFormat` uses a custom `Alphabet` to encode the handler name, giving it a unique look.\n\n#### Custom Alphabets\n\nYou can create your own custom alphabets by implementing the `Alphabet` interface. This allows you to encode text in unique ways, such as the `BoldSmallAlphabet` used by the `CommandFeedbackFormat`.\n\n```kotlin\nobject MyAlphabet : Alphabet {\n    override val alphabet = \"...\" // Your custom alphabet characters\n}\n```\n\n### GUIs\n\nBackbone's GUI framework provides a declarative way to create and manage inventories. The GUI handler automatically manages state on a per-player basis, differentiating each player's unique inventory view.\n\n```kotlin\n// Create a Test Inventory Gui with 27 slots\nobject TestGui : Gui(component { append(\"Test Gui\") }, 27) {\n    // 'prepare' is run once during construction.\n    // Use this to define the static layout of your GUI, such as placing buttons.\n    override fun prepare(inventory: Inventory) {\n        inventory.setItem(0, ItemStack(Material.GOLDEN_APPLE))\n    }\n\n    // 'onOpen' is run whenever the inventory is first loaded for a player.\n    // Use this to dynamically load player-specific data.\n    override fun onOpen(player: Player, inventory: Inventory) {\n        // GUI has been opened for the player\n    }\n\n    // 'onClose' is called when the inventory is closed.\n    // Note: To open another GUI from this event, schedule it for the next tick\n    // by wrapping the .open() call in Backbone.dispatchMain {}.\n    override fun onClose(inventory: InventoryCloseEvent) {\n        // GUI has been closed\n    }\n\n    // 'onTick' runs every game tick for open GUIs.\n    // Useful for animations and other dynamic logic.\n    override fun onTick(inventory: Inventory) {\n        val randomSlot = (0 until inventory.size).random()\n        inventory.setItem(randomSlot, ItemStack(Material.APPLE))\n    }\n\n    // 'onClick' runs when a slot is clicked in this GUI.\n    override fun onClick(inventory: InventoryClickEvent) {\n        // A slot was clicked\n    }\n\n    // 'onInteract' runs on any interaction (including clicks).\n    override fun onInteract(inventory: InventoryInteractEvent) {\n        // An interaction occurred\n    }\n}\n\n// In a command or event within your script:\nTestGui.open(player)\n```\n\n### PlaceholderAPI Integration\nBackbone provides a set of placeholders through its soft dependency on PlaceholderAPI. If you have PlaceholderAPI installed, you can use the following placeholders in any plugin that supports them:\n\n- `%backbone_version%`: Displays the current version of the Backbone plugin.\n\nMore placeholders are planned for future releases.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintegr-dev%2Fbackbone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fintegr-dev%2Fbackbone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintegr-dev%2Fbackbone/lists"}