Add Crafting Terminal

This commit is contained in:
Shadowfacts 2021-03-28 13:50:11 -04:00
parent 535dedbdd2
commit a098e622a7
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
18 changed files with 530 additions and 10 deletions

View File

@ -26,9 +26,10 @@ object PhysicalConnectivity: ModInitializer {
PhyItems.init() PhyItems.init()
PhyScreens.init() PhyScreens.init()
registerGlobalReceiver(C2SConfigureDevice)
registerGlobalReceiver(C2STerminalCraftingButton)
registerGlobalReceiver(C2STerminalRequestItem) registerGlobalReceiver(C2STerminalRequestItem)
registerGlobalReceiver(C2STerminalUpdateDisplayedItems) registerGlobalReceiver(C2STerminalUpdateDisplayedItems)
registerGlobalReceiver(C2SConfigureDevice)
for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) { for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) {
it.initializePhyCon(PhyConAPIImpl) it.initializePhyCon(PhyConAPIImpl)

View File

@ -8,6 +8,7 @@ import net.fabricmc.fabric.api.renderer.v1.RendererAccess
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial
import net.shadowfacts.phycon.block.inserter.InserterScreen import net.shadowfacts.phycon.block.inserter.InserterScreen
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen 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.init.PhyScreens
import net.shadowfacts.phycon.block.terminal.TerminalScreen import net.shadowfacts.phycon.block.terminal.TerminalScreen
import net.shadowfacts.phycon.client.PhyExtendedModelProvider import net.shadowfacts.phycon.client.PhyExtendedModelProvider
@ -40,6 +41,7 @@ object PhysicalConnectivityClient: ClientModInitializer {
} }
ScreenRegistry.register(PhyScreens.TERMINAL, ::TerminalScreen) ScreenRegistry.register(PhyScreens.TERMINAL, ::TerminalScreen)
ScreenRegistry.register(PhyScreens.CRAFTING_TERMINAL, ::CraftingTerminalScreen)
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen) ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen) ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)

View File

@ -52,7 +52,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>): DeviceBloc
protected val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>() protected val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>()
val internalBuffer = TerminalBufferInventory(18) val internalBuffer = TerminalBufferInventory(18)
private val pendingRequests = LinkedList<StackLocateRequest>() protected val pendingRequests = LinkedList<StackLocateRequest>()
override val pendingInsertions = mutableListOf<PendingInsertion>() override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = INSERTION_TIMEOUT override val dispatchStackTimeout = INSERTION_TIMEOUT
@ -190,7 +190,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>): DeviceBloc
sendPacket(LocateStackPacket(stack, ipAddress)) sendPacket(LocateStackPacket(stack, ipAddress))
} }
private fun stackLocateRequestCompleted(request: StackLocateRequest) { protected open fun stackLocateRequestCompleted(request: StackLocateRequest) {
pendingRequests.remove(request) pendingRequests.remove(request)
val sortedResults = request.results.toMutableList() val sortedResults = request.results.toMutableList()
@ -252,19 +252,20 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>): DeviceBloc
var bufferSlot by Delegates.notNull<Int>() var bufferSlot by Delegates.notNull<Int>()
} }
data class StackLocateRequest( open class StackLocateRequest(
val stack: ItemStack, val stack: ItemStack,
val amount: Int, val amount: Int,
val timestamp: Long, val timestamp: Long,
var results: MutableSet<Pair<Int, NetworkStackProvider>> = mutableSetOf()
) { ) {
var results: MutableSet<Pair<Int, NetworkStackProvider>> = mutableSetOf()
val totalResultAmount: Int val totalResultAmount: Int
get() = results.fold(0) { acc, (amount, _) -> acc + amount } get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Long): Boolean { fun isFinishable(currentTimestamp: Long): Boolean {
// we can't check totalResultAmount >= amount because we need to hear back from all network stack providers to // we can't check totalResultAmount >= amount because we need to hear back from all network stack providers to
// correctly sort by priority // correctly sort by priority
return currentTimestamp - timestamp >= AbstractTerminalBlockEntity.LOCATE_REQUEST_TIMEOUT return currentTimestamp - timestamp >= LOCATE_REQUEST_TIMEOUT
} }
} }

