Skip to content

Commit

Permalink
Impl pipe side mode
Browse files Browse the repository at this point in the history
  • Loading branch information
james58899 committed Jun 28, 2021
1 parent 3b39805 commit a835589
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.ItemScatterer;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
Expand Down Expand Up @@ -86,4 +88,17 @@ public BlockState getStateForNeighborUpdate(BlockState state, Direction directio
if (entity instanceof CustomBlockNeighborUpdateListener) ((CustomBlockNeighborUpdateListener) entity).onNeighborUpdate(true);
return super.getStateForNeighborUpdate(state, direction, newState, world, pos, posFrom);
}

@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
if (!state.isOf(newState.getBlock())) {
BlockEntity blockEntity = world.getBlockEntity(pos);
if (blockEntity instanceof Inventory) {
ItemScatterer.spawn(world, pos, (Inventory) blockEntity);
world.updateComparators(pos, (Block) (Object) this);
}

super.onStateReplaced(state, world, pos, newState, moved);
}
}
}
13 changes: 13 additions & 0 deletions src/main/kotlin/one/oktw/galaxy/block/entity/CustomBlockEntity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,22 @@ package one.oktw.galaxy.block.entity
import net.minecraft.block.Blocks
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.nbt.NbtCompound
import net.minecraft.util.math.BlockPos

// BlockEntity need extend
open class CustomBlockEntity(type: BlockEntityType<*>, pos: BlockPos) : BlockEntity(type, pos, Blocks.BARRIER.defaultState) {
fun getId() = BlockEntityType.getId(type)!!

override fun readNbt(nbt: NbtCompound) {
super.readNbt(nbt)
readCopyableData(nbt)
}

/**
* Read custom data from NBT, it will use on block clone.
*
* Also call by [readNbt].
*/
open fun readCopyableData(nbt: NbtCompound) = Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ class DummyBlockEntity(type: BlockEntityType<*>, pos: BlockPos) : CustomBlockEnt
tag.getString("id")?.let(Identifier::tryParse)?.let(CustomBlock.registry::get)?.let {
if (it != CustomBlock.DUMMY) {
world?.removeBlockEntity(pos)
world?.addBlockEntity(it.createBlockEntity(pos))

val realBlockEntity = it.createBlockEntity(pos)
realBlockEntity.readCopyableData(tag)

world?.addBlockEntity(realBlockEntity)
}
}
}
Expand Down
231 changes: 195 additions & 36 deletions src/main/kotlin/one/oktw/galaxy/block/entity/PipeBlockEntity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,54 +18,68 @@

package one.oktw.galaxy.block.entity

import com.google.common.collect.MapMaker
import net.fabricmc.fabric.api.util.NbtType
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.decoration.ItemFrameEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.SidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtList
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.ItemScatterer
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import one.oktw.galaxy.block.listener.CustomBlockClickListener
import one.oktw.galaxy.block.listener.CustomBlockNeighborUpdateListener
import one.oktw.galaxy.block.pipe.ItemTransferPacket
import one.oktw.galaxy.block.pipe.SideMode
import one.oktw.galaxy.block.pipe.*
import one.oktw.galaxy.block.pipe.PipeSideMode.*
import one.oktw.galaxy.item.CustomItemHelper
import one.oktw.galaxy.item.PipeModelItem
import one.oktw.galaxy.item.PipeModelItem.Companion.PIPE_PORT_EXPORT
import one.oktw.galaxy.item.PipeModelItem.Companion.PIPE_PORT_IMPORT
import one.oktw.galaxy.item.PipeModelItem.Companion.PIPE_PORT_STORAGE
import one.oktw.galaxy.item.Tool
import one.oktw.galaxy.util.getOrCreateSubTag
import java.util.*

