Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
slot conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
homchom committed Jun 24, 2024
1 parent 3407ad7 commit aed9e6f
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 2 deletions.
120 changes: 120 additions & 0 deletions src/main/java/io/github/homchom/recode/game/SlotConversions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.github.homchom.recode.game

import io.github.homchom.recode.util.InvokableWrapper

/**
* An index of a [net.minecraft.world.Container] slot, ordered by [order]. The order is stored in type
* parameter @T and can be changed, allowing for safe slot conversions.
*
* @see SlotOrder
*/
data class SlotIndex<out T : SlotOrder>(
override val value: Int,
private val order: T
) : InvokableWrapper<Int> {
/**
* Converts the index to one in order [order].
*/
fun <S : SlotOrder> reorder(order: S) = converted(value, this.order, order)

companion object {
/**
* @return A [SlotIndex] with order [targetOrder] and an index equivalent to [value] in [sourceOrder].
*/
fun <S : SlotOrder> converted(value: Int, sourceOrder: SlotOrder, targetOrder: S): SlotIndex<S> {
val (order, subIndex) = sourceOrder.getOrNull(value)
?: throw IndexOutOfBoundsException("Slot order $sourceOrder does not have index $value")

val newIndex = targetOrder.indexOf(order, subIndex)
require(newIndex != -1) {
"Slot order $targetOrder does not have an index equivalent to $value in $sourceOrder"
}

return SlotIndex(newIndex, targetOrder)
}
}
}

/**
* A recursive total ordering of [net.minecraft.world.Container] indices with identity.
*
* @see SlotIndex
*/
sealed interface SlotOrder { // TODO: add unit tests
data object Hotbar : SlotOrder by slots(9)
data object UpperInventory : SlotOrder by slots(27)
data object Armor : SlotOrder by slots(4)
data object Offhand : SlotOrder by slots(1)
data object QuickCraft : SlotOrder by slots(5)

data object InventoryItems : SlotOrder by slots(Hotbar, UpperInventory, Armor.reversed, Offhand)
data object InventoryMenu : SlotOrder by slots(QuickCraft, Armor, UpperInventory, Hotbar, Offhand)

/**
* The identity of the order, used for equality checks.
*/
val id get() = this

/**
* The number of slots present in this order.
*/
val size: Int

/**
* @return The base [SlotOrder] found at [index], paired with the sub-index of [index] in it. Returns
* `null` if [index] is out of bounds.
*/
fun getOrNull(index: Int): Pair<SlotOrder, Int>?

/**
* @return The index of the given nested [order]'s [subIndex] in this order.
*/
fun indexOf(order: SlotOrder, subIndex: Int): Int
}

private fun slots(size: Int) = SlotRange(size)
private fun slots(vararg compartments: SlotOrder) = SlotSequence(*compartments)
private val SlotOrder.reversed get() = ReversedSlotOrder(this)

private class SlotRange(override val size: Int) : SlotOrder {
override fun getOrNull(index: Int) =
if (index in 0..<size) id to index else null

override fun indexOf(order: SlotOrder, subIndex: Int) =
if (id == order.id) subIndex else -1
}

private class SlotSequence(vararg compartments: SlotOrder) : SlotOrder {
override val size = compartments.sumOf(SlotOrder::size)

private val compartments = compartments.toList()

override fun getOrNull(index: Int): Pair<SlotOrder, Int>? {
var mutIndex = index
for (compartment in compartments) {
compartment.getOrNull(mutIndex)?.let { return it }
mutIndex -= compartment.size
}
return null
}

override fun indexOf(order: SlotOrder, subIndex: Int): Int {
var index = 0
for (compartment in compartments) {
val nestedIndex = compartment.indexOf(order, subIndex)
if (nestedIndex != -1) return index + nestedIndex
index += compartment.size
}
return -1
}
}

private class ReversedSlotOrder(private val delegate: SlotOrder) : SlotOrder {
override val size get() = delegate.size

override fun getOrNull(index: Int) =
delegate.getOrNull(size - 1 - index)

override fun indexOf(order: SlotOrder, subIndex: Int) =
delegate.indexOf(order, order.size - 1 - subIndex)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.homchom.recode.hypercube.state
class PermissionGroup(ranks: Iterable<Rank>) {
val ranks = ranks.toSet()

inline operator fun <reified P> contains(rankPermission: P) where P : Rank, P : Comparable<P> =
inline operator fun <reified P> contains(rankPermission: P)
where P : Rank, P : Comparable<P> =
ranks.any { it is P && it >= rankPermission }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
package io.github.homchom.recode.multiplayer

import io.github.homchom.recode.game.SlotIndex
import io.github.homchom.recode.game.SlotOrder
import io.github.homchom.recode.mc
import net.minecraft.client.player.LocalPlayer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack

val Player.username: String get() = gameProfile.name
val Player.username: String get() = gameProfile.name

/**
* @return A [Sequence] of this [Inventory]'s items in the order specified by [SlotOrder.InventoryItems].
*/
fun Inventory.asSequence() = sequenceOf(items, armor, offhand).flatten()

/**
* Sets this player's creative [Inventory] [slot] to [stack] and synchronizes it with the server.
*
* @throws IllegalStateException if the player is not in creative mode.
*/
fun LocalPlayer.setItemAndSync(slot: SlotIndex<SlotOrder.InventoryItems>, stack: ItemStack) =
setItemAndSync(slot, slot.reorder(SlotOrder.InventoryMenu), stack)

/**
* Sets this player's creative [Inventory] [slot] to [stack] and synchronizes it with the server.
*
* @throws IllegalStateException if the player is not in creative mode.
*/
@JvmName("setItemAndSyncMenu")
fun LocalPlayer.setItemAndSync(slot: SlotIndex<SlotOrder.InventoryMenu>, stack: ItemStack) =
setItemAndSync(slot.reorder(SlotOrder.InventoryItems), slot, stack)

private fun LocalPlayer.setItemAndSync(
itemsSlot: SlotIndex<SlotOrder.InventoryItems>,
menuSlot: SlotIndex<SlotOrder.InventoryMenu>,
stack: ItemStack
) {
check(isCreative) { "Inventory items can only be set by the client in creative mode" }
inventory.setItem(itemsSlot(), stack)
mc.gameMode?.handleCreativeModeItemAdd(stack, menuSlot())
}

0 comments on commit aed9e6f

Please sign in to comment.