Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Platform Support for Minestom #692

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
6 changes: 3 additions & 3 deletions .github/workflows/generate-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [Bukkit, Paper]
platform: [Bukkit, Paper, Minestom]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v2
with:
Expand All @@ -31,7 +31,7 @@ jobs:
arguments: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}:shadowJar
gradle-version: wrapper
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}-${{ github.sha }}.jar
path: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}/build/libs/*.jar
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.gradle/
.idea/
.kotlin/
build/
libs/
30 changes: 30 additions & 0 deletions examples/minestom/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

plugins {
alias(libs.plugins.shadowjar)
alias(libs.plugins.kotlin)
id("application")
}

apply from: '../../library.gradle'

dependencies {
implementation projects.inventoryFrameworkPlatformMinestom
implementation libs.minestom
}

shadowJar {
archiveBaseName.set('inventory-framework-example')
archiveAppendix.set('minestome')
}

java {
targetCompatibility = JavaVersion.VERSION_21
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}

application {
applicationDefaultJvmArgs = ["-Dme.devnatan.inventoryframework.debug=true"]
mainClass = 'me.devnatan.inventoryframework.runtime.SampleServerKt'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package me.devnatan.inventoryframework.runtime

import net.kyori.adventure.text.Component
import net.minestom.server.item.ItemStack
import net.minestom.server.item.Material
import java.util.*


object ExampleUtil {
@JvmStatic
fun getRandomItems(amount: Int): List<ItemStack> {
val materials = Material.values().toTypedArray()
val random = Random()

val result = ArrayList<ItemStack>()

for (i in 0 until amount) {
result.add(ItemStack.of(materials[random.nextInt(10, 100)]))
}

return result
}

@JvmStatic
fun displayItem(material: Material, displayName: String): ItemStack {
return ItemStack.of(material).withCustomName(Component.text(displayName))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package me.devnatan.inventoryframework.runtime

import me.devnatan.inventoryframework.ViewFrame
import me.devnatan.inventoryframework.runtime.command.GamemodeCommand
import me.devnatan.inventoryframework.runtime.command.IFExampleCommand
import me.devnatan.inventoryframework.runtime.view.Failing
import me.devnatan.inventoryframework.runtime.view.ScheduledView
import me.devnatan.inventoryframework.runtime.view.SimplePagination
import net.minestom.server.MinecraftServer
import net.minestom.server.coordinate.Pos
import net.minestom.server.entity.PlayerSkin
import net.minestom.server.event.Event
import net.minestom.server.event.EventFilter
import net.minestom.server.event.EventNode
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent
import net.minestom.server.event.player.PlayerSkinInitEvent
import net.minestom.server.event.player.PlayerSpawnEvent
import net.minestom.server.instance.LightingChunk
import net.minestom.server.instance.block.Block
import net.minestom.server.inventory.TransactionOption
import net.minestom.server.item.ItemStack
import net.minestom.server.item.Material

class SampleServer {

init {
val server = MinecraftServer.init()
val instanceManager = MinecraftServer.getInstanceManager()

// Create word filled with quartz blocks up to height 50
val instance = instanceManager.createInstanceContainer()
instance.setGenerator {
it.modifier().fillHeight(it.absoluteStart().blockY(), 50, Block.QUARTZ_BLOCK)
}
instance.setChunkSupplier(::LightingChunk)

val handler = MinecraftServer.getGlobalEventHandler()

handler.addListener(AsyncPlayerConfigurationEvent::class.java) { event ->
event.spawningInstance = instance
event.player.respawnPoint = Pos(0.0, 53.0, 0.0)
}
handler.addListener(PlayerSkinInitEvent::class.java) { event ->
event.skin = PlayerSkin.fromUsername(event.player.username)
}
handler.addListener(PlayerSpawnEvent::class.java) { event ->
event.player.inventory.addItemStacks(ExampleUtil.getRandomItems(20), TransactionOption.ALL)
event.player.inventory.addItemStack(ItemStack.of(Material.OAK_PLANKS, 64), TransactionOption.ALL)
}

val viewFrame = ViewFrame.create(handler)
.with(
Failing(),
SimplePagination(),
ScheduledView())
.register()

MinecraftServer.getCommandManager().register(
IFExampleCommand(viewFrame),
GamemodeCommand(viewFrame),
)


server.start("0.0.0.0", 25565)
}
}

fun main() {
SampleServer()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package me.devnatan.inventoryframework.runtime.command

import me.devnatan.inventoryframework.ViewFrame
import net.minestom.server.command.CommandSender
import net.minestom.server.command.builder.Command
import net.minestom.server.command.builder.CommandContext
import net.minestom.server.command.builder.arguments.Argument
import net.minestom.server.command.builder.arguments.ArgumentType
import net.minestom.server.command.builder.suggestion.SuggestionEntry
import net.minestom.server.entity.GameMode
import net.minestom.server.entity.Player
import java.util.*

class GamemodeCommand(private val viewFrame: ViewFrame) : Command("gamemode") {

private val gamemodeArg: Argument<GameMode> = ArgumentType.Enum("gameMode", GameMode::class.java)
.setSuggestionCallback { _, _, suggestion ->
for (gameMode in GameMode.entries) {
suggestion.addEntry(SuggestionEntry(gameMode.name.lowercase()))
}
}

init {
setDefaultExecutor(::onCommand)
addSyntax(::onCommand, gamemodeArg)
}


private fun onCommand(sender: CommandSender, ctx: CommandContext) {
if (sender !is Player) {
sender.sendMessage("This command can only be executed by players.")
return
}

val gameMode: GameMode = ctx.get(gamemodeArg)
sender.gameMode = gameMode
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package me.devnatan.inventoryframework.runtime.command

import me.devnatan.inventoryframework.ViewFrame
import me.devnatan.inventoryframework.runtime.view.Failing
import me.devnatan.inventoryframework.runtime.view.ScheduledView
import me.devnatan.inventoryframework.runtime.view.SimplePagination
import net.minestom.server.command.CommandSender
import net.minestom.server.command.builder.Command
import net.minestom.server.command.builder.CommandContext
import net.minestom.server.command.builder.arguments.Argument
import net.minestom.server.command.builder.arguments.ArgumentType
import net.minestom.server.command.builder.suggestion.SuggestionEntry
import net.minestom.server.entity.Player
import java.util.*

class IFExampleCommand(private val viewFrame: ViewFrame) : Command("ifexample") {

private val availableViews = mapOf(
"failing" to Failing::class.java,
"simple-pagination" to SimplePagination::class.java,
"scheduled" to ScheduledView::class.java
);

private val arg: Argument<String> =
ArgumentType.String("view").setSuggestionCallback { _, _, suggestion ->
availableViews.keys.forEach {
suggestion.addEntry(SuggestionEntry(it))
}
}

init {
addSyntax({ sender, ctx -> onCommand(sender, ctx) }, arg)
}

private fun onCommand(sender: CommandSender, ctx: CommandContext) {
if (sender !is Player) {
sender.sendMessage("This command can only be executed by players.")
return
}

val view = availableViews[ctx.get(arg)]
if (view != null) {
sender.sendMessage("Opened view: ${ctx.get(arg)}")
try {
viewFrame.open(view, sender)
} catch (e: Exception) {
e.printStackTrace()
}
} else {
sender.sendMessage("Unknown view: ${ctx.get(arg)}")
sender.sendMessage("Available views: ${availableViews.keys.joinToString(", ")}")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package me.devnatan.inventoryframework.runtime.view

import me.devnatan.inventoryframework.View
import me.devnatan.inventoryframework.ViewConfigBuilder
import me.devnatan.inventoryframework.context.RenderContext
import me.devnatan.inventoryframework.context.SlotClickContext
import me.devnatan.inventoryframework.context.SlotRenderContext
import me.devnatan.inventoryframework.runtime.ExampleUtil.displayItem
import me.devnatan.inventoryframework.state.MutableState
import net.minestom.server.item.Material

class Failing : View() {
var state: MutableState<Int> = mutableState(0)

override fun onInit(config: ViewConfigBuilder) {
config.size(1)
config.cancelOnClick()
config.title("Failing Inventory")
config.layout(" R C ")
}

override fun onFirstRender(render: RenderContext) {
render.layoutSlot('R')
.onRender { ctx: SlotRenderContext ->
if (state[ctx] == 0) {
ctx.item = displayItem(
Material.DIAMOND,
"Click me to fail"
)
} else {
throw IllegalStateException("This item cannot be rendered")
}
}
.onClick { ctx: SlotClickContext ->
state[1] = ctx
ctx.update()
}

render.layoutSlot('C', displayItem(Material.STONE, "Click me and I will fail"))
.onClick { _ ->
throw IllegalStateException("This is a failing inventory")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package me.devnatan.inventoryframework.runtime.view

import me.devnatan.inventoryframework.View
import me.devnatan.inventoryframework.ViewConfigBuilder
import me.devnatan.inventoryframework.context.Context
import me.devnatan.inventoryframework.context.RenderContext
import me.devnatan.inventoryframework.context.SlotClickContext
import me.devnatan.inventoryframework.runtime.ExampleUtil
import net.minestom.server.item.Material
import java.time.Duration

class ScheduledView : View() {

val counter = mutableState(0)

override fun onInit(config: ViewConfigBuilder) {
config.cancelOnClick()
config.size(3)
config.title("Simple Pagination")
config.layout(
" ",
" C ",
"B ")
config.scheduleUpdate(20)
}

override fun onFirstRender(render: RenderContext) {
render.layoutSlot('C')
.onRender {
it.item = ExampleUtil.displayItem(Material.STONE, counter.increment(it).toString())
}

render.layoutSlot('B', ExampleUtil.displayItem(Material.PAPER, "Back"))
.displayIf(Context::canBack)
.onClick(SlotClickContext::back)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package me.devnatan.inventoryframework.runtime.view

import me.devnatan.inventoryframework.View
import me.devnatan.inventoryframework.ViewConfigBuilder
import me.devnatan.inventoryframework.component.MinestomIemComponentBuilder
import me.devnatan.inventoryframework.component.Pagination
import me.devnatan.inventoryframework.component.PaginationValueConsumer
import me.devnatan.inventoryframework.context.Context
import me.devnatan.inventoryframework.context.RenderContext
import me.devnatan.inventoryframework.context.SlotClickContext
import me.devnatan.inventoryframework.runtime.ExampleUtil.displayItem
import me.devnatan.inventoryframework.runtime.ExampleUtil.getRandomItems
import me.devnatan.inventoryframework.state.State
import net.minestom.server.item.ItemStack
import net.minestom.server.item.Material
import java.util.function.BooleanSupplier
import java.util.function.Supplier

class SimplePagination : View() {
private val state: State<Pagination> = lazyPaginationState(
{_ -> getRandomItems(123).toMutableList() },
{ _: Context, builder: MinestomIemComponentBuilder, index: Int, value: ItemStack ->
builder.withItem(value)
builder.onClick { ctx: SlotClickContext ->
ctx.player.sendMessage(
"You clicked on item $index"
)
}
})

override fun onInit(config: ViewConfigBuilder) {
config.cancelOnClick()
config.size(3)
config.title("Simple Pagination")
config.layout("OOOOOOOOO", "OOOOOOOOO", " P N ")
}

override fun onFirstRender(render: RenderContext) {
val previousItem = displayItem(Material.ARROW, "Previous")
val nextItem = displayItem(Material.ARROW, "Next")
render.layoutSlot('P', previousItem)
.displayIf({ctx -> state[ctx].canBack() })
.updateOnStateChange(state)
.onClick { ctx: SlotClickContext -> state[ctx].back() }
render.layoutSlot('N', nextItem)
.displayIf({ctx -> state[ctx].canAdvance()})
.updateOnStateChange(state)
.onClick { ctx: SlotClickContext -> state[ctx].advance() }
}
}
File renamed without changes.
Loading
Loading