Add Crafting Terminal
This commit is contained in:
parent
e4662b0f6f
commit
9d98481ba5
|
@ -26,9 +26,10 @@ object PhysicalConnectivity: ModInitializer {
|
|||
PhyItems.init()
|
||||
PhyScreens.init()
|
||||
|
||||
registerGlobalReceiver(C2SConfigureDevice)
|
||||
registerGlobalReceiver(C2STerminalCraftingButton)
|
||||
registerGlobalReceiver(C2STerminalRequestItem)
|
||||
registerGlobalReceiver(C2STerminalUpdateDisplayedItems)
|
||||
registerGlobalReceiver(C2SConfigureDevice)
|
||||
|
||||
for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) {
|
||||
it.initializePhyCon(PhyConAPIImpl)
|
||||
|
|
|
@ -8,6 +8,7 @@ import net.fabricmc.fabric.api.renderer.v1.RendererAccess
|
|||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial
|
||||
import net.shadowfacts.phycon.block.inserter.InserterScreen
|
||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalScreen
|
||||
import net.shadowfacts.phycon.init.PhyScreens
|
||||
import net.shadowfacts.phycon.block.terminal.TerminalScreen
|
||||
import net.shadowfacts.phycon.client.PhyExtendedModelProvider
|
||||
|
@ -40,6 +41,7 @@ object PhysicalConnectivityClient: ClientModInitializer {
|
|||
}
|
||||
|
||||
ScreenRegistry.register(PhyScreens.TERMINAL, ::TerminalScreen)
|
||||
ScreenRegistry.register(PhyScreens.CRAFTING_TERMINAL, ::CraftingTerminalScreen)
|
||||
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
|
||||
ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>): DeviceBloc
|
|||
protected val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>()
|
||||
val internalBuffer = TerminalBufferInventory(18)
|
||||
|
||||
private val pendingRequests = LinkedList<StackLocateRequest>()
|
||||
protected val pendingRequests = LinkedList<StackLocateRequest>()
|
||||
override val pendingInsertions = mutableListOf<PendingInsertion>()
|
||||
override val dispatchStackTimeout = INSERTION_TIMEOUT
|
||||
|
||||
|
@ -190,7 +190,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>): DeviceBloc
|
|||
sendPacket(LocateStackPacket(stack, ipAddress))
|
||||
}
|
||||
|
||||
private fun stackLocateRequestCompleted(request: StackLocateRequest) {
|
||||
protected open fun stackLocateRequestCompleted(request: StackLocateRequest) {
|
||||
pendingRequests.remove(request)
|
||||
|
||||
val sortedResults = request.results.toMutableList()
|
||||
|
@ -252,19 +252,20 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>): DeviceBloc
|
|||
var bufferSlot by Delegates.notNull<Int>()
|
||||
}
|
||||
|
||||
data class StackLocateRequest(
|
||||
open class StackLocateRequest(
|
||||
val stack: ItemStack,
|
||||
val amount: Int,
|
||||
val timestamp: Long,
|
||||
var results: MutableSet<Pair<Int, NetworkStackProvider>> = mutableSetOf()
|
||||
) {
|
||||
var results: MutableSet<Pair<Int, NetworkStackProvider>> = mutableSetOf()
|
||||
|
||||
val totalResultAmount: Int
|
||||
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
||||
|
||||
fun isFinishable(currentTimestamp: Long): Boolean {
|
||||
// we can't check totalResultAmount >= amount because we need to hear back from all network stack providers to
|
||||
// correctly sort by priority
|
||||
return currentTimestamp - timestamp >= AbstractTerminalBlockEntity.LOCATE_REQUEST_TIMEOUT
|
||||
return currentTimestamp - timestamp >= LOCATE_REQUEST_TIMEOUT
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,8 @@ abstract class AbstractTerminalScreenHandler<T: AbstractTerminalBlockEntity>(
|
|||
var itemsForDisplay = listOf<ItemStack>()
|
||||
private set
|
||||
|
||||
open val xOffset: Int = 0
|
||||
|
||||
init {
|
||||
if (!terminal.world!!.isClient) {
|
||||
assert(terminal.netItemObserver?.get() === null)
|
||||
|
@ -63,29 +65,31 @@ abstract class AbstractTerminalScreenHandler<T: AbstractTerminalBlockEntity>(
|
|||
// intentionally don't call netItemsChanged immediately, we need to wait for the client to send us its settings
|
||||
}
|
||||
|
||||
val xOffset = xOffset
|
||||
|
||||
// network
|
||||
for (y in 0 until 6) {
|
||||
for (x in 0 until 9) {
|
||||
addSlot(TerminalFakeSlot(fakeInv, y * 9 + x, 66 + x * 18, 18 + y * 18))
|
||||
addSlot(TerminalFakeSlot(fakeInv, y * 9 + x, xOffset + 66 + x * 18, 18 + y * 18))
|
||||
}
|
||||
}
|
||||
|
||||
// internal buffer
|
||||
for (y in 0 until 6) {
|
||||
for (x in 0 until 3) {
|
||||
addSlot(Slot(terminal.internalBuffer, y * 3 + x, 8 + x * 18, 18 + y * 18))
|
||||
addSlot(Slot(terminal.internalBuffer, y * 3 + x, xOffset + 8 + x * 18, 18 + y * 18))
|
||||
}
|
||||
}
|
||||
|
||||
// player inv
|
||||
for (y in 0 until 3) {
|
||||
for (x in 0 until 9) {
|
||||
addSlot(Slot(playerInv, x + y * 9 + 9, 66 + x * 18, 140 + y * 18))
|
||||
addSlot(Slot(playerInv, x + y * 9 + 9, xOffset + 66 + x * 18, 140 + y * 18))
|
||||
}
|
||||
}
|
||||
// hotbar
|
||||
for (x in 0 until 9) {
|
||||
addSlot(Slot(playerInv, x, 66 + x * 18, 198))
|
||||
addSlot(Slot(playerInv, x, xOffset + 66 + x * 18, 198))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ abstract class AbstractTerminalViewController<BE: AbstractTerminalBlockEntity, S
|
|||
|
||||
buffer = view.addLayoutGuide()
|
||||
view.solver.dsl {
|
||||
buffer.leftAnchor equalTo (pane.leftAnchor + 7)
|
||||
buffer.leftAnchor equalTo (pane.leftAnchor + 7 + handler.xOffset)
|
||||
buffer.topAnchor equalTo (pane.topAnchor + 17)
|
||||
buffer.widthAnchor equalTo (18 * 3)
|
||||
buffer.heightAnchor equalTo (18 * 6)
|
||||
|
@ -72,7 +72,7 @@ abstract class AbstractTerminalViewController<BE: AbstractTerminalBlockEntity, S
|
|||
|
||||
network = view.addLayoutGuide()
|
||||
view.solver.dsl {
|
||||
network.leftAnchor equalTo (pane.leftAnchor + 65)
|
||||
network.leftAnchor equalTo (pane.leftAnchor + 65 + handler.xOffset)
|
||||
network.topAnchor equalTo buffer.topAnchor
|
||||
network.widthAnchor equalTo (18 * 9)
|
||||
network.heightAnchor equalTo (18 * 6)
|
||||
|
@ -122,12 +122,12 @@ abstract class AbstractTerminalViewController<BE: AbstractTerminalBlockEntity, S
|
|||
playerInvLabel.leftAnchor equalTo playerInv.leftAnchor
|
||||
playerInvLabel.topAnchor equalTo (pane.topAnchor + 128)
|
||||
|
||||
searchField.leftAnchor equalTo (pane.leftAnchor + 138)
|
||||
searchField.leftAnchor equalTo (pane.leftAnchor + 138 + handler.xOffset)
|
||||
searchField.topAnchor equalTo (pane.topAnchor + 5)
|
||||
searchField.widthAnchor equalTo 80
|
||||
searchField.heightAnchor equalTo 9
|
||||
|
||||
scrollTrack.leftAnchor equalTo (pane.leftAnchor + 232)
|
||||
scrollTrack.leftAnchor equalTo (pane.leftAnchor + 232 + handler.xOffset)
|
||||
scrollTrack.topAnchor equalTo (network.topAnchor + 1)
|
||||
scrollTrack.bottomAnchor equalTo (network.bottomAnchor - 1)
|
||||
scrollTrack.widthAnchor equalTo 12
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package net.shadowfacts.phycon.block.terminal
|
||||
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.world.BlockView
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class CraftingTerminalBlock: AbstractTerminalBlock<CraftingTerminalBlockEntity>() {
|
||||
|
||||
companion object {
|
||||
val ID = Identifier(PhysicalConnectivity.MODID, "crafting_terminal")
|
||||
}
|
||||
|
||||
override fun createBlockEntity(world: BlockView) = CraftingTerminalBlockEntity()
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package net.shadowfacts.phycon.block.terminal
|
||||
|
||||
import alexiil.mc.lib.attributes.item.ItemStackCollections
|
||||
import alexiil.mc.lib.attributes.item.ItemStackUtil
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap
|
||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.CraftingInventory
|
||||
import net.minecraft.inventory.SimpleInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.text.TranslatableText
|
||||
import net.shadowfacts.phycon.init.PhyBlockEntities
|
||||
import net.shadowfacts.phycon.packet.ItemStackPacket
|
||||
import net.shadowfacts.phycon.packet.LocateStackPacket
|
||||
import net.shadowfacts.phycon.packet.RequestInventoryPacket
|
||||
import net.shadowfacts.phycon.util.fromTag
|
||||
import net.shadowfacts.phycon.util.toTag
|
||||
import java.util.LinkedList
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class CraftingTerminalBlockEntity: AbstractTerminalBlockEntity(PhyBlockEntities.CRAFTING_TERMINAL) {
|
||||
|
||||
val craftingInv = SimpleInventory(9)
|
||||
|
||||
private val completedCraftingStackRequests = LinkedList<CraftingStackLocateRequest>()
|
||||
|
||||
override fun onActivate(player: PlayerEntity) {
|
||||
if (!world!!.isClient) {
|
||||
updateAndSync()
|
||||
|
||||
inventoryCache.clear()
|
||||
sendPacket(RequestInventoryPacket(ipAddress))
|
||||
val factory = object: ExtendedScreenHandlerFactory {
|
||||
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? {
|
||||
return CraftingTerminalScreenHandler(syncId, playerInv, this@CraftingTerminalBlockEntity)
|
||||
}
|
||||
|
||||
override fun getDisplayName() = TranslatableText("block.phycon.crafting_terminal")
|
||||
|
||||
override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {
|
||||
buf.writeBlockPos(this@CraftingTerminalBlockEntity.pos)
|
||||
}
|
||||
}
|
||||
player.openHandledScreen(factory)
|
||||
}
|
||||
}
|
||||
|
||||
fun requestItemsForCrafting(maxAmount: Int) {
|
||||
val amounts = ItemStackCollections.map<IntArray>()
|
||||
|
||||
for (i in 0 until craftingInv.size()) {
|
||||
val craftingInvStack = craftingInv.getStack(i)
|
||||
if (craftingInvStack.isEmpty) continue
|
||||
if (craftingInvStack.count >= craftingInvStack.maxCount) continue
|
||||
|
||||
if (craftingInvStack !in amounts) amounts[craftingInvStack] = IntArray(9) { 0 }
|
||||
amounts[craftingInvStack]!![i] = min(maxAmount, craftingInvStack.maxCount - craftingInvStack.count)
|
||||
}
|
||||
|
||||
for ((stack, amountPerSlot) in amounts) {
|
||||
val total = amountPerSlot.sum()
|
||||
val request = CraftingStackLocateRequest(stack, total, counter, amountPerSlot)
|
||||
pendingRequests.add(request)
|
||||
sendPacket(LocateStackPacket(stack, ipAddress))
|
||||
}
|
||||
}
|
||||
|
||||
override fun stackLocateRequestCompleted(request: StackLocateRequest) {
|
||||
if (request is CraftingStackLocateRequest) {
|
||||
completedCraftingStackRequests.add(request)
|
||||
}
|
||||
|
||||
super.stackLocateRequestCompleted(request)
|
||||
}
|
||||
|
||||
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
|
||||
val craftingReq = completedCraftingStackRequests.find { ItemStackUtil.areEqualIgnoreAmounts(it.stack, packet.stack) }
|
||||
if (craftingReq != null) {
|
||||
var remaining = packet.stack.copy()
|
||||
|
||||
for (i in 0 until craftingInv.size()) {
|
||||
val currentStack = craftingInv.getStack(i)
|
||||
if (currentStack.count >= currentStack.maxCount) continue
|
||||
if (!ItemStackUtil.areEqualIgnoreAmounts(currentStack, remaining)) continue
|
||||
|
||||
val toInsert = minOf(remaining.count, currentStack.maxCount - currentStack.count, craftingReq.amountPerSlot[i])
|
||||
currentStack.count += toInsert
|
||||
remaining.count -= toInsert
|
||||
craftingReq.amountPerSlot[i] -= toInsert
|
||||
craftingReq.received += toInsert
|
||||
|
||||
if (remaining.isEmpty) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (craftingReq.amountPerSlot.sum() == 0 || craftingReq.received >= craftingReq.totalResultAmount) {
|
||||
completedCraftingStackRequests.remove(craftingReq)
|
||||
}
|
||||
|
||||
if (!remaining.isEmpty) {
|
||||
remaining = internalBuffer.insert(remaining, TerminalBufferInventory.Mode.FROM_NETWORK)
|
||||
}
|
||||
|
||||
updateAndSync()
|
||||
|
||||
return remaining
|
||||
} else {
|
||||
return super.doHandleItemStack(packet)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toCommonTag(tag: CompoundTag) {
|
||||
super.toCommonTag(tag)
|
||||
tag.put("CraftingInv", craftingInv.toTag())
|
||||
}
|
||||
|
||||
override fun fromCommonTag(tag: CompoundTag) {
|
||||
super.fromCommonTag(tag)
|
||||
craftingInv.fromTag(tag.getList("CraftingInv", 10))
|
||||
}
|
||||
|
||||
class CraftingStackLocateRequest(
|
||||
stack: ItemStack,
|
||||
amount: Int,
|
||||
timestamp: Long,
|
||||
val amountPerSlot: IntArray,
|
||||
): StackLocateRequest(stack, amount, timestamp) {
|
||||
var received: Int = 0
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package net.shadowfacts.phycon.block.terminal
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class CraftingTerminalScreen(
|
||||
handler: CraftingTerminalScreenHandler,
|
||||
playerInv: PlayerInventory,
|
||||
title: Text,
|
||||
): AbstractTerminalScreen<CraftingTerminalBlockEntity, CraftingTerminalScreenHandler>(
|
||||
handler,
|
||||
playerInv,
|
||||
title,
|
||||
259,
|
||||
252,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val BACKGROUND_1 = Identifier(PhysicalConnectivity.MODID, "textures/gui/crafting_terminal_1.png")
|
||||
private val BACKGROUND_2 = Identifier(PhysicalConnectivity.MODID, "textures/gui/crafting_terminal_2.png")
|
||||
}
|
||||
|
||||
override val backgroundTexture = BACKGROUND_1
|
||||
|
||||
override fun createViewController(): AbstractTerminalViewController<*, *, *> {
|
||||
return CraftingTerminalViewController(this, handler)
|
||||
}
|
||||
|
||||
override fun drawBackgroundTexture(matrixStack: MatrixStack) {
|
||||
RenderSystem.color4f(1f, 1f, 1f, 1f)
|
||||
client!!.textureManager.bindTexture(BACKGROUND_1)
|
||||
val x = (width - backgroundWidth) / 2
|
||||
val y = (height - backgroundHeight) / 2
|
||||
drawTexture(matrixStack, x, y, 0, 0, 256, 252)
|
||||
|
||||
client!!.textureManager.bindTexture(BACKGROUND_2)
|
||||
drawTexture(matrixStack, x + 256, y, 0, 0, 3, 252)
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package net.shadowfacts.phycon.block.terminal
|
||||
|
||||
import alexiil.mc.lib.attributes.item.ItemStackCollections
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.CraftingInventory
|
||||
import net.minecraft.inventory.CraftingResultInventory
|
||||
import net.minecraft.inventory.Inventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket
|
||||
import net.minecraft.recipe.RecipeFinder
|
||||
import net.minecraft.recipe.RecipeType
|
||||
import net.minecraft.screen.slot.CraftingResultSlot
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.shadowfacts.phycon.init.PhyBlocks
|
||||
import net.shadowfacts.phycon.init.PhyScreens
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class CraftingTerminalScreenHandler(
|
||||
syncId: Int,
|
||||
playerInv: PlayerInventory,
|
||||
terminal: CraftingTerminalBlockEntity,
|
||||
): AbstractTerminalScreenHandler<CraftingTerminalBlockEntity>(PhyScreens.CRAFTING_TERMINAL, syncId, playerInv, terminal) {
|
||||
|
||||
val craftingInv = CraftingInv(this)
|
||||
val result = CraftingResultInventory()
|
||||
val resultSlot: CraftingResultSlot
|
||||
|
||||
override val xOffset: Int
|
||||
get() = 5
|
||||
|
||||
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
|
||||
this(
|
||||
syncId,
|
||||
playerInv,
|
||||
PhyBlocks.CRAFTING_TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!
|
||||
)
|
||||
|
||||
init {
|
||||
for (y in 0 until 3) {
|
||||
for (x in 0 until 3) {
|
||||
this.addSlot(Slot(craftingInv, x + y * 3, 13 + x * 18, 140 + y * 18))
|
||||
}
|
||||
}
|
||||
|
||||
resultSlot = CraftingResultSlot(playerInv.player, craftingInv, result, 0, 31, 224)
|
||||
addSlot(resultSlot)
|
||||
|
||||
updateCraftingResult()
|
||||
}
|
||||
|
||||
override fun onContentChanged(inventory: Inventory?) {
|
||||
updateCraftingResult()
|
||||
}
|
||||
|
||||
private fun updateCraftingResult() {
|
||||
val world = playerInv.player.world
|
||||
if (!world.isClient) {
|
||||
val player = playerInv.player as ServerPlayerEntity
|
||||
val recipe = world.server!!.recipeManager.getFirstMatch(RecipeType.CRAFTING, craftingInv, world)
|
||||
val resultStack =
|
||||
if (recipe.isPresent && result.shouldCraftRecipe(world, player, recipe.get())) {
|
||||
recipe.get().craft(craftingInv)
|
||||
} else {
|
||||
ItemStack.EMPTY
|
||||
}
|
||||
result.setStack(0, resultStack)
|
||||
player.networkHandler.sendPacket(ScreenHandlerSlotUpdateS2CPacket(syncId, resultSlot.id, resultStack))
|
||||
}
|
||||
}
|
||||
|
||||
fun clearCraftingGrid() {
|
||||
assert(!playerInv.player.world.isClient)
|
||||
for (i in 0 until terminal.craftingInv.size()) {
|
||||
val craftingInvStack = terminal.craftingInv.getStack(i)
|
||||
if (craftingInvStack.isEmpty) continue
|
||||
val remainder = terminal.internalBuffer.insert(craftingInvStack, TerminalBufferInventory.Mode.TO_NETWORK)
|
||||
terminal.craftingInv.setStack(i, remainder)
|
||||
}
|
||||
updateCraftingResult()
|
||||
sendContentUpdates()
|
||||
}
|
||||
|
||||
fun requestMoreCraftingIngredients(maxAmount: Int) {
|
||||
assert(!playerInv.player.world.isClient)
|
||||
terminal.requestItemsForCrafting(maxAmount)
|
||||
}
|
||||
|
||||
// RecipeType.CRAFTING wants a CraftingInventory, but we can't store a CraftingInventory on the BE without a screen handler, so...
|
||||
class CraftingInv(val handler: CraftingTerminalScreenHandler): CraftingInventory(handler, 3, 3) {
|
||||
private val backing = handler.terminal.craftingInv
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return backing.isEmpty
|
||||
}
|
||||
|
||||
override fun getStack(i: Int): ItemStack {
|
||||
return backing.getStack(i)
|
||||
}
|
||||
|
||||
override fun removeStack(i: Int): ItemStack {
|
||||
return backing.removeStack(i)
|
||||
}
|
||||
|
||||
override fun removeStack(i: Int, j: Int): ItemStack {
|
||||
val res = backing.removeStack(i, j)
|
||||
if (!res.isEmpty) {
|
||||
handler.onContentChanged(this)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
override fun setStack(i: Int, itemStack: ItemStack?) {
|
||||
backing.setStack(i, itemStack)
|
||||
handler.onContentChanged(this)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
backing.clear()
|
||||
}
|
||||
|
||||
override fun provideRecipeInputs(finder: RecipeFinder) {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package net.shadowfacts.phycon.block.terminal
|
||||
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.util.InputUtil
|
||||
import net.minecraft.text.TranslatableText
|
||||
import net.minecraft.util.Identifier
|
||||
import net.shadowfacts.cacao.geometry.Size
|
||||
import net.shadowfacts.cacao.util.Color
|
||||
import net.shadowfacts.cacao.util.LayoutGuide
|
||||
import net.shadowfacts.cacao.util.texture.Texture
|
||||
import net.shadowfacts.cacao.view.Label
|
||||
import net.shadowfacts.cacao.view.TextureView
|
||||
import net.shadowfacts.cacao.view.button.Button
|
||||
import net.shadowfacts.kiwidsl.dsl
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.networking.C2STerminalCraftingButton
|
||||
import org.lwjgl.glfw.GLFW
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class CraftingTerminalViewController(
|
||||
screen: CraftingTerminalScreen,
|
||||
handler: CraftingTerminalScreenHandler,
|
||||
): AbstractTerminalViewController<CraftingTerminalBlockEntity, CraftingTerminalScreen, CraftingTerminalScreenHandler>(
|
||||
screen,
|
||||
handler,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val SMALL_BUTTON = Texture(Identifier(PhysicalConnectivity.MODID, "textures/gui/icons.png"), 0, 48)
|
||||
val SMALL_BUTTON_HOVERED = Texture(Identifier(PhysicalConnectivity.MODID, "textures/gui/icons.png"), 16, 48)
|
||||
val CLEAR_ICON = Texture(Identifier(PhysicalConnectivity.MODID, "textures/gui/icons.png"), 32, 48)
|
||||
val PLUS_ICON = Texture(Identifier(PhysicalConnectivity.MODID, "textures/gui/icons.png"), 48, 48)
|
||||
}
|
||||
|
||||
lateinit var craftingInv: LayoutGuide
|
||||
|
||||
override fun viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
craftingInv = view.addLayoutGuide()
|
||||
view.solver.dsl {
|
||||
craftingInv.leftAnchor equalTo buffer.leftAnchor
|
||||
craftingInv.topAnchor equalTo playerInv.topAnchor
|
||||
craftingInv.widthAnchor equalTo buffer.widthAnchor
|
||||
craftingInv.heightAnchor equalTo 54
|
||||
}
|
||||
|
||||
val craftingLabel = view.addSubview(Label(TranslatableText("gui.phycon.terminal_crafting"))).apply {
|
||||
textColor = Color.TEXT
|
||||
}
|
||||
view.solver.dsl {
|
||||
craftingLabel.leftAnchor equalTo craftingInv.leftAnchor
|
||||
craftingLabel.topAnchor equalTo playerInvLabel.topAnchor
|
||||
}
|
||||
|
||||
val clearIcon = TextureView(CLEAR_ICON).apply {
|
||||
intrinsicContentSize = Size(3.0,3.0)
|
||||
}
|
||||
val clearButton = view.addSubview(Button(clearIcon, padding = 2.0, handler = ::clearPressed)).apply {
|
||||
background = TextureView(SMALL_BUTTON)
|
||||
hoveredBackground = TextureView(SMALL_BUTTON_HOVERED)
|
||||
tooltip = TranslatableText("gui.phycon.terminal.clear_crafting")
|
||||
}
|
||||
view.solver.dsl {
|
||||
clearButton.topAnchor equalTo craftingInv.topAnchor
|
||||
clearButton.leftAnchor equalTo (pane.leftAnchor + 4)
|
||||
}
|
||||
|
||||
val plusIcon = TextureView(PLUS_ICON).apply {
|
||||
intrinsicContentSize = Size(3.0, 3.0)
|
||||
}
|
||||
val plusButton = view.addSubview(Button(plusIcon, padding = 2.0, handler = ::plusPressed)).apply {
|
||||
background= TextureView(SMALL_BUTTON)
|
||||
hoveredBackground = TextureView(SMALL_BUTTON_HOVERED)
|
||||
tooltip = TranslatableText("gui.phycon.terminal.more_crafting")
|
||||
}
|
||||
view.solver.dsl {
|
||||
plusButton.topAnchor equalTo (clearButton.bottomAnchor + 2)
|
||||
plusButton.leftAnchor equalTo clearButton.leftAnchor
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearPressed(button: Button) {
|
||||
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2STerminalCraftingButton(terminal, C2STerminalCraftingButton.Action.CLEAR_GRID))
|
||||
}
|
||||
|
||||
private fun plusPressed(button: Button) {
|
||||
val client = MinecraftClient.getInstance()
|
||||
val action =
|
||||
if (InputUtil.isKeyPressed(client.window.handle, GLFW.GLFW_KEY_LEFT_SHIFT) || InputUtil.isKeyPressed(client.window.handle, GLFW.GLFW_KEY_RIGHT_SHIFT)) {
|
||||
C2STerminalCraftingButton.Action.REQUEST_MAX_MORE
|
||||
} else {
|
||||
C2STerminalCraftingButton.Action.REQUEST_ONE_MORE
|
||||
}
|
||||
client.player!!.networkHandler.sendPacket(C2STerminalCraftingButton(terminal, action))
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,8 @@ import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
|
|||
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
|
||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
|
||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlockEntity
|
||||
import net.shadowfacts.phycon.block.terminal.TerminalBlock
|
||||
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
|
||||
|
||||
|
@ -29,6 +31,7 @@ object PhyBlockEntities {
|
|||
|
||||
val INTERFACE = create(::InterfaceBlockEntity, PhyBlocks.INTERFACE)
|
||||
val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
|
||||
val CRAFTING_TERMINAL = create(::CraftingTerminalBlockEntity, PhyBlocks.CRAFTING_TERMINAL)
|
||||
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
|
||||
val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR)
|
||||
val INSERTER = create(::InserterBlockEntity, PhyBlocks.INSERTER)
|
||||
|
@ -43,6 +46,7 @@ object PhyBlockEntities {
|
|||
fun init() {
|
||||
register(InterfaceBlock.ID, INTERFACE)
|
||||
register(TerminalBlock.ID, TERMINAL)
|
||||
register(CraftingTerminalBlock.ID, CRAFTING_TERMINAL)
|
||||
register(SwitchBlock.ID, SWITCH)
|
||||
register(ExtractorBlock.ID, EXTRACTOR)
|
||||
register(InserterBlock.ID, INSERTER)
|
||||
|
|
|
@ -13,6 +13,7 @@ import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
|
|||
import net.shadowfacts.phycon.block.netswitch.SwitchBlock
|
||||
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
|
||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
|
||||
import net.shadowfacts.phycon.block.terminal.TerminalBlock
|
||||
|
||||
/**
|
||||
|
@ -24,6 +25,7 @@ object PhyBlocks {
|
|||
|
||||
val INTERFACE = InterfaceBlock()
|
||||
val TERMINAL = TerminalBlock()
|
||||
val CRAFTING_TERMINAL = CraftingTerminalBlock()
|
||||
val SWITCH = SwitchBlock()
|
||||
val EXTRACTOR = ExtractorBlock()
|
||||
val INSERTER = InserterBlock()
|
||||
|
@ -38,6 +40,7 @@ object PhyBlocks {
|
|||
|
||||
register(InterfaceBlock.ID, INTERFACE)
|
||||
register(TerminalBlock.ID, TERMINAL)
|
||||
register(CraftingTerminalBlock.ID, CRAFTING_TERMINAL)
|
||||
register(SwitchBlock.ID, SWITCH)
|
||||
register(ExtractorBlock.ID, EXTRACTOR)
|
||||
register(InserterBlock.ID, INSERTER)
|
||||
|
|
|
@ -14,6 +14,7 @@ import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
|
|||
import net.shadowfacts.phycon.block.netswitch.SwitchBlock
|
||||
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
|
||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
|
||||
import net.shadowfacts.phycon.block.terminal.TerminalBlock
|
||||
import net.shadowfacts.phycon.item.DeviceBlockItem
|
||||
import net.shadowfacts.phycon.item.FaceDeviceBlockItem
|
||||
|
@ -29,6 +30,7 @@ object PhyItems {
|
|||
|
||||
val INTERFACE = FaceDeviceBlockItem(PhyBlocks.INTERFACE, Item.Settings())
|
||||
val TERMINAL = DeviceBlockItem(PhyBlocks.TERMINAL, Item.Settings())
|
||||
val CRAFTING_TERMINAL = DeviceBlockItem(PhyBlocks.CRAFTING_TERMINAL, Item.Settings())
|
||||
val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
|
||||
val EXTRACTOR = FaceDeviceBlockItem(PhyBlocks.EXTRACTOR, Item.Settings())
|
||||
val INSERTER = FaceDeviceBlockItem(PhyBlocks.INSERTER, Item.Settings())
|
||||
|
@ -52,6 +54,7 @@ object PhyItems {
|
|||
|
||||
register(InterfaceBlock.ID, INTERFACE)
|
||||
register(TerminalBlock.ID, TERMINAL)
|
||||
register(CraftingTerminalBlock.ID, CRAFTING_TERMINAL)
|
||||
register(SwitchBlock.ID, SWITCH)
|
||||
register(ExtractorBlock.ID, EXTRACTOR)
|
||||
register(InserterBlock.ID, INSERTER)
|
||||
|
|
|
@ -4,6 +4,8 @@ import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry
|
|||
import net.minecraft.screen.ScreenHandlerType
|
||||
import net.shadowfacts.phycon.block.inserter.InserterScreenHandler
|
||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreenHandler
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalScreenHandler
|
||||
import net.shadowfacts.phycon.block.terminal.TerminalBlock
|
||||
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
|
||||
|
||||
|
@ -11,6 +13,8 @@ object PhyScreens {
|
|||
|
||||
lateinit var TERMINAL: ScreenHandlerType<TerminalScreenHandler>
|
||||
private set
|
||||
lateinit var CRAFTING_TERMINAL: ScreenHandlerType<CraftingTerminalScreenHandler>
|
||||
private set
|
||||
lateinit var INSERTER: ScreenHandlerType<InserterScreenHandler>
|
||||
private set
|
||||
lateinit var REDSTONE_EMITTER: ScreenHandlerType<RedstoneEmitterScreenHandler>
|
||||
|
@ -18,6 +22,7 @@ object PhyScreens {
|
|||
|
||||
fun init() {
|
||||
TERMINAL = ScreenHandlerRegistry.registerExtended(TerminalBlock.ID, ::TerminalScreenHandler)
|
||||
CRAFTING_TERMINAL = ScreenHandlerRegistry.registerExtended(CraftingTerminalBlock.ID, ::CraftingTerminalScreenHandler)
|
||||
INSERTER = ScreenHandlerRegistry.registerExtended(InserterScreenHandler.ID, ::InserterScreenHandler)
|
||||
REDSTONE_EMITTER = ScreenHandlerRegistry.registerExtended(RedstoneEmitterScreenHandler.ID, ::RedstoneEmitterScreenHandler)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package net.shadowfacts.phycon.networking
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender
|
||||
import net.minecraft.network.Packet
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.registry.Registry
|
||||
import net.minecraft.util.registry.RegistryKey
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlockEntity
|
||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalScreenHandler
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
object C2STerminalCraftingButton: ServerReceiver {
|
||||
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "terminal_crafting_button")
|
||||
|
||||
enum class Action {
|
||||
CLEAR_GRID,
|
||||
REQUEST_ONE_MORE,
|
||||
REQUEST_MAX_MORE,
|
||||
}
|
||||
|
||||
operator fun invoke(terminal: CraftingTerminalBlockEntity, action: Action): Packet<*> {
|
||||
val buf = PacketByteBufs.create()
|
||||
|
||||
buf.writeIdentifier(terminal.world!!.registryKey.value)
|
||||
buf.writeBlockPos(terminal.pos)
|
||||
buf.writeByte(action.ordinal)
|
||||
|
||||
return createPacket(buf)
|
||||
}
|
||||
|
||||
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
|
||||
val dimID = buf.readIdentifier()
|
||||
val pos = buf.readBlockPos()
|
||||
val action = Action.values()[buf.readByte().toInt()]
|
||||
|
||||
server.execute {
|
||||
val key = RegistryKey.of(Registry.DIMENSION, dimID)
|
||||
val screenHandler = player.currentScreenHandler as? CraftingTerminalScreenHandler ?: return@execute
|
||||
if (screenHandler.terminal.pos != pos || screenHandler.terminal.world!!.registryKey != key) return@execute
|
||||
|
||||
when (action) {
|
||||
Action.CLEAR_GRID -> screenHandler.clearCraftingGrid()
|
||||
Action.REQUEST_ONE_MORE -> screenHandler.requestMoreCraftingIngredients(1)
|
||||
Action.REQUEST_MAX_MORE -> screenHandler.requestMoreCraftingIngredients(64)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
"block.phycon.switch": "Network Switch",
|
||||
"block.phycon.network_interface": "Inventory Interface",
|
||||
"block.phycon.terminal": "Terminal",
|
||||
"block.phycon.crafting_terminal": "Crafting Terminal",
|
||||
"block.phycon.cable_white": "White Cable",
|
||||
"block.phycon.cable_orange": "Orange Cable",
|
||||
"block.phycon.cable_magenta": "Magenta Cable",
|
||||
|
@ -35,6 +36,9 @@
|
|||
|
||||
"gui.phycon.terminal_buffer": "Buffer",
|
||||
"gui.phycon.terminal_network": "Network",
|
||||
"gui.phycon.terminal_crafting": "Crafting",
|
||||
"gui.phycon.terminal.clear_crafting": "Clear crafting table",
|
||||
"gui.phycon.terminal.more_crafting": "Request more ingredients",
|
||||
"gui.phycon.console.details": "Device Details",
|
||||
"gui.phycon.console.details.ip": "IP Address: %s",
|
||||
"gui.phycon.console.details.mac": "MAC Address: %s",
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.5 KiB |
Loading…
Reference in New Issue