open class PipeBlockEntity(type: BlockEntityType<*>, pos: BlockPos, modelItem: ItemStack) : ModelCustomBlockEntity(type, pos, modelItem),
CustomBlockClickListener,
CustomBlockNeighborUpdateListener {
CustomBlockNeighborUpdateListener,
SidedInventory {
companion object {
private val POSITIVE_DIRECTION = Direction.values().filter { it.direction == Direction.AxisDirection.POSITIVE }
}

private val sideMode = HashMap<Direction, SideMode>()
private val side = HashMap<Direction, PipeSide>()
private val sideEntity = HashMap<Direction, UUID>()
private val queue = LinkedList<ItemTransferPacket>()
private val connectedPipe = MapMaker().weakValues().concurrencyLevel(1).makeMap<Direction, PipeBlockEntity>()
private var showingItemEntity: ItemEntity? = null
private var redstone = 0
private var needUpdatePipeConnect = true

/**
* Push [ItemTransferPacket] into pipe queue.
*/
fun pushItem(item: ItemTransferPacket) {
queue.offer(item)
fun pushItem(item: ItemTransferPacket): Boolean {
return queue.offer(item)
}

/**
* Get pipe I/O mode.
*/
fun getMode(side: Direction): SideMode {
return sideMode.getOrDefault(side, SideMode.NONE)
fun getMode(side: Direction): PipeSideMode {
return this.side[side]?.mode ?: NONE
}

fun getPressure() {
queue.size
}

override fun tick() {
Expand All @@ -89,35 +103,79 @@ open class PipeBlockEntity(type: BlockEntityType<*>, pos: BlockPos, modelItem: I
super.markRemoved()
val world = world as ServerWorld
sideEntity.values.forEach { world.getEntity(it)?.discard() }
side.values.mapNotNull {
when (it.mode) {
IMPORT -> PIPE_PORT_IMPORT
EXPORT -> PIPE_PORT_EXPORT
STORAGE -> PIPE_PORT_STORAGE
else -> null
}?.createItemStack()
}
}

override fun readNbt(tag: NbtCompound) {
super.readNbt(tag)
val pipeData = tag.getCompound("GalaxyData").getCompound("PipeData")
override fun readNbt(nbt: NbtCompound) {
super.readNbt(nbt)
val pipeData = nbt.getCompound("GalaxyData").getCompound("PipeData")

if (sideEntity.isNotEmpty() && world is ServerWorld) {
sideEntity.values.forEach { (world as ServerWorld).getEntity(it)?.discard() }
sideEntity.clear()
}

sideEntity.clear()
sideMode.clear()
queue.clear()
pipeData.getCompound("SideEntity").run { keys.forEach { sideEntity[Direction.valueOf(it)] = getUuid(it) } }
pipeData.getCompound("SideMode").run { keys.forEach { sideMode[Direction.valueOf(it)] = SideMode.valueOf(getString(it)) } }

queue.clear()
pipeData.getList("queue", NbtType.COMPOUND).mapTo(queue) { ItemTransferPacket.createFromTag(it as NbtCompound) }
}

override fun readCopyableData(nbt: NbtCompound) {
super.readCopyableData(nbt)

side.clear()
nbt.getCompound("GalaxyData").getCompound("PipeData").getCompound("Side").run {
keys.forEach { direction -> PipeSide.createFromNBT(getCompound(direction))?.let { side[Direction.valueOf(direction)] = it } }
}
}

override fun writeNbt(tag: NbtCompound): NbtCompound {
super.writeNbt(tag)

tag.getOrCreateSubTag("GalaxyData").getOrCreateSubTag("PipeData").apply {
put("Queue", queue.mapTo(NbtList()) { it.toTag(NbtCompound()) })
put("SideMode", NbtCompound().apply { sideMode.forEach { (k, v) -> if (v != SideMode.NONE) putString(k.name, v.name) } })
put("SideEntity", NbtCompound().apply { sideEntity.forEach { (k, v) -> putUuid(k.name, v) } })
put("Side", NbtCompound().apply { side.forEach { (direction, side) -> if (side.mode != NONE) put(direction.name, side.writeNBT(NbtCompound())) } })
put("SideEntity", NbtCompound().apply { sideEntity.forEach { (direction, v) -> putUuid(direction.name, v) } })
}

return tag
}

override fun onClick(player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
// TODO plug IO module or open GUI
return ActionResult.PASS
val item = player.getStackInHand(hand)
val direction = hit.side.opposite
when (item.let(CustomItemHelper::getItem)) {
PIPE_PORT_EXPORT -> setSideMode(direction, EXPORT)
PIPE_PORT_IMPORT -> setSideMode(direction, IMPORT)
PIPE_PORT_STORAGE -> setSideMode(direction, STORAGE)
Tool.WRENCH -> {
val dropPos = pos.offset(hit.side)
when (getMode(direction)) {
IMPORT -> PIPE_PORT_IMPORT.createItemStack()
EXPORT -> PIPE_PORT_EXPORT.createItemStack()
STORAGE -> PIPE_PORT_STORAGE.createItemStack()
else -> null
}?.let {
ItemScatterer.spawn(world, dropPos.x.toDouble(), dropPos.y.toDouble(), dropPos.z.toDouble(), it)
setSideMode(direction, NONE)
return ActionResult.SUCCESS
}

return ActionResult.PASS
}
else -> return ActionResult.PASS
}
if (!player.isCreative) item.decrement(1)

return ActionResult.SUCCESS
}

override fun onNeighborUpdate(direct: Boolean) {
Expand All @@ -129,9 +187,94 @@ open class PipeBlockEntity(type: BlockEntityType<*>, pos: BlockPos, modelItem: I
}
}

private fun setSideMode(side: Direction, mode: SideMode) {
if (mode == SideMode.NONE) {
sideMode.remove(side)?.let {
override fun clear() {
// TODO
}

override fun size(): Int {
return 60 // queue 54 + side 6
}

override fun isEmpty() = false

override fun getStack(slot: Int): ItemStack {
return if (slot < 54) {
queue.getOrNull(slot)?.item ?: ItemStack.EMPTY
} else {
when (side[Direction.byId(slot - 54)]?.mode) {
IMPORT -> PIPE_PORT_IMPORT.createItemStack()
EXPORT -> PIPE_PORT_EXPORT.createItemStack()
STORAGE -> PIPE_PORT_STORAGE.createItemStack()
else -> ItemStack.EMPTY
}
}
}

override fun removeStack(slot: Int, amount: Int): ItemStack {
return ItemStack.EMPTY
// return if (slot < 54) {
// if (slot > queue.size || amount <= 0) ItemStack.EMPTY else {
// val item = queue.removeAt(slot)
// val remove = item.item.split(amount)
// if (item.item.count > 0) queue.push(item)
// return remove
// }
// } else {
// val direction = Direction.byId(slot - 54)
// val mode = side.remove(direction)?.mode
//
// if (mode != null) {
// val world = world as ServerWorld
// sideEntity[direction]?.let(world::getEntity)?.discard()
// }
//
// when (mode) {
// IMPORT -> PIPE_PORT_IMPORT.createItemStack()
// EXPORT -> PIPE_PORT_EXPORT.createItemStack()
// STORAGE -> PIPE_PORT_STORAGE.createItemStack()
// else -> ItemStack.EMPTY
// }
// }
}

override fun removeStack(slot: Int): ItemStack {
return ItemStack.EMPTY // Not support
// return if (slot < 54) {
// if (slot >= queue.size) ItemStack.EMPTY else queue.removeAt(slot).item
// } else {
// val direction = Direction.byId(slot - 54)
// val mode = side.remove(direction)?.mode
//
// if (mode != null) {
// val world = world as ServerWorld
// sideEntity[direction]?.let(world::getEntity)?.discard()
// }
//
// when (mode) {
// IMPORT -> PIPE_PORT_IMPORT.createItemStack()
// EXPORT -> PIPE_PORT_EXPORT.createItemStack()
// STORAGE -> PIPE_PORT_STORAGE.createItemStack()
// else -> ItemStack.EMPTY
// }
// }
}

override fun setStack(slot: Int, stack: ItemStack?) {
// Not support
}

override fun canPlayerUse(player: PlayerEntity?) = false

// Not support IO
override fun getAvailableSlots(side: Direction?) = intArrayOf()

override fun canInsert(slot: Int, stack: ItemStack, dir: Direction?) = false

override fun canExtract(slot: Int, stack: ItemStack, dir: Direction) = false

private fun setSideMode(side: Direction, mode: PipeSideMode) {
if (mode == NONE) {
this.side.remove(side)?.let {
// Remove IO entity
val world = world as ServerWorld
sideEntity[side]?.let(world::getEntity)?.discard()
Expand All @@ -140,26 +283,42 @@ open class PipeBlockEntity(type: BlockEntityType<*>, pos: BlockPos, modelItem: I
updatePipeConnect()
}
} else {
sideMode[side] = mode
this.side[side] = when (mode) {
IMPORT -> PipeSideImport()
EXPORT -> PipeSideExport()
STORAGE -> PipeSideStorage()
NONE -> throw IllegalStateException()
}
spawnSideEntity(side, mode)
}
}

private fun updatePipeConnect() {
val world = (world as ServerWorld)
for (direction in POSITIVE_DIRECTION) {
val connectPipe = world.getBlockEntity(pos.offset(direction.opposite))
if (sideMode.getOrDefault(direction, SideMode.NONE) != SideMode.NONE || connectPipe !is PipeBlockEntity) {
sideEntity.remove(direction)?.let(world::getEntity)?.discard()
continue
}
if (!sideEntity.contains(direction) && connectPipe.getMode(direction.opposite) == SideMode.NONE) {
spawnSideEntity(direction, SideMode.NONE)
for (direction in Direction.values()) {
val connectPipe = world.getBlockEntity(pos.offset(direction.opposite)) as? PipeBlockEntity
val sideMode = side[direction]?.mode
if (connectPipe != null && sideMode == null) {
connectedPipe[direction] = connectPipe

// Connect pipe
if (direction in POSITIVE_DIRECTION && connectPipe.getMode(direction.opposite) == NONE) {
spawnSideEntity(direction, NONE)
}
} else {
connectedPipe.remove(direction)

// Disconnect pipe
if (sideMode == null) {
sideEntity.remove(direction)?.let(world::getEntity)?.discard()
} else if (sideEntity[direction] == null) {
spawnSideEntity(direction, sideMode)
}
}
}
}

private fun spawnSideEntity(side: Direction, mode: SideMode) {
private fun spawnSideEntity(side: Direction, mode: PipeSideMode) {
val world = world as ServerWorld
sideEntity[side]?.let(world::getEntity)?.discard()

Expand All @@ -170,10 +329,10 @@ open class PipeBlockEntity(type: BlockEntityType<*>, pos: BlockPos, modelItem: I
isSilent = true
addScoreboardTag("PIPE")
heldItemStack = when (mode) {
SideMode.NONE -> PipeModelItem.PIPE_EXTENDED.createItemStack()
SideMode.IMPORT -> PipeModelItem.PIPE_PORT_IMPORT.createItemStack()
SideMode.EXPORT -> PipeModelItem.PIPE_PORT_EXPORT.createItemStack()
SideMode.STORAGE -> PipeModelItem.PIPE_PORT_STORAGE.createItemStack()
NONE -> PipeModelItem.PIPE_EXTENDED.createItemStack()
IMPORT -> PIPE_PORT_IMPORT.createItemStack()
EXPORT -> PIPE_PORT_EXPORT.createItemStack()
STORAGE -> PIPE_PORT_STORAGE.createItemStack()
}
}
if (world.spawnEntity(entity)) sideEntity[side] = entity.uuid
Expand Down

0 comments on commit a835589

Please sign in to comment.