Skip to content

A high-performance, type-safe Kotlin library designed for Minecraft plugins to handle dynamic placeholders with built-in caching and smart state management.

License

Notifications You must be signed in to change notification settings

MrLarkyy/Replace

Repository files navigation

Replace 🔄

CodeFactor Reposilite Kotlin Discord

Replace is a high-performance, type-safe Kotlin library designed for Minecraft plugins to handle dynamic placeholders with built-in caching and smart state management.

✨ Key Features

  • Smart Updating: Automatically avoids redundant updates to save CPU and network bandwidth (crucial for packet-based systems).
  • Type-Safe Contexts: Link placeholders to specific types (e.g., Player, Entity, or custom objects).
  • Context Transformations: Easily map data types (e.g., provide a Game object and automatically inherit Player placeholders).
  • Update Intervals: Built-in throttling to control how often values are re-calculated.
  • Multi-Format: Support for String literals and Kyori Components out of the box.

📦 Installation

Add the repository and dependency to your build.gradle.kts:

repositories {
    maven("https://repo.nekroplex.com/releases")
}

dependencies {
    implementation("gg.aquatic.replace:Replace:26.0.2")
}

🚀 Quick Start

1. Define Placeholders

You can define placeholders that return simple strings or complex Kyori components.

// A constant placeholder (only calculated once per session)
val playerName = Placeholder.Literal<Player>("name", isConst = true) { player, _ -> 
    player.name 
}

// A dynamic placeholder with internal arguments (e.g., %stat_kills%)
val statPlaceholder = Placeholder.Literal<Player>("stat", isConst = false) { player, arg ->
    when (arg.lowercase()) {
        "kills" -> getKills(player).toString()
        "deaths" -> getDeaths(player).toString()
        else -> "0"
    }
}

2. Global Registration

Register placeholders globally so they are automatically included when creating new contexts for that type.

Placeholders.register(playerName, statPlaceholder)

3. Context & Transformations

A PlaceholderContext manages the lifecycle of your placeholders. You can transform contexts to reuse existing logic.

class MyGameSession(val player: Player, val score: Int)

// Create a context for MyGameSession that INHERITS all Player placeholders
val gameContext = Placeholders.resolverFor<MyGameSession>(
    maxUpdateInterval = 20, // 20 ticks
    transforms = arrayOf(
        Placeholders.Transform { it.player } // Tell the context how to get a Player from a MyGameSession
    )
)

4. Efficient Updating

The library uses a "State" system. You can check if a value actually changed before sending updates to a player.

val component = Component.text("Welcome %name%! Kills: %stat_kills%")
val contextItem = gameContext.createItem(mySession, component)

// Attempt to update (respects maxUpdateInterval)
val updateResult = contextItem.tryUpdate(mySession)

if (updateResult.wasUpdated) {
    val newComponent = updateResult.value
    player.sendMessage(newComponent)
}

5. Placeholder DSL (New! 🌳)

For complex placeholders with multiple branches and arguments (PAPI-style), you can use the built-in DSL. It handles underscored tokens and quoted arguments automatically.

Placeholders.registerDSL<Player>("rank") {
    // %rank_name%
    "name" {
        handle { getRankName(binder) }
    }
    
    // %rank_info_<rank>%
    "info" {
        stringArgument("target_rank") {
            handle { 
                val rank = string("target_rank")
                "Details for $rank..." 
            }
        }
    }

    // Optional arguments logic: %rank_status% or %rank_status_detailed%
    "status" {
        handle { "Simple Status" }
        "detailed" {
            handle { "Very Detailed Status" }
        }
    }
}

Example with shared handler

Placeholders.registerDSL<Player>("stat") {
    // Shared handler for both %stat_wins% and %stat_wins_<player>%
    "wins" {
        handle {
            val targetName = string("player") ?: binder.name
            getWins(targetName).toString()
        }
        stringArgument("player") {
            // No handler needed here; it automatically falls back to the parent
        }
    }
}

🔌 PlaceholderAPI Support

If PlaceholderAPI is present on the server, Replace can automatically wrap PAPI placeholders into its type-safe system using the papi identifier:

%papi_player_name% → Automatically resolved via PAPI.


🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


💬 Community & Support

Got questions, need help, or want to showcase what you've built with Replace? Join our community!

Discord Banner


Built with ❤️ by Larkyy

About

A high-performance, type-safe Kotlin library designed for Minecraft plugins to handle dynamic placeholders with built-in caching and smart state management.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages