Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,37 @@ Keeping the plugin compatible both with Paper and Spigot consumes a lot of time.
Now, Mimic is Paper-first as it is the most popular platform.
This means that compatibility with Spigot is not guaranteed.

To reduce the maintenance burden, support for versions older than 1.20 has been dropped.
Java 17 is required.
To reduce the maintenance burden, support for versions older than 1.20.6 has been dropped.
Java 21 is required.

### BlockRegistry API

This version introduces new *experimental* BlockRegistry API.
It can be used to get IDs of placed blocks and block items, set blocks, and break blocks passing drops to a custom consumer.
Block IDs from all registries are available via `Mimic.getBlockRegistry()`, namespaced with the registry ID.
Block IDs are [resource locations](https://minecraft.wiki/w/Resource_location): `minecraft:stone`, `craftengine:default/palm_log`.
ID without namespace or with the reserved `mimic` namespace is searched over all registries in order of priority.

```java
// Register a block registry
Mimic.getInstance().registerBlockRegistry(new MyBlockRegistry(), MimicApiLevel.CURRENT, plugin, ServicePriority.Normal);

// Set a block
BukkitBlockRegistry blockRegistry = Mimic.getInstance().getBlockRegistry();
blockRegistry.setBlock(location.getBlock(), "minecraft:stone_stairs", "[facing=north]");

// Break a block with a pickaxe, collecting drops into a chest inventory
blockRegistry.breakBlock(block, pickaxe, drops -> chestInventory.addItem(drops.toArray(new ItemStack[0])).values());
```

Added commands to debug block registries:

- `/mimic blocks info` - show information about block registries
- `/mimic blocks set [location] [block] (payload)` - set block at the given location
- `/mimic blocks break [location]` - break block at the given location with the item in hand
- `/mimic blocks compare [block]` - compare targeted block with the given block
- `/mimic blocks id` - print IDs of targeted block and of block placed by item in hand
- `/mimic blocks find [block]` - check that block with the given ID exists

### Added command `/mimic config`

Expand Down Expand Up @@ -40,7 +69,7 @@ Now it is possible to change Mimic config using commands in two ways:

### Housekeeping

- Update required Java 1.8 → 17
- Update required Java 1.8 → 21
- Update Kotlin 1.6.20 → 2.4.21, preserving API and Language level 2.2
- Replace ACF with CommandAPI
- Update Gradle 7.4.2 → 9.8.2
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]

paper = "1.20-R0.1-SNAPSHOT"
paper = "1.20.6-R0.1-SNAPSHOT"
kotlin = "2.4.21"
kotlinx-serialization = "1.12.0"
dokka = "2.2.0"
Expand Down
27 changes: 27 additions & 0 deletions mimic-api/api/mimic-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public final class ru/endlesscode/mimic/MimicApiLevel {
public static final field VERSION_0_6_2 I
public static final field VERSION_0_7 I
public static final field VERSION_0_8 I
public static final field VERSION_0_9 I
public static final fun checkApiLevel (I)Z
}

Expand All @@ -26,6 +27,32 @@ public abstract interface class ru/endlesscode/mimic/ProviderService : ru/endles
public abstract fun get (Ljava/lang/Object;)Ljava/lang/Object;
}

