Skip to content

Commit 811bfca

Browse files
authored
Add Platform Support for Minestom (#692)
Co-authored-by: Nico Lube <nico@primecodes.de>
1 parent b114785 commit 811bfca

46 files changed

Lines changed: 2630 additions & 10 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/generate-artifacts.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ jobs:
99
strategy:
1010
fail-fast: false
1111
matrix:
12-
platform: [Bukkit, Paper]
12+
platform: [Bukkit, Paper, Minestom]
1313
runs-on: ubuntu-latest
1414
steps:
15-
- uses: actions/checkout@v2
15+
- uses: actions/checkout@v4
1616
- uses: gradle/wrapper-validation-action@v1
1717
- uses: actions/setup-java@v2
1818
with:
@@ -31,7 +31,7 @@ jobs:
3131
arguments: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}:shadowJar
3232
gradle-version: wrapper
3333
- name: Upload artifacts
34-
uses: actions/upload-artifact@v3
34+
uses: actions/upload-artifact@v4
3535
with:
3636
name: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}-${{ github.sha }}.jar
3737
path: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}/build/libs/*.jar

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.gradle/
22
.idea/
3+
.kotlin/
34
build/
45
libs/

examples/minestom/build.gradle

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
plugins {
3+
alias(libs.plugins.shadowjar)
4+
alias(libs.plugins.kotlin)
5+
id("application")
6+
}
7+
8+
apply from: '../../library.gradle'
9+
10+
dependencies {
11+
implementation projects.inventoryFrameworkPlatformMinestom
12+
implementation libs.minestom
13+
}
14+
15+
shadowJar {
16+
archiveBaseName.set('inventory-framework-example')
17+
archiveAppendix.set('minestome')
18+
}
19+
20+
java {
21+
targetCompatibility = JavaVersion.VERSION_21
22+
toolchain {
23+
languageVersion.set(JavaLanguageVersion.of(21))
24+
}
25+
}
26+
27+
application {
28+
applicationDefaultJvmArgs = ["-Dme.devnatan.inventoryframework.debug=true"]
29+
mainClass = 'me.devnatan.inventoryframework.runtime.SampleServerKt'
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package me.devnatan.inventoryframework.runtime
2+
3+
import net.kyori.adventure.text.Component
4+
import net.minestom.server.item.ItemStack
5+
import net.minestom.server.item.Material
6+
import java.util.Random
7+
8+
object ExampleUtil {
9+
@JvmStatic
10+
fun getRandomItems(amount: Int): List<ItemStack> {
11+
val materials = Material.values().toTypedArray()
12+
val random = Random()
13+
14+
val result = ArrayList<ItemStack>()
15+
16+
for (i in 0 until amount) {
17+
result.add(ItemStack.of(materials[random.nextInt(10, 100)]))
18+
}
19+
20+
return result
21+
}
22+
23+
@JvmStatic
24+
fun displayItem(
25+
material: Material,
26+
displayName: String,
27+
): ItemStack {
28+
return ItemStack.of(material).withCustomName(Component.text(displayName))
29+
}
30+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package me.devnatan.inventoryframework.runtime
2+
3+
import me.devnatan.inventoryframework.ViewFrame
4+
import me.devnatan.inventoryframework.runtime.command.GamemodeCommand
5+
import me.devnatan.inventoryframework.runtime.command.IFExampleCommand
6+
import me.devnatan.inventoryframework.runtime.view.Failing
7+
import me.devnatan.inventoryframework.runtime.view.ScheduledView
8+
import me.devnatan.inventoryframework.runtime.view.SimplePagination
9+
import net.minestom.server.MinecraftServer
10+
import net.minestom.server.coordinate.Pos
11+
import net.minestom.server.entity.PlayerSkin
12+
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent
13+
import net.minestom.server.event.player.PlayerSkinInitEvent
14+
import net.minestom.server.event.player.PlayerSpawnEvent
15+
import net.minestom.server.instance.LightingChunk
16+
import net.minestom.server.instance.block.Block
17+
import net.minestom.server.inventory.TransactionOption
18+
import net.minestom.server.item.ItemStack
19+
import net.minestom.server.item.Material
20+
21+
class SampleServer {
22+
init {
23+
val server = MinecraftServer.init()
24+
val instanceManager = MinecraftServer.getInstanceManager()
25+
26+
// Create word filled with quartz blocks up to height 50
27+
val instance = instanceManager.createInstanceContainer()
28+
instance.setGenerator {
29+
it.modifier().fillHeight(it.absoluteStart().blockY(), 50, Block.QUARTZ_BLOCK)
30+
}
31+
instance.setChunkSupplier(::LightingChunk)
32+
33+
val handler = MinecraftServer.getGlobalEventHandler()
34+
35+
handler.addListener(AsyncPlayerConfigurationEvent::class.java) { event ->
36+
event.spawningInstance = instance
37+
event.player.respawnPoint = Pos(0.0, 53.0, 0.0)
38+
}
39+
handler.addListener(PlayerSkinInitEvent::class.java) { event ->
40+
event.skin = PlayerSkin.fromUsername(event.player.username)
41+
}
42+
handler.addListener(PlayerSpawnEvent::class.java) { event ->
43+
event.player.inventory.addItemStacks(ExampleUtil.getRandomItems(20), TransactionOption.ALL)
44+
event.player.inventory.addItemStack(ItemStack.of(Material.OAK_PLANKS, 64), TransactionOption.ALL)
45+
}
46+
47+
val viewFrame =
48+
ViewFrame.create(handler)
49+
.with(
50+
Failing(),
51+
SimplePagination(),
52+
ScheduledView(),
53+
)
54+
.register()
55+
56+
MinecraftServer.getCommandManager().register(
57+
IFExampleCommand(viewFrame),
58+
GamemodeCommand(viewFrame),
59+
)
60+
61+
server.start("0.0.0.0", 25565)
62+
}
63+
}
64+
65+
fun main() {
66+
SampleServer()
67+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package me.devnatan.inventoryframework.runtime.command
2+
3+
import me.devnatan.inventoryframework.ViewFrame
4+
import net.minestom.server.command.CommandSender
5+
import net.minestom.server.command.builder.Command
6+
import net.minestom.server.command.builder.CommandContext
7+
import net.minestom.server.command.builder.arguments.Argument
8+
import net.minestom.server.command.builder.arguments.ArgumentType
9+
import net.minestom.server.command.builder.suggestion.SuggestionEntry
10+
import net.minestom.server.entity.GameMode
11+
import net.minestom.server.entity.Player
12+
13+
class GamemodeCommand(private val viewFrame: ViewFrame) : Command("gamemode") {
14+
private val gamemodeArg: Argument<GameMode> =
15+
ArgumentType.Enum("gameMode", GameMode::class.java)
16+
.setSuggestionCallback { _, _, suggestion ->
17+
for (gameMode in GameMode.entries) {
18+
suggestion.addEntry(SuggestionEntry(gameMode.name.lowercase()))
19+
}
20+
}
21+
22+
init {
23+
setDefaultExecutor(::onCommand)
24+
addSyntax(::onCommand, gamemodeArg)
25+
}
26+
27+
private fun onCommand(
28+
sender: CommandSender,
29+
ctx: CommandContext,
30+
) {
31+
if (sender !is Player) {
32+
sender.sendMessage("This command can only be executed by players.")
33+
return
34+
}
35+
36+
val gameMode: GameMode = ctx.get(gamemodeArg)
37+
sender.gameMode = gameMode
38+
}
39+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package me.devnatan.inventoryframework.runtime.command
2+
3+
import me.devnatan.inventoryframework.ViewFrame
4+
import me.devnatan.inventoryframework.runtime.view.Failing
5+
import me.devnatan.inventoryframework.runtime.view.ScheduledView
6+
import me.devnatan.inventoryframework.runtime.view.SimplePagination
7+
import net.minestom.server.command.CommandSender
8+
import net.minestom.server.command.builder.Command
9+
import net.minestom.server.command.builder.CommandContext
10+
import net.minestom.server.command.builder.arguments.Argument
11+
import net.minestom.server.command.builder.arguments.ArgumentType
12+
import net.minestom.server.command.builder.suggestion.SuggestionEntry
13+
import net.minestom.server.entity.Player
14+
15+
class IFExampleCommand(private val viewFrame: ViewFrame) : Command("ifexample") {
16+
private val availableViews =
17+
mapOf(
18+
"failing" to Failing::class.java,
19+
"simple-pagination" to SimplePagination::class.java,
20+
"scheduled" to ScheduledView::class.java,
21+
)
22+
23+
private val arg: Argument<String> =
24+
ArgumentType.String("view").setSuggestionCallback { _, _, suggestion ->
25+
availableViews.keys.forEach {
26+
suggestion.addEntry(SuggestionEntry(it))
27+
}
28+
}
29+
30+
init {
31+
addSyntax({ sender, ctx -> onCommand(sender, ctx) }, arg)
32+
}
33+
34+
private fun onCommand(
35+
sender: CommandSender,
36+
ctx: CommandContext,
37+
) {
38+
if (sender !is Player) {
39+
sender.sendMessage("This command can only be executed by players.")
40+
return
41+
}
42+
43+
val view = availableViews[ctx.get(arg)]
44+
if (view != null) {
45+
sender.sendMessage("Opened view: ${ctx.get(arg)}")
46+
try {
47+
viewFrame.open(view, sender)
48+
} catch (e: Exception) {
49+
e.printStackTrace()
50+
}
51+
} else {
52+
sender.sendMessage("Unknown view: ${ctx.get(arg)}")
53+
sender.sendMessage("Available views: ${availableViews.keys.joinToString(", ")}")
54+
}
55+
}
56+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package me.devnatan.inventoryframework.runtime.view
2+
3+
import me.devnatan.inventoryframework.View
4+
import me.devnatan.inventoryframework.ViewConfigBuilder
5+
import me.devnatan.inventoryframework.context.RenderContext
6+
import me.devnatan.inventoryframework.context.SlotClickContext
7+
import me.devnatan.inventoryframework.context.SlotRenderContext
8+
import me.devnatan.inventoryframework.runtime.ExampleUtil.displayItem
9+
import me.devnatan.inventoryframework.state.MutableState
10+
import net.minestom.server.item.Material
11+
12+
class Failing : View() {
13+
var state: MutableState<Int> = mutableState(0)
14+
15+
override fun onInit(config: ViewConfigBuilder): Unit =
16+
with(config) {
17+
size(1)
18+
cancelOnClick()
19+
title("Failing Inventory")
20+
layout(" R C ")
21+
}
22+
23+
override fun onFirstRender(render: RenderContext) {
24+
render.layoutSlot('R')
25+
.onRender { ctx: SlotRenderContext ->
26+
if (state[ctx] == 0) {
27+
ctx.item =
28+
displayItem(
29+
Material.DIAMOND,
30+
"Click me to fail",
31+
)
32+
} else {
33+
throw IllegalStateException("This item cannot be rendered")
34+
}
35+
}
36+
.onClick { ctx: SlotClickContext ->
37+
state[1] = ctx
38+
ctx.update()
39+
}
40+
41+
render.layoutSlot('C', displayItem(Material.STONE, "Click me and I will fail"))
42+
.onClick { _ ->
43+
throw IllegalStateException("This is a failing inventory")
44+
}
45+
}
46+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package me.devnatan.inventoryframework.runtime.view
2+
3+
import me.devnatan.inventoryframework.View
4+
import me.devnatan.inventoryframework.ViewConfigBuilder
5+
import me.devnatan.inventoryframework.context.Context
6+
import me.devnatan.inventoryframework.context.RenderContext
7+
import me.devnatan.inventoryframework.context.SlotClickContext
8+
import me.devnatan.inventoryframework.runtime.ExampleUtil
9+
import net.minestom.server.item.Material
10+
11+
class ScheduledView : View() {
12+
val counter = mutableState(0)
13+
14+
override fun onInit(config: ViewConfigBuilder): Unit =
15+
with(config) {
16+
cancelOnClick()
17+
size(3)
18+
title("Simple Pagination")
19+
layout(
20+
" ",
21+
" C ",
22+
"B ",
23+
)
24+
scheduleUpdate(20)
25+
}
26+
27+
override fun onFirstRender(render: RenderContext) {
28+
render.layoutSlot('C')
29+
.onRender {
30+
it.item = ExampleUtil.displayItem(Material.STONE, counter.increment(it).toString())
31+
}
32+
33+
render.layoutSlot('B', ExampleUtil.displayItem(Material.PAPER, "Back"))
34+
.displayIf(Context::canBack)
35+
.onClick(SlotClickContext::back)
36+
}
37+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package me.devnatan.inventoryframework.runtime.view
2+
3+
import me.devnatan.inventoryframework.View
4+
import me.devnatan.inventoryframework.ViewConfigBuilder
5+
import me.devnatan.inventoryframework.component.MinestomIemComponentBuilder
6+
import me.devnatan.inventoryframework.component.Pagination
7+
import me.devnatan.inventoryframework.context.Context
8+
import me.devnatan.inventoryframework.context.RenderContext
9+
import me.devnatan.inventoryframework.context.SlotClickContext
10+
import me.devnatan.inventoryframework.runtime.ExampleUtil.displayItem
11+
import me.devnatan.inventoryframework.runtime.ExampleUtil.getRandomItems
12+
import me.devnatan.inventoryframework.state.State
13+
import net.minestom.server.item.ItemStack
14+
import net.minestom.server.item.Material
15+
16+
class SimplePagination : View() {
17+
private val state: State<Pagination> =
18+
lazyPaginationState(
19+
{ _ -> getRandomItems(123).toMutableList() },
20+
{ _: Context, builder: MinestomIemComponentBuilder, index: Int, value: ItemStack ->
21+
builder.withItem(value)
22+
builder.onClick { ctx: SlotClickContext ->
23+
ctx.player.sendMessage(
24+
"You clicked on item $index",
25+
)
26+
}
27+
},
28+
)
29+
30+
override fun onInit(config: ViewConfigBuilder): Unit =
31+
with(config) {
32+
cancelOnClick()
33+
size(3)
34+
title("Simple Pagination")
35+
layout(
36+
"OOOOOOOOO",
37+
"OOOOOOOOO",
38+
" P N ",
39+
)
40+
}
41+
42+
override fun onFirstRender(render: RenderContext) {
43+
val previousItem = displayItem(Material.ARROW, "Previous")
44+
val nextItem = displayItem(Material.ARROW, "Next")
45+
render.layoutSlot('P', previousItem)
46+
.displayIf({ ctx -> state[ctx].canBack() })
47+
.updateOnStateChange(state)
48+
.onClick { ctx: SlotClickContext -> state[ctx].back() }
49+
render.layoutSlot('N', nextItem)
50+
.displayIf({ ctx -> state[ctx].canAdvance() })
51+
.updateOnStateChange(state)
52+
.onClick { ctx: SlotClickContext -> state[ctx].advance() }
53+
}
54+
}

0 commit comments

Comments
 (0)