Skip to content

Commit be8789f

Browse files
committed
feat: block-predicate-arguments page, and better tabs behavior
1 parent 3196860 commit be8789f

File tree

6 files changed

+216
-12
lines changed

6 files changed

+216
-12
lines changed

docs/.vitepress/config.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {defineConfig, UserConfig} from 'vitepress';
44
import {withI18n} from 'vitepress-i18n';
55
import {VitePressI18nOptions} from 'vitepress-i18n/dist/types';
66
import {VitePressSidebarOptions, withSidebar} from "vitepress-sidebar";
7-
import {tabsPlugin} from "./theme/tabs/codesMarkdownPlugin";
7+
import {tabsPlugin} from "./theme/tabs/markdownPlugin";
88
import fs from "fs";
99
import {exampleAutoAnchorPreprocessor} from "./theme/anchor/exampleAutoAnchorPreprocessor";
1010

docs/.vitepress/theme/tabs/codesMarkdownPlugin.ts renamed to docs/.vitepress/theme/tabs/markdownPlugin.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@ type Params = {
1010
shareStateKey: string | undefined
1111
}
1212

13-
const parseTabsParams = (): Params => {
14-
return {
15-
shareStateKey: "always"
16-
}
17-
}
18-
1913
export const tabsPlugin = (md: MarkdownIt) => {
2014
md.use(container, 'tabs', {
2115
render(tokens: Token[], index: number) {
16+
let i = index + 1;
17+
let tabsCnt = 0;
18+
while(tokens[i].type !== 'container_tabs_close') {
19+
if (tokens[i].type === 'tab_open') {
20+
tabsCnt++;
21+
}
22+
i++;
23+
if (i >= tokens.length) {
24+
break;
25+
}
26+
}
2227
const token = tokens[index]
2328
if (token.nesting === 1) {
24-
const params = parseTabsParams()
25-
const shareStateKeyProp = params.shareStateKey
26-
? `sharedStateKey="${md.utils.escapeHtml(params.shareStateKey)}"`
27-
: ''
28-
return `<PluginTabs ${shareStateKeyProp}>\n`
29+
return `<PluginTabs sharedStateKey="${tabsCnt}">\n`
2930
} else {
3031
return `</PluginTabs>\n`
3132
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
order: 1
3+
authors:
4+
- misode
5+
- JorelAli
6+
- DerEchtePilz
7+
- willkroboth
8+
---
9+
10+
# Block predicate arguments
11+
12+
The `BlockPredicateArgument` is used to represent a "test" for Minecraft blocks. This can consist of tags, such as the ones listed [here on the MinecraftWiki](https://minecraft.wiki/w/Tag#Blocks), or individual blocks. If a block matches the tag or block, then the predicate is satisfied.
13+
14+
For example, if we were to use the predicate `#leaves`, then the following blocks will be satisfied by that predicate: `jungle_leaves`, `oak_leaves`, `spruce_leaves`, `dark_oak_leaves`, `acacia_leaves`, `birch_leaves`.
15+
16+
When used, this argument must be casted to a `Predicate<Block>`. As with other similar arguments with parameterized types, you can ignore Java's unchecked cast type safety warning.
17+
18+
::::tip Example – Replacing specific blocks in a radius
19+
20+
Say you want to replace blocks in a given radius. To do this, we'll use the following command structure:
21+
22+
```mccmd
23+
/replace <radius> <fromBlock> <toBlock>
24+
```
25+
26+
Of course, we could simply use a `BlockStateArgument` or even an `ItemStackArgument` as our `<fromBlock>` in order to get the material to replace, but the block predicate argument provides a test for a given block, which if satisfied, allows certain code to be executed.
27+
28+
First, we declare our arguments. We want to use the `BlockPredicateArgument` since it also allows us to use Minecraft tags to identify blocks, as well as individual blocks. We then use `BlockStateArgument` to set the block to a given type. The `BlockStateArgument` also allows the user to provide any block data (e.g. contents of a chest or a stair's orientation).
29+
30+
:::tabs
31+
===Java
32+
<<< @/../reference-code/src/main/java/createcommands/arguments/types/predicate/BlockPredicateArguments.java#useBlockStateArgumentsExample
33+
===Kotlin
34+
<<< @/../reference-code/src/main/kotlin/createcommands/arguments/types/predicate/BlockPredicateArguments.kt#useBlockStateArgumentsExample
35+
:::
36+
37+
We then register our `/replace` command. First, we parse the arguments making sure to cast to `Predicate<Block>` and `BlockData` (and not `BlockState`). After that, we use a few simple for loops to find the blocks within a radius sphere from the player.
38+
39+
In our most nested loop, we can then check if the block meets the requirements of our predicate. This is simply performed using `predicate.test(block)`, and if satisfied, we can set the block's type.
40+
41+
Lastly, we register our command as normal using the `register()` method.
42+
43+
:::tabs
44+
===Java
45+
<<< @/../reference-code/src/main/java/createcommands/arguments/types/predicate/BlockPredicateArguments.java#blockPredicateArgumentsExample
46+
===Kotlin
47+
<<< @/../reference-code/src/main/kotlin/createcommands/arguments/types/predicate/BlockPredicateArguments.kt#blockPredicateArgumentsExample
48+
===Kotlin DSL
49+
<<< @/../reference-code/src/main/kotlin/createcommands/arguments/types/predicate/BlockPredicateArguments.kt#blockPredicateArgumentsExampleDSL
50+
:::
51+
52+
::::
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
order: 10
3+
title: Predicate arguments
4+
---
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package createcommands.arguments.types.predicate;
2+
3+
import dev.jorel.commandapi.CommandAPICommand;
4+
import dev.jorel.commandapi.arguments.Argument;
5+
import dev.jorel.commandapi.arguments.BlockPredicateArgument;
6+
import dev.jorel.commandapi.arguments.BlockStateArgument;
7+
import dev.jorel.commandapi.arguments.IntegerArgument;
8+
import org.bukkit.Location;
9+
import org.bukkit.block.Block;
10+
import org.bukkit.block.data.BlockData;
11+
12+
import java.util.function.Predicate;
13+
14+
class BlockPredicateArguments {
15+
{
16+
// #region useBlockStateArgumentsExample
17+
Argument<?>[] arguments = new Argument<?>[] {
18+
new IntegerArgument("radius"),
19+
new BlockPredicateArgument("fromBlock"),
20+
new BlockStateArgument("toBlock"),
21+
};
22+
// #endregion useBlockStateArgumentsExample
23+
24+
// #region blockPredicateArgumentsExample
25+
new CommandAPICommand("replace")
26+
.withArguments(arguments)
27+
.executesPlayer((player, args) -> {
28+
// Parse the arguments
29+
int radius = (int) args.get("radius");
30+
@SuppressWarnings("unchecked")
31+
Predicate<Block> predicate = (Predicate<Block>) args.get("fromBlock");
32+
BlockData blockData = (BlockData) args.get("toBlock");
33+
34+
// Find a (solid) sphere of blocks around the player with a given radius
35+
Location center = player.getLocation();
36+
for (int x = -radius; x <= radius; x++) {
37+
for (int y = -radius; y <= radius; y++) {
38+
for (int z = -radius; z <= radius; z++) {
39+
if (Math.sqrt(((x * x) + (y * y) + (z * z))) <= radius) {
40+
Block block = center.getWorld().getBlockAt(x + center.getBlockX(), y + center.getBlockY(), z + center.getBlockZ());
41+
42+
// If that block matches a block from the predicate, set it
43+
if (predicate.test(block)) {
44+
block.setType(blockData.getMaterial());
45+
block.setBlockData(blockData);
46+
}
47+
}
48+
}
49+
}
50+
}
51+
})
52+
.register();
53+
// #endregion blockPredicateArgumentsExample
54+
}
55+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package createcommands.arguments.types.predicate
2+
3+
import dev.jorel.commandapi.CommandAPICommand
4+
import dev.jorel.commandapi.arguments.Argument
5+
import dev.jorel.commandapi.arguments.BlockPredicateArgument
6+
import dev.jorel.commandapi.arguments.BlockStateArgument
7+
import dev.jorel.commandapi.arguments.IntegerArgument
8+
import dev.jorel.commandapi.executors.PlayerCommandExecutor
9+
import dev.jorel.commandapi.kotlindsl.arguments
10+
import dev.jorel.commandapi.kotlindsl.commandAPICommand
11+
import dev.jorel.commandapi.kotlindsl.playerExecutor
12+
import org.bukkit.block.Block
13+
import org.bukkit.block.data.BlockData
14+
import java.util.function.Predicate
15+
import kotlin.math.sqrt
16+
17+
fun blockPredicateArguments() {
18+
// #region useBlockStateArgumentsExample
19+
val arguments = arrayOf<Argument<*>>(
20+
IntegerArgument("radius"),
21+
BlockPredicateArgument("fromBlock"),
22+
BlockStateArgument("toBlock"),
23+
)
24+
// #endregion useBlockStateArgumentsExample
25+
26+
// #region blockPredicateArgumentsExample
27+
CommandAPICommand("replace")
28+
.withArguments(*arguments)
29+
.executesPlayer(PlayerCommandExecutor { player, args ->
30+
// Parse the arguments
31+
val radius = args["radius"] as Int
32+
val predicate = args["fromBlock"] as Predicate<Block>
33+
val blockData = args["toBlock"] as BlockData
34+
35+
// Find a (solid) sphere of blocks around the player with a given radius
36+
val center = player.location // for (i in 1 until 11) { }
37+
for (x in -radius until radius + 1) {
38+
for (y in -radius until radius + 1) {
39+
for (z in -radius until radius + 1) {
40+
if (sqrt((x * x + y * y + z * z).toDouble()) <= radius) {
41+
val block = center.world.getBlockAt(x + center.blockX, y + center.blockY, z + center.blockZ)
42+
43+
// If that block matches a block from the predicate, set it
44+
if (predicate.test(block)) {
45+
block.type = blockData.material
46+
block.blockData = blockData
47+
}
48+
}
49+
}
50+
}
51+
}
52+
})
53+
.register()
54+
// #endregion blockPredicateArgumentsExample
55+
}
56+
57+
fun blockPredicateArgumentsDSL() {
58+
val arguments = arrayOf<Argument<*>>(
59+
IntegerArgument("radius"),
60+
BlockPredicateArgument("fromBlock"),
61+
BlockStateArgument("toBlock"),
62+
)
63+
// #region blockPredicateArgumentsExampleDSL
64+
commandAPICommand("replace") {
65+
arguments(*arguments)
66+
playerExecutor { player, args ->
67+
// Parse the arguments
68+
val radius = args["radius"] as Int
69+
val predicate = args["fromBlock"] as Predicate<Block>
70+
val blockData = args["toBlock"] as BlockData
71+
72+
// Find a (solid) sphere of blocks around the player with a given radius
73+
val center = player.location // for (i in 1 until 11) { }
74+
for (x in -radius until radius + 1) {
75+
for (y in -radius until radius + 1) {
76+
for (z in -radius until radius + 1) {
77+
if (Math.sqrt((x * x + y * y + z * z).toDouble()) <= radius) {
78+
val block = center.world.getBlockAt(x + center.blockX, y + center.blockY, z + center.blockZ)
79+
80+
// If that block matches a block from the predicate, set it
81+
if (predicate.test(block)) {
82+
block.type = blockData.material
83+
block.blockData = blockData
84+
}
85+
}
86+
}
87+
}
88+
}
89+
}
90+
}
91+
// #endregion blockPredicateArgumentsExampleDSL
92+
}

0 commit comments

Comments
 (0)