This repository has been archived by the owner on Sep 2, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
161 additions
and
2 deletions.
There are no files selected for viewing
120 changes: 120 additions & 0 deletions
120
src/main/java/io/github/homchom/recode/game/SlotConversions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 39 additions & 1 deletion
40
src/main/java/io/github/homchom/recode/multiplayer/PlayerExtensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} |