public abstract interface class ru/endlesscode/mimic/blocks/BlockRegistry : ru/endlesscode/mimic/MimicService {
public fun breakBlock (Ljava/lang/Object;)Z
public fun breakBlock (Ljava/lang/Object;Ljava/lang/Object;)Z
public abstract fun breakBlock (Ljava/lang/Object;Ljava/lang/Object;Lru/endlesscode/mimic/blocks/DropsConsumer;)Z
public abstract fun getBlockId (Ljava/lang/Object;)Ljava/lang/String;
public abstract fun getBlockIdFromItem (Ljava/lang/Object;)Ljava/lang/String;
public abstract fun getKnownIds ()Ljava/util/Collection;
public abstract fun isBlockExists (Ljava/lang/String;)Z
public fun isSameBlock (Ljava/lang/Object;Ljava/lang/String;)Z
public fun isSameBlockItem (Ljava/lang/Object;Ljava/lang/String;)Z
public fun setBlock (Ljava/lang/Object;Ljava/lang/String;)Z
public abstract fun setBlock (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Z
}

public abstract interface class ru/endlesscode/mimic/blocks/DropsConsumer {
public static final field Companion Lru/endlesscode/mimic/blocks/DropsConsumer$Companion;
public abstract fun consume (Ljava/util/Collection;)Ljava/util/Collection;
public static fun disappear ()Lru/endlesscode/mimic/blocks/DropsConsumer;
public static fun drop ()Lru/endlesscode/mimic/blocks/DropsConsumer;
}

public final class ru/endlesscode/mimic/blocks/DropsConsumer$Companion {
public final fun disappear ()Lru/endlesscode/mimic/blocks/DropsConsumer;
public final fun drop ()Lru/endlesscode/mimic/blocks/DropsConsumer;
}

public abstract interface class ru/endlesscode/mimic/classes/ClassSystem {
public abstract fun getClasses ()Ljava/util/List;
public fun getPrimaryClass ()Ljava/lang/String;
Expand Down
8 changes: 7 additions & 1 deletion mimic-api/src/main/kotlin/MimicApiLevel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public object MimicApiLevel {
*/
public const val VERSION_0_8: Int = 4

/**
* # Version 0.9
* - Experimental API: BlockRegistry
*/
public const val VERSION_0_9: Int = 5

/**
* The latest version at the moment of Mimic **COMPILATION**.
*
Expand All @@ -60,7 +66,7 @@ public object MimicApiLevel {
* Use [checkApiLevel] if you want to check that the current **RUNNING**
* Mimic API level meets to the required API level.
*/
public const val CURRENT: Int = VERSION_0_8
public const val CURRENT: Int = VERSION_0_9

/**
* Returns `true` if the current **RUNNING** Mimic API level is equal
Expand Down
75 changes: 75 additions & 0 deletions mimic-api/src/main/kotlin/blocks/BlockRegistry.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package ru.endlesscode.mimic.blocks

import ru.endlesscode.mimic.ExperimentalMimicApi
import ru.endlesscode.mimic.MimicService

/**
* Registry to get, set and break blocks.
*
* Block IDs are [resource locations](https://minecraft.wiki/w/Resource_location),
* where the namespace is the ID of a block registry, for example `craftengine:default/palm_log`.
* IDs without namespace or with the `mimic` namespace are searched over all registries.
*
* Implementations work with IDs without the registry namespace.
* A plugin's own namespace becomes the first path segment: `default:palm_log` -> `default/palm_log`.
*
* Methods changing blocks should be called from the server thread.
* They fire no place or break events and make no protection checks, the caller is responsible for both.
* Block physics is applied as usual, so neighboring blocks are updated.
* @since 0.9.0
*/
@ExperimentalMimicApi
public interface BlockRegistry<BlockT : Any, ItemStackT : Any> : MimicService {

/** Returns currently known block IDs. They may change while plugins load or reload their data. */
public val knownIds: Collection<String>

/** Returns `true` if the given [block] has the given [blockId]. */
public fun isSameBlock(block: BlockT, blockId: String): Boolean = getBlockId(block) == blockId

/** Returns `true` if the given [blockItem] places the block with the given [blockId]. */
public fun isSameBlockItem(blockItem: ItemStackT, blockId: String): Boolean =
getBlockIdFromItem(blockItem) == blockId

/** Returns `true` if a block with the given [blockId] exists. */
public fun isBlockExists(blockId: String): Boolean

/** Returns ID of the given [block], or `null` if the block doesn't belong to this registry. */
public fun getBlockId(block: BlockT): String?

/** Returns ID of the block placed by the given [blockItem], or `null` if it doesn't belong to this registry. */
public fun getBlockIdFromItem(blockItem: ItemStackT): String?

/** Sets the block with the given [blockId] in place of the empty [block]. */
public fun setBlock(block: BlockT, blockId: String): Boolean = setBlock(block, blockId, payload = null)

/**
* Sets the block with the given [blockId] configured with the [payload] in place of the empty [block].
* To replace a block, break it first, so its registry can clean up.
*
* Every implementation supports a [String] payload with block state properties, for example `[facing=north]`.
* Unsupported payload is ignored.
*
* Returns `false` if the [block] is not empty, the ID is unknown, or the block can't be set.
*/
public fun setBlock(block: BlockT, blockId: String, payload: Any?): Boolean

/** Breaks the given [block] with a suitable tool, dropping items on the ground. */
public fun breakBlock(block: BlockT): Boolean = breakBlock(block, tool = null, DropsConsumer.drop())

/** Breaks the given [block] with the [tool], dropping items on the ground. */
public fun breakBlock(block: BlockT, tool: ItemStackT?): Boolean =
breakBlock(block, tool, DropsConsumer.drop())

/**
* Breaks the given [block] with the [tool] and passes the dropped items to the [dropsConsumer].
*
* If [tool] is `null`, drops are the same as for a suitable tool without enchantments.
* Pass an empty item to break the block by hand.
* Only items are dropped, without experience and break effects.
* Use [DropsConsumer.disappear] to drop nothing.
*
* Returns `false` if the block can't be broken or doesn't belong to this registry.
*/
public fun breakBlock(block: BlockT, tool: ItemStackT?, dropsConsumer: DropsConsumer<ItemStackT>): Boolean
}
24 changes: 24 additions & 0 deletions mimic-api/src/main/kotlin/blocks/DropsConsumer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ru.endlesscode.mimic.blocks

import ru.endlesscode.mimic.ExperimentalMimicApi

/**
* Consumes items dropped by a block broken via [BlockRegistry.breakBlock].
* @since 0.9.0
*/
@ExperimentalMimicApi
public fun interface DropsConsumer<ItemStackT : Any> {

/** Consumes the [drops] and returns the items to drop on the ground near the broken block. */
public fun consume(drops: Collection<ItemStackT>): Collection<ItemStackT>

public companion object {
/** Drops all items on the ground. */
@JvmStatic
public fun <ItemStackT : Any> drop(): DropsConsumer<ItemStackT> = DropsConsumer { it }

/** Drops nothing, the items disappear. */
@JvmStatic
public fun <ItemStackT : Any> disappear(): DropsConsumer<ItemStackT> = DropsConsumer { emptyList() }
}
}
7 changes: 7 additions & 0 deletions mimic-bukkit-api/api/mimic-bukkit-api.api
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
public abstract interface class ru/endlesscode/mimic/Mimic {
public static final field Companion Lru/endlesscode/mimic/Mimic$Companion;
public abstract fun getAllBlockRegistries ()Ljava/util/Map;
public abstract fun getAllClassSystemProviders ()Ljava/util/Map;
public abstract fun getAllItemsRegistries ()Ljava/util/Map;
public abstract fun getAllLevelSystemProviders ()Ljava/util/Map;
public abstract fun getAllPlayerInventoryProviders ()Ljava/util/Map;
public abstract fun getBlockRegistry ()Lru/endlesscode/mimic/blocks/BukkitBlockRegistry;
public fun getClassSystem (Lorg/bukkit/entity/Player;)Lru/endlesscode/mimic/classes/BukkitClassSystem;
public abstract fun getClassSystemProvider ()Lru/endlesscode/mimic/classes/BukkitClassSystem$Provider;
public static fun getInstance ()Lru/endlesscode/mimic/Mimic;
Expand All @@ -12,6 +14,8 @@ public abstract interface class ru/endlesscode/mimic/Mimic {
public abstract fun getLevelSystemProvider ()Lru/endlesscode/mimic/level/BukkitLevelSystem$Provider;
public fun getPlayerInventory (Lorg/bukkit/entity/Player;)Lru/endlesscode/mimic/inventory/BukkitPlayerInventory;
public abstract fun getPlayerInventoryProvider ()Lru/endlesscode/mimic/inventory/BukkitPlayerInventory$Provider;
public fun registerBlockRegistry (Lru/endlesscode/mimic/blocks/BukkitBlockRegistry;ILorg/bukkit/plugin/Plugin;)Lru/endlesscode/mimic/blocks/BukkitBlockRegistry;
public abstract fun registerBlockRegistry (Lru/endlesscode/mimic/blocks/BukkitBlockRegistry;ILorg/bukkit/plugin/Plugin;Lorg/bukkit/plugin/ServicePriority;)Lru/endlesscode/mimic/blocks/BukkitBlockRegistry;
public fun registerClassSystem (Lru/endlesscode/mimic/classes/BukkitClassSystem$Provider;ILorg/bukkit/plugin/Plugin;)Lru/endlesscode/mimic/classes/BukkitClassSystem$Provider;
public abstract fun registerClassSystem (Lru/endlesscode/mimic/classes/BukkitClassSystem$Provider;ILorg/bukkit/plugin/Plugin;Lorg/bukkit/plugin/ServicePriority;)Lru/endlesscode/mimic/classes/BukkitClassSystem$Provider;
public fun registerItemsRegistry (Lru/endlesscode/mimic/items/BukkitItemsRegistry;ILorg/bukkit/plugin/Plugin;)Lru/endlesscode/mimic/items/BukkitItemsRegistry;
Expand All @@ -31,6 +35,9 @@ public abstract interface class ru/endlesscode/mimic/PlayerSystemProviderService
public abstract fun getSystem (Lorg/bukkit/entity/Player;)Ljava/lang/Object;
}

public abstract interface class ru/endlesscode/mimic/blocks/BukkitBlockRegistry : ru/endlesscode/mimic/blocks/BlockRegistry {
}

public abstract class ru/endlesscode/mimic/classes/BukkitClassSystem : ru/endlesscode/mimic/classes/ClassSystem {
public fun <init> (Lorg/bukkit/entity/Player;)V
public final fun getPlayer ()Lorg/bukkit/entity/Player;
Expand Down
4 changes: 4 additions & 0 deletions mimic-bukkit-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ dependencies {
compileOnly(libs.annotations)
compileOnly(libs.paperApi)
}

kotlin {
jvmToolchain(21)
}
Loading
Loading