View File

@ -56,6 +56,8 @@ abstract class AbstractTerminalScreenHandler<T: AbstractTerminalBlockEntity>(
var itemsForDisplay = listOf<ItemStack>() var itemsForDisplay = listOf<ItemStack>()
private set private set
open val xOffset: Int = 0
init { init {
if (!terminal.world!!.isClient) { if (!terminal.world!!.isClient) {
assert(terminal.netItemObserver?.get() === null) 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 // intentionally don't call netItemsChanged immediately, we need to wait for the client to send us its settings
} }
val xOffset = xOffset
// network // network
for (y in 0 until 6) { for (y in 0 until 6) {
for (x in 0 until 9) { 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 // internal buffer
for (y in 0 until 6) { for (y in 0 until 6) {
for (x in 0 until 3) { 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 // player inv
for (y in 0 until 3) { for (y in 0 until 3) {
for (x in 0 until 9) { 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 // hotbar
for (x in 0 until 9) { for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 66 + x * 18, 198)) addSlot(Slot(playerInv, x, xOffset + 66 + x * 18, 198))
} }
} }

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}
}

View File

@ -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))
}
}

View File

@ -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_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity 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.TerminalBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
@ -29,6 +31,7 @@ object PhyBlockEntities {
val INTERFACE = create(::InterfaceBlockEntity, PhyBlocks.INTERFACE) val INTERFACE = create(::InterfaceBlockEntity, PhyBlocks.INTERFACE)
val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL) val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
val CRAFTING_TERMINAL = create(::CraftingTerminalBlockEntity, PhyBlocks.CRAFTING_TERMINAL)
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH) val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR) val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR)
val INSERTER = create(::InserterBlockEntity, PhyBlocks.INSERTER) val INSERTER = create(::InserterBlockEntity, PhyBlocks.INSERTER)
@ -43,6 +46,7 @@ object PhyBlockEntities {
fun init() { fun init() {
register(InterfaceBlock.ID, INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(CraftingTerminalBlock.ID, CRAFTING_TERMINAL)
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(ExtractorBlock.ID, EXTRACTOR) register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER) register(InserterBlock.ID, INSERTER)

View File

@ -13,6 +13,7 @@ import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock 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.block.terminal.TerminalBlock
/** /**
@ -24,6 +25,7 @@ object PhyBlocks {
val INTERFACE = InterfaceBlock() val INTERFACE = InterfaceBlock()
val TERMINAL = TerminalBlock() val TERMINAL = TerminalBlock()
val CRAFTING_TERMINAL = CraftingTerminalBlock()
val SWITCH = SwitchBlock() val SWITCH = SwitchBlock()
val EXTRACTOR = ExtractorBlock() val EXTRACTOR = ExtractorBlock()
val INSERTER = InserterBlock() val INSERTER = InserterBlock()
@ -38,6 +40,7 @@ object PhyBlocks {
register(InterfaceBlock.ID, INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(CraftingTerminalBlock.ID, CRAFTING_TERMINAL)
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(ExtractorBlock.ID, EXTRACTOR) register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER) register(InserterBlock.ID, INSERTER)

View File

@ -14,6 +14,7 @@ import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock 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.block.terminal.TerminalBlock
import net.shadowfacts.phycon.item.DeviceBlockItem import net.shadowfacts.phycon.item.DeviceBlockItem
import net.shadowfacts.phycon.item.FaceDeviceBlockItem import net.shadowfacts.phycon.item.FaceDeviceBlockItem
@ -29,6 +30,7 @@ object PhyItems {
val INTERFACE = FaceDeviceBlockItem(PhyBlocks.INTERFACE, Item.Settings()) val INTERFACE = FaceDeviceBlockItem(PhyBlocks.INTERFACE, Item.Settings())
val TERMINAL = DeviceBlockItem(PhyBlocks.TERMINAL, 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 SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
val EXTRACTOR = FaceDeviceBlockItem(PhyBlocks.EXTRACTOR, Item.Settings()) val EXTRACTOR = FaceDeviceBlockItem(PhyBlocks.EXTRACTOR, Item.Settings())
val INSERTER = FaceDeviceBlockItem(PhyBlocks.INSERTER, Item.Settings()) val INSERTER = FaceDeviceBlockItem(PhyBlocks.INSERTER, Item.Settings())
@ -52,6 +54,7 @@ object PhyItems {
register(InterfaceBlock.ID, INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(CraftingTerminalBlock.ID, CRAFTING_TERMINAL)
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(ExtractorBlock.ID, EXTRACTOR) register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER) register(InserterBlock.ID, INSERTER)

View File

@ -4,6 +4,8 @@ import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry
import net.minecraft.screen.ScreenHandlerType import net.minecraft.screen.ScreenHandlerType
import net.shadowfacts.phycon.block.inserter.InserterScreenHandler import net.shadowfacts.phycon.block.inserter.InserterScreenHandler
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreenHandler 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.TerminalBlock
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
@ -11,6 +13,8 @@ object PhyScreens {
lateinit var TERMINAL: ScreenHandlerType<TerminalScreenHandler> lateinit var TERMINAL: ScreenHandlerType<TerminalScreenHandler>
private set private set
lateinit var CRAFTING_TERMINAL: ScreenHandlerType<CraftingTerminalScreenHandler>
private set
lateinit var INSERTER: ScreenHandlerType<InserterScreenHandler> lateinit var INSERTER: ScreenHandlerType<InserterScreenHandler>
private set private set
lateinit var REDSTONE_EMITTER: ScreenHandlerType<RedstoneEmitterScreenHandler> lateinit var REDSTONE_EMITTER: ScreenHandlerType<RedstoneEmitterScreenHandler>
@ -18,6 +22,7 @@ object PhyScreens {
fun init() { fun init() {
TERMINAL = ScreenHandlerRegistry.registerExtended(TerminalBlock.ID, ::TerminalScreenHandler) TERMINAL = ScreenHandlerRegistry.registerExtended(TerminalBlock.ID, ::TerminalScreenHandler)
CRAFTING_TERMINAL = ScreenHandlerRegistry.registerExtended(CraftingTerminalBlock.ID, ::CraftingTerminalScreenHandler)
INSERTER = ScreenHandlerRegistry.registerExtended(InserterScreenHandler.ID, ::InserterScreenHandler) INSERTER = ScreenHandlerRegistry.registerExtended(InserterScreenHandler.ID, ::InserterScreenHandler)
REDSTONE_EMITTER = ScreenHandlerRegistry.registerExtended(RedstoneEmitterScreenHandler.ID, ::RedstoneEmitterScreenHandler) REDSTONE_EMITTER = ScreenHandlerRegistry.registerExtended(RedstoneEmitterScreenHandler.ID, ::RedstoneEmitterScreenHandler)
} }

View File

@ -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)
}
}
}
}

View File

@ -2,6 +2,7 @@
"block.phycon.switch": "Network Switch", "block.phycon.switch": "Network Switch",
"block.phycon.network_interface": "Inventory Interface", "block.phycon.network_interface": "Inventory Interface",
"block.phycon.terminal": "Terminal", "block.phycon.terminal": "Terminal",
"block.phycon.crafting_terminal": "Crafting Terminal",
"block.phycon.cable_white": "White Cable", "block.phycon.cable_white": "White Cable",
"block.phycon.cable_orange": "Orange Cable", "block.phycon.cable_orange": "Orange Cable",
"block.phycon.cable_magenta": "Magenta Cable", "block.phycon.cable_magenta": "Magenta Cable",
@ -35,6 +36,9 @@
"gui.phycon.terminal_buffer": "Buffer", "gui.phycon.terminal_buffer": "Buffer",
"gui.phycon.terminal_network": "Network", "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": "Device Details",
"gui.phycon.console.details.ip": "IP Address: %s", "gui.phycon.console.details.ip": "IP Address: %s",
"gui.phycon.console.details.mac": "MAC 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