Kludge
All of my Kotlin plugins use a library I’ve made called Kludge. It’s essentially a large collection of utilities, designed to take advantage of Kotlin’s extension, operator, and property support to simplify a few things. I’ve decided to release it since it could be useful to people.
Feature list
- A global plugin variable, so you don’t have to pass it to things like task builders.
- Text manipulation has never been so easy.
- A rich
replacefunction. Never again mess about withTextTemplatefor on-the-fly replacement! Replace a string or regex with a text.- Formatting in the original text is perfectly preserved.
- Optionally enable lossy mode to allow some formatting to be lost in the event that the string to be replaced is spread out over multiple text children (use this for e.g. swear filtering color-code-enabled chat).
- You can’t use regex
$varreferences in the replacement text, but I’m working on it!
- Convert to text anything that can be converted by using the
!operator or thetext()function, and convert to builder withtextBuilder(). - Format any text convertibles into text immediately using provided
color,format, andstylefunctions, along with individual functions for each color and style, as well asonClick,onHover, andonShiftClick.- This includes
Textitself!
- This includes
- Use
+to construct text or aText.Builder, and-to remove from aText.Builder. You can even use+onTextwith a receiver closure. - ALL of this preserves types as much as possible. If you call
!"Text", you get aLiteralText. If you callScoreText#red(), you get aScoreText. Etc. -
Stringhas two extra functions -textByJsonandtextByFormat. - Join arrays or iterables to a text with
joinToText.
- A rich
- Builders. Every builder in the API has a
whichamajiggerOfstyle function , which you call with a receiver lambda, for Kotlin-style builders. Yes, every builder.-
dataRegistrationOf,taskOf, and other similar builders support the global plugin.
-
- Service properties. Why call the
ServiceManagerwhen you can just delegate a property to it? Just write your property like this:val svc: EconomyService? by Serviceand when you call it, it’ll call theServiceManagerfor you.- There’s also
UncheckedServicefor non-nullable properties. - Use it like a constructor instead of an object to cache the result.
- There’s also
- A couple of
Optionalhelpers. Useunwrap()or the!operator on anOptionalto convert it to a nullable type, and.optional()on a nullable type to convert toOptional. - Call
command()on yourCommandExecutor-applicable function to convert it into one, or pass it intoCommandSpec.Builderdirectly. - Operator all the things!.
- The Data API:
- Use index getters and setters and the
inoperator forKeys, classes,Values, etc. -
+=and-=add and remove data from holders -
%and%=work with raw data orfill -
-=rolls back a data transaction - Use the bare operator (i.e. without
=) to get theDataTransactionResult
- Use index getters and setters and the
- Scoreboards:
-
Scorehas full numerical functionality -
Teamhas+=,-=, andinfor members -
Scoreboardhas+=,-=, andinfor objectives, and indexers forDisplaySlots -
Objectivehas+=,-=, andinforScores,-=andinfor names, an indexer to get a score, and an invoker togetOrCreatea score.
-
- Advancements:
-
ScoreCriterionProgresshas full numerical functionality -
AdvancementCriterion'sandandorfunctions are now infix -
TreeLayouthas an indexer -
Advancementnow has atoastTextproperty
-
- Inventories:
-
+=to add items to an inventory - Indexers for properties, queries, getting slots at indexes or positions, and setting items at indexes or positions.
- Invokers for peeking at items at indexes or positions
- Inventory now has a
slotsproperty, which fixes ducktyping on theslots()function - In general just pretend the API was written with Kotlin in mind, and write anything that you think makes sense, and it’ll probably work
-
- Flow Math:
- All the vectory and matrixy types now support all the math operators they can
- The Data API:
- All the static singletons you get from
SpongeandGameRegistrynow act like objects (you could callCommandManager.registerdirectly, etc.) -
UUIDhasplayer(),user(), andprofile()functions -
Playernow has astorageInventoryproperty which gets theMainPlayerInventorydirectly -
MessageReceivernow has all the functions ofChatTypeMessageReceiver. If the receiver happens to be aChatTypeMessageReceiver, then its functions get called normally; otherwise, the extraChatTypeargument is ignored and theMessageReceiverfunctions are called instead. Because even a smart cast is too much boilerplate for message sending. If you care if theChatTypewas actually used, the methods returntrueif it was andfalseif it wasn’t. -
Vector3dnow haseulerToDirectionanddirectionToEuler. In addition,Transformnow has a.directionproperty. - Command arguments can now be done with a closure. This removes the need to put
GenericArguments.before your arguments.- Note that any custom
CommandElements you use must be prefixed with a+in the closure. - You can also just generate a
CommandElementdirectly, withcommandArgumentsOf.
- Note that any custom
- Less
Tasknoise.
*sync(plugin) { doThing() }to run on the main server thread,async(plugin) { doThing() }to run off the main server thread. If you’re already on the main server thread,syncjust executes immediately.-
delay(plugin, 30) { doThing() }to delay by a certain number of ticks. - Coroutines! Use
task(plugin) { }to begin a coroutine that usesTasks for continuations.- Now you can write what you mean.
desync()to move off the server thread,resync()to move back on the server thread,delayanddelayTicksto wait for a period of time. All of these backend toTaskjumping. - Fully compatible with all other methods of starting or stopping coroutines.
- Now you can write what you mean.
- Supports the global plugin, letting you remove the
pluginargument.
-
- Less
Itemnoise.Playerhas agivefunction which deposits the item into their inventory and drops whatever didn’t fit on the ground next to them. - Less
ItemStacknoise.-
ItemType,BlockType,BlockState,BlockSnapshotandLocationnow havetoStackfunctions which turn them intoItemStacks. Optionally, open out a closure to keep customizing the builder. -
ItemStack.BuilderhasdisplayNameandlorefunctions.
-
- Other little things like
String#toUUID()andtypeTokenOf.
Feel free to suggest more features!
How to include in your plugin
First, enable the shadow plugin if you haven’t already.
How to enable the shadow plugin
- Add to your
pluginsblockid 'com.github.johnrengelman.shadow' version '1.2.4'. - Set
jar.enabled = falseandbuild.dependsOn shadowJar. If you have the signing plugin enabled, addsignArchives.dependsOn shadowJar. - Add to your
configurationsblockcompile.extendsFrom shadow. - Create a
shadowJarblock:
shadowJar {
classifier = null
configurations = [project.configurations.shadow]
}
Add the Maven repository https://jitpack.io/ and the shadow dependency com.github.pie-flavor:Kludge.
- The ‘latest’ version is
master-SNAPSHOT, but I would advise against using this as the API could break, and this isn’t going to be numerically versioned. Instead, for the version use the shortened commit hash you get from GitHub (eecb6d2at the time of writing).
To prevent version conflicts with anyone else using it, add relocate 'flavor.pie.kludge', 'my.plugin.package.util' to your shadowJar block.
A recommended setting for IntelliJ IDEA is to go to Editor > Code Style > Kotlin > Imports, and add flavor.pie.kludge to Packages to Use Import with '*'.
