Compare commits

..

4 Commits

Author SHA1 Message Date
Shadowfacts f12965fc6c
Bump version 2021-03-24 17:28:40 -04:00
Shadowfacts 28e14ae8bf
Add terminal settings API 2021-03-24 17:28:03 -04:00
Shadowfacts e41c9e3ccb
Fix not being able to close just terminal amount dialog 2021-03-23 18:20:59 -04:00
Shadowfacts b87a36caa4
Add TechReborn plugin 2021-03-22 22:08:08 -04:00
33 changed files with 600 additions and 126 deletions

View File

@ -74,7 +74,7 @@ repositories {
dependencies {
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
modImplementation "alexiil.mc.lib:libblockattributes-all:${project.libblockattributes_version}"
modCompile "alexiil.mc.lib:libblockattributes-all:${project.libblockattributes_version}"
include "alexiil.mc.lib:libblockattributes-core:${project.libblockattributes_version}"
include "alexiil.mc.lib:libblockattributes-items:${project.libblockattributes_version}"
@ -85,6 +85,8 @@ dependencies {
include project(":plugin:mousewheelie")
runtimeOnly project(":plugin:rei")
include project(":plugin:rei")
runtimeOnly project(":plugin:techreborn")
include project(":plugin:techreborn")
modRuntime("io.github.cottonmc:cotton-resources:${project.cotton_resources_version}") {
exclude group: "net.fabricmc.fabric-api"

View File

@ -5,7 +5,7 @@ minecraft_version=1.16.5
yarn_mappings=1.16.5+build.4
loader_version=0.11.1
mod_version=0.1.0
mod_version=0.2.0
maven_group=net.shadowfacts
archives_base_name=PhysicalConnectivity

View File

@ -34,8 +34,8 @@ object PhyConPlugin: ClientModInitializer, REIPluginV0 {
override fun registerBounds(helper: DisplayHelper) {
BaseBoundsHandler.getInstance().registerExclusionZones(TerminalScreen::class.java) {
val screen = MinecraftClient.getInstance().currentScreen as TerminalScreen
val button = screen.terminalVC.sortMode
val rect = button.convert(button.bounds, to = null)
val view = screen.terminalVC.settingsView
val rect = view.convert(view.bounds, to = null)
listOf(
Rectangle(rect.left.toInt(), rect.top.toInt(), 20, 20)
)

View File

@ -0,0 +1,24 @@
plugins {
id "fabric-loom"
id "org.jetbrains.kotlin.jvm"
}
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
repositories {
maven {
url = "https://maven.modmuss50.me/"
}
jcenter()
}
dependencies {
implementation project(":")
modImplementation("TechReborn:TechReborn-1.16:${project.techreborn_version}") {
exclude group: "net.fabricmc.fabric-api"
}
}

View File

@ -0,0 +1,4 @@
archives_base_name=PhyCon-Plugin-TechReborn
techreborn_version=3.8.2+build.222

View File

@ -0,0 +1,31 @@
package net.shadowfacts.phycon.plugin.techreborn
import alexiil.mc.lib.attributes.AttributeList
import alexiil.mc.lib.attributes.AttributeSourceType
import alexiil.mc.lib.attributes.item.GroupedItemInv
import alexiil.mc.lib.attributes.item.ItemAttributes
import net.fabricmc.api.ModInitializer
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import techreborn.blockentity.storage.item.StorageUnitBaseBlockEntity
import techreborn.init.TRContent
/**
* @author shadowfacts
*/
object PhyConTR: ModInitializer {
override fun onInitialize() {
TRContent.StorageUnit.values().forEach {
ItemAttributes.GROUPED_INV.setBlockAdder(AttributeSourceType.COMPAT_WRAPPER, it.block, ::addStorageUnitGroupedInv)
}
}
private fun addStorageUnitGroupedInv(world: World, pos: BlockPos, state: BlockState, to: AttributeList<GroupedItemInv>) {
(world.getBlockEntity(pos) as? StorageUnitBaseBlockEntity)?.also { su ->
to.offer(StorageUnitWrapper(su))
}
}
}

View File

@ -0,0 +1,81 @@
package net.shadowfacts.phycon.plugin.techreborn
import alexiil.mc.lib.attributes.Simulation
import alexiil.mc.lib.attributes.item.GroupedItemInv
import alexiil.mc.lib.attributes.item.GroupedItemInvView
import alexiil.mc.lib.attributes.item.ItemStackCollections
import alexiil.mc.lib.attributes.item.ItemStackUtil
import alexiil.mc.lib.attributes.item.filter.ItemFilter
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.util.copyWithCount
import techreborn.blockentity.storage.item.StorageUnitBaseBlockEntity
import kotlin.math.min
/**
* @author shadowfacts
*/
class StorageUnitWrapper(
val be: StorageUnitBaseBlockEntity,
): GroupedItemInv {
override fun getStoredStacks(): Set<ItemStack> {
val set = ItemStackCollections.set()
if (!be.storedStack.isEmpty) {
set.add(be.storedStack)
}
return set
}
override fun getTotalCapacity(): Int {
return be.maxCapacity
}
override fun getStatistics(filter: ItemFilter): GroupedItemInvView.ItemInvStatistic {
// todo: should spaceAddable really be zero? that's what SimpleGroupedItemInv does
return if (be.storedStack.isEmpty) {
GroupedItemInvView.ItemInvStatistic(filter, 0, 0, totalCapacity)
} else if (filter.matches(be.storedStack)) {
// don't use the storedAmount field, it's only used on the client for rendering
val amount = be.getStoredAmount()
GroupedItemInvView.ItemInvStatistic(filter, amount, 0, totalCapacity - amount)
} else {
GroupedItemInvView.ItemInvStatistic(filter, 0, 0, 0)
}
}
override fun attemptInsertion(filter: ItemStack, simulation: Simulation): ItemStack {
if (simulation.isAction) {
return be.processInput(filter)
}
if (be.storedStack.isEmpty) {
return ItemStack.EMPTY
}
if (!ItemStackUtil.areEqualIgnoreAmounts(be.storedStack, filter)) {
return filter
}
val availableCapacity = totalCapacity - be.getStoredAmount()
return if (availableCapacity >= filter.count) {
ItemStack.EMPTY
} else {
filter.copyWithCount(filter.count - availableCapacity)
}
}
override fun attemptExtraction(filter: ItemFilter, maxAmount: Int, simulation: Simulation): ItemStack {
if (be.storedStack.isEmpty || !filter.matches(be.storedStack)) {
return ItemStack.EMPTY
}
val extracted = min(maxAmount, be.getStoredAmount())
if (simulation.isAction) {
be.storedStack.decrement(extracted)
}
return be.storedStack.copyWithCount(extracted)
}
}

View File

@ -0,0 +1,38 @@
{
"schemaVersion": 1,
"id": "phycon_techreborn",
"version": "${version}",
"name": "PhyCon TechReborn Integration",
"description": "",
"authors": [
"Shadowfacts"
],
"contact": {
"homepage": "https://git.shadowfacts.net/minecraft/PhysicalConnectivity"
},
"license": "LGPL-3.0",
"environment": "client",
"entrypoints": {
"main": [
{
"adapter": "kotlin",
"value": "net.shadowfacts.phycon.plugin.techreborn.PhyConTR"
}
]
},
"mixins": [
],
"depends": {
"fabricloader": ">=0.4.0",
"fabric": "*",
"fabric-language-kotlin": ">=1.3.50",
"phycon": "*",
"techreborn": "*"
},
"custom": {
"modmenu:parent": "phycon"
}
}

View File

@ -12,3 +12,4 @@ pluginManagement {
include("kiwi-java")
include("plugin:mousewheelie")
include("plugin:rei")
include("plugin:techreborn")

View File

@ -0,0 +1,14 @@
package net.shadowfacts.phycon.api;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
/**
* @author shadowfacts
*/
public interface PhyConAPI {
@NotNull
<E extends Enum<E> & TerminalSetting> TerminalSettingKey<E> registerTerminalSetting(Identifier id, E defaultValue);
}

View File

@ -0,0 +1,10 @@
package net.shadowfacts.phycon.api;
/**
* @author shadowfacts
*/
public interface PhyConPlugin {
void initializePhyCon(PhyConAPI api);
}

View File

@ -0,0 +1,18 @@
package net.shadowfacts.phycon.api;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
/**
* @author shadowfacts
*/
public interface TerminalSetting {
Identifier getIconTexture();
int[] getUV();
@Nullable Text getTooltip();
}

View File

@ -0,0 +1,14 @@
package net.shadowfacts.phycon.api;
import net.minecraft.util.Identifier;
/**
* @author shadowfacts
*/
public interface TerminalSettingKey<E extends Enum<E> & TerminalSetting> {
Identifier getID();
E getValue();
}

View File

@ -13,6 +13,7 @@ import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.window.ScreenHandlerWindow
import net.shadowfacts.cacao.window.Window
import org.lwjgl.glfw.GLFW
import java.util.*
/**
@ -48,6 +49,9 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
override fun removeWindow(window: Window) {
_windows.remove(window)
if (windows.isEmpty()) {
onClose()
}
}
override fun init() {
@ -151,12 +155,17 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
windows.lastOrNull()?.removeFromScreen()
return true
} else {
val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {
return true
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
}
override fun charTyped(char: Char, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) }
@ -166,4 +175,8 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
return super.charTyped(char, modifiers)
}
override fun shouldCloseOnEsc(): Boolean {
return false
}
}

View File

@ -10,6 +10,7 @@ import net.shadowfacts.cacao.util.KeyModifiers
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.window.Window
import org.lwjgl.glfw.GLFW
import java.util.*
/**
@ -60,7 +61,9 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
*/
override fun removeWindow(window: Window) {
_windows.remove(window)
// todo: VC callbacks
if (windows.isEmpty()) {
onClose()
}
}
override fun init() {
@ -126,12 +129,17 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
windows.lastOrNull()?.removeFromScreen()
return true
} else {
val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {
return true
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
}
override fun keyReleased(i: Int, j: Int, k: Int): Boolean {
return super.keyReleased(i, j, k)
@ -145,6 +153,10 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
return super.charTyped(char, modifiers)
}
override fun shouldCloseOnEsc(): Boolean {
return false
}
}
fun AbstractCacaoScreen.findResponder(fn: (Responder) -> Boolean): Boolean {

View File

@ -11,10 +11,12 @@ import net.shadowfacts.cacao.util.texture.Texture
*
* @author shadowfacts
*/
class TextureView(var texture: Texture): View() {
class TextureView(var texture: Texture?): View() {
override fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) {
RenderHelper.draw(matrixStack, bounds, texture)
texture?.also {
RenderHelper.draw(matrixStack, bounds, it)
}
}
}

View File

@ -0,0 +1,21 @@
package net.shadowfacts.phycon
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.api.PhyConAPI
import net.shadowfacts.phycon.api.PhyConPlugin
import net.shadowfacts.phycon.api.TerminalSettingKey
import net.shadowfacts.phycon.util.SortMode
/**
* @author shadowfacts
*/
object DefaultPlugin: PhyConPlugin {
lateinit var SORT_MODE: TerminalSettingKey<SortMode>
private set
override fun initializePhyCon(api: PhyConAPI) {
SORT_MODE = api.registerTerminalSetting(Identifier(PhysicalConnectivity.MODID, "sort"), SortMode.COUNT_HIGH_FIRST)
}
}

View File

@ -0,0 +1,18 @@
package net.shadowfacts.phycon
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.api.PhyConAPI
import net.shadowfacts.phycon.api.TerminalSetting
import net.shadowfacts.phycon.api.TerminalSettingKey
import net.shadowfacts.phycon.util.TerminalSettings
/**
* @author shadowfacts
*/
object PhyConAPIImpl: PhyConAPI {
override fun <E> registerTerminalSetting(id: Identifier, defaultValue: E): TerminalSettingKey<E> where E: Enum<E>, E: TerminalSetting? {
return TerminalSettings.register(id, defaultValue)
}
}

View File

@ -2,6 +2,8 @@ package net.shadowfacts.phycon
import net.fabricmc.api.ModInitializer
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
import net.fabricmc.loader.api.FabricLoader
import net.shadowfacts.phycon.api.PhyConPlugin
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyItems
@ -27,6 +29,10 @@ object PhysicalConnectivity: ModInitializer {
registerGlobalReceiver(C2STerminalRequestItem)
registerGlobalReceiver(C2STerminalUpdateDisplayedItems)
registerGlobalReceiver(C2SConfigureDevice)
for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) {
it.initializePhyCon(PhyConAPIImpl)
}
}
private fun registerGlobalReceiver(receiver: ServerReceiver) {

View File

@ -14,12 +14,15 @@ import net.shadowfacts.phycon.client.PhyExtendedModelProvider
import net.shadowfacts.phycon.client.PhyModelProvider
import net.shadowfacts.phycon.networking.ClientReceiver
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.TerminalSettings
/**
* @author shadowfacts
*/
object PhysicalConnectivityClient: ClientModInitializer {
val terminalSettings = TerminalSettings()
var screenMaterial: RenderMaterial? = null
private set

View File

@ -0,0 +1,72 @@
package net.shadowfacts.phycon.block.terminal
import net.minecraft.client.util.math.MatrixStack
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.EnumHelper
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.button.AbstractButton
import net.shadowfacts.phycon.PhysicalConnectivityClient
import net.shadowfacts.phycon.api.TerminalSetting
import net.shadowfacts.phycon.util.TerminalSettings
import java.util.EnumMap
/**
* @author shadowfacts
*/
class SettingButton<E>(
val key: TerminalSettings.SettingKey<E>,
): AbstractButton<SettingButton<E>>(
TextureView(null).apply {
intrinsicContentSize = Size(16.0, 16.0)
},
padding = 2.0
) where E: Enum<E>, E: TerminalSetting {
private val textureCache = EnumMap<E, Texture>(key.clazz)
private val textureView: TextureView
get() = content as TextureView
init {
updateTexture()
}
private fun updateTexture() {
textureView.texture = textureCache.getOrPut(key.value) {
val uv = key.value.uv
Texture(key.value.iconTexture, uv[0], uv[1])
}
}
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
if (!disabled) {
val newValue = when (mouseButton) {
MouseButton.LEFT -> EnumHelper.next(key.value)
MouseButton.RIGHT -> EnumHelper.previous(key.value)
else -> {
return false
}
}
PhysicalConnectivityClient.terminalSettings[key] = newValue
updateTexture()
}
return super.mouseClicked(point, mouseButton)
}
override fun draw(matrixStack: MatrixStack, mouse: Point, delta: Float) {
super.draw(matrixStack, mouse, delta)
if (mouse in bounds) {
key.value.tooltip?.also {
window!!.drawTooltip(listOf(it))
}
}
}
}

View File

@ -1,57 +0,0 @@
package net.shadowfacts.phycon.block.terminal
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.EnumHelper
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.button.AbstractButton
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.util.SortMode
/**
* @author shadowfacts
*/
class SortModeButton(
initialValue: SortMode,
): AbstractButton<SortModeButton>(
TextureView(TEXTURES[initialValue]!!).apply {
intrinsicContentSize = Size(16.0, 16.0)
}
) {
companion object {
private val ID = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png")
private val TEXTURES = mapOf(
SortMode.COUNT_HIGH_FIRST to Texture(ID, 0, 230),
SortMode.COUNT_LOW_FIRST to Texture(ID, 16, 230),
SortMode.ALPHABETICAL to Texture(ID, 32, 230),
)
}
private val textureView: TextureView
get() = content as TextureView
var value: SortMode = initialValue
set(value) {
field = value
textureView.texture = TEXTURES[value]!!
}
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
if (!disabled) {
value = when (mouseButton) {
MouseButton.LEFT -> EnumHelper.next(value)
MouseButton.RIGHT -> EnumHelper.previous(value)
else -> {
return false
}
}
}
return super.mouseClicked(point, mouseButton)
}
}

View File

@ -57,6 +57,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
private var observers = 0
val cachedNetItems = ItemStackCollections.intMap()
// todo: multiple players could have the terminal open simultaneously
var netItemObserver: WeakReference<NetItemObserver>? = null
init {
@ -117,7 +118,9 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
private fun updateAndSync() {
updateNetItems()
// syncs the internal buffer to the client
sync()
// syncs the open container (if any) to the client
netItemObserver?.get()?.netItemsChanged()
}
@ -153,14 +156,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
finishable.forEach(::stackLocateRequestCompleted)
}
fun addObserver() {
observers++
}
fun removeObserver() {
observers--
}
override fun tick() {
super.tick()
@ -171,10 +166,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
if (counter % 20 == 0L && !world!!.isClient) {
beginInsertions()
if (observers > 0) {
updateAndSync()
}
}
}
@ -197,7 +188,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
}
player.openHandledScreen(factory)
}
addObserver()
}
fun requestItem(stack: ItemStack, amount: Int = stack.count) {

View File

@ -53,7 +53,6 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
var searchQuery = ""
var scrollPosition = 0.0
var sortMode = SortMode.COUNT_HIGH_FIRST
init {
backgroundWidth = 252
@ -72,7 +71,7 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
fun requestUpdatedItems() {
val player = MinecraftClient.getInstance().player!!
player.networkHandler.sendPacket(C2STerminalUpdateDisplayedItems(handler.terminal, searchQuery, sortMode, scrollPosition.toFloat()))
player.networkHandler.sendPacket(C2STerminalUpdateDisplayedItems(handler.terminal, searchQuery, scrollPosition.toFloat()))
}
private fun showRequestAmountDialog(stack: ItemStack) {

View File

@ -10,11 +10,13 @@ import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.DefaultPlugin
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.TerminalSettings
import net.shadowfacts.phycon.util.copyWithCount
import java.lang.ref.WeakReference
import kotlin.math.ceil
@ -25,7 +27,11 @@ import kotlin.math.roundToInt
/**
* @author shadowfacts
*/
class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL, syncId),
class TerminalScreenHandler(
syncId: Int,
val playerInv: PlayerInventory,
val terminal: TerminalBlockEntity,
): ScreenHandler(PhyScreens.TERMINAL, syncId),
TerminalBlockEntity.NetItemObserver {
companion object {
@ -36,8 +42,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
private val fakeInv = FakeInventory(this)
private var searchQuery: String = ""
var sortMode = SortMode.COUNT_HIGH_FIRST
private set
private var settings = TerminalSettings()
var totalEntries = 0
private set
var scrollPosition = 0f
@ -54,12 +59,17 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
private set
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
this(syncId, playerInv, PhyBlocks.TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!)
this(
syncId,
playerInv,
PhyBlocks.TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!
)
init {
if (!terminal.world!!.isClient) {
assert(terminal.netItemObserver?.get() === null)
terminal.netItemObserver = WeakReference(this)
netItemsChanged()
// intentionally don't call netItemsChanged immediately, we need to wait for the client to send us its settings
}
// network
@ -107,7 +117,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
totalEntries = filtered.size
val sorted =
when (sortMode) {
when (settings[DefaultPlugin.SORT_MODE]) {
SortMode.COUNT_HIGH_FIRST -> filtered.sortedByDescending { it.intValue }
SortMode.COUNT_LOW_FIRST -> filtered.sortedBy { it.intValue }
SortMode.ALPHABETICAL -> filtered.sortedBy { it.key.name.string }
@ -120,7 +130,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
// itemEntries = sorted.map { Entry(it.key, it.intValue) }
(player as ServerPlayerEntity).networkHandler.sendPacket(S2CTerminalUpdateDisplayedItems(terminal, itemEntries, searchQuery, sortMode, scrollPosition, totalEntries))
(player as ServerPlayerEntity).networkHandler.sendPacket(S2CTerminalUpdateDisplayedItems(terminal, itemEntries, searchQuery, settings, scrollPosition, totalEntries))
}
fun totalRows(): Int {
@ -139,18 +149,17 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
return currentScrollOffsetInRows() * 9
}
fun sendUpdatedItemsToClient(player: ServerPlayerEntity, query: String, sortMode: SortMode, scrollPosition: Float) {
fun sendUpdatedItemsToClient(player: ServerPlayerEntity, query: String, settings: TerminalSettings, scrollPosition: Float) {
this.searchQuery = query
this.sortMode = sortMode
this.settings = settings
this.scrollPosition = scrollPosition
netItemsChanged()
}
fun receivedUpdatedItemsFromServer(entries: List<Entry>, query: String, sortMode: SortMode, scrollPosition: Float, totalEntries: Int) {
fun receivedUpdatedItemsFromServer(entries: List<Entry>, query: String, scrollPosition: Float, totalEntries: Int) {
assert(playerInv.player.world.isClient)
this.searchQuery = query
this.sortMode = sortMode
this.scrollPosition = scrollPosition
this.totalEntries = totalEntries
itemEntries = entries
@ -163,7 +172,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
override fun close(player: PlayerEntity) {
super.close(player)
terminal.removeObserver()
terminal.netItemObserver = null
}
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {

View File

@ -2,15 +2,18 @@ package net.shadowfacts.phycon.block.terminal
import net.minecraft.text.TranslatableText
import net.minecraft.util.math.MathHelper
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.View
import net.shadowfacts.cacao.view.textfield.TextField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.TerminalSettings
/**
* @author shadowfacts
@ -22,7 +25,7 @@ class TerminalViewController(
): ViewController() {
private lateinit var scrollTrack: ScrollTrackView
lateinit var sortMode: SortModeButton
lateinit var settingsView: View
private set
lateinit var searchField: TextField
private set
@ -84,8 +87,12 @@ class TerminalViewController(
scrollTrack = view.addSubview(ScrollTrackView(::scrollPositionChanged))
sortMode = view.addSubview(SortModeButton(SortMode.COUNT_HIGH_FIRST)).apply {
handler = ::sortModeChanged
val settingsStack = view.addSubview(StackView(Axis.VERTICAL, spacing = 2.0))
settingsView = settingsStack
TerminalSettings.allKeys.forEach { key ->
val button = SettingButton(key)
button.handler = { settingsChanged() }
settingsStack.addArrangedSubview(button)
}
view.solver.dsl {
@ -108,10 +115,8 @@ class TerminalViewController(
scrollTrack.bottomAnchor equalTo (network.bottomAnchor - 1)
scrollTrack.widthAnchor equalTo 12
sortMode.leftAnchor equalTo pane.rightAnchor + 4
sortMode.topAnchor equalTo pane.topAnchor
sortMode.widthAnchor equalTo 20
sortMode.heightAnchor equalTo 20
settingsStack.leftAnchor equalTo (pane.rightAnchor + 4)
settingsStack.topAnchor equalTo pane.topAnchor
}
}
@ -131,8 +136,7 @@ class TerminalViewController(
}
}
private fun sortModeChanged(button: SortModeButton) {
screen.sortMode = button.value
private fun settingsChanged() {
screen.requestUpdatedItems()
}

View File

@ -15,6 +15,8 @@ import net.minecraft.util.registry.RegistryKey
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.util.copyWithCount
import net.shadowfacts.phycon.util.readItemStackWithoutCount
import net.shadowfacts.phycon.util.writeItemStackWithoutCount
/**
* @author shadowfacts
@ -28,10 +30,10 @@ object C2STerminalRequestItem: ServerReceiver {
buf.writeIdentifier(terminal.world!!.registryKey.value)
buf.writeBlockPos(terminal.pos)
// Force the count of the stack to be 1 before serializing for the network because
// Don't send the count of the fake stack when sending over the network because
// PacketByteBuf serializes the stack count as a byte. Otherwise counts > 127 will result in
// an overflow and be negative on the receiving side.
buf.writeItemStack(stack.copyWithCount(1))
buf.writeItemStackWithoutCount(stack)
buf.writeVarInt(amount)
return ClientPlayNetworking.createC2SPacket(CHANNEL, buf)
@ -40,7 +42,7 @@ object C2STerminalRequestItem: ServerReceiver {
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
val dimID = buf.readIdentifier()
val pos = buf.readBlockPos()
val stack = buf.readItemStack()
val stack = buf.readItemStackWithoutCount()
val amount = buf.readVarInt()
server.execute {

View File

@ -10,9 +10,11 @@ import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.PhysicalConnectivityClient
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.TerminalSettings
/**
* @author shadowfacts
@ -21,14 +23,14 @@ object C2STerminalUpdateDisplayedItems: ServerReceiver {
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "terminal_update_displayed")
operator fun invoke(terminal: TerminalBlockEntity, query: String, sortMode: SortMode, scrollPosition: Float): Packet<*> {
operator fun invoke(terminal: TerminalBlockEntity, query: String, scrollPosition: Float): Packet<*> {
val buf = PacketByteBufs.create()
buf.writeIdentifier(terminal.world!!.registryKey.value)
buf.writeBlockPos(terminal.pos)
buf.writeString(query)
buf.writeVarInt(sortMode.ordinal)
buf.writeCompoundTag(PhysicalConnectivityClient.terminalSettings.toTag())
buf.writeFloat(scrollPosition)
return ClientPlayNetworking.createC2SPacket(CHANNEL, buf)
@ -38,7 +40,8 @@ object C2STerminalUpdateDisplayedItems: ServerReceiver {
val dimID = buf.readIdentifier()
val pos = buf.readBlockPos()
val query = buf.readString()
val sortMode = SortMode.values()[buf.readVarInt()]
val settings = TerminalSettings()
settings.fromTag(buf.readCompoundTag()!!)
val scrollPosition = buf.readFloat()
server.execute {
@ -46,7 +49,7 @@ object C2STerminalUpdateDisplayedItems: ServerReceiver {
val screenHandler = player.currentScreenHandler
if (screenHandler !is TerminalScreenHandler) return@execute
if (screenHandler.terminal.pos != pos) return@execute
screenHandler.sendUpdatedItemsToClient(player, query, sortMode, scrollPosition)
screenHandler.sendUpdatedItemsToClient(player, query, settings, scrollPosition)
}
}
}

View File

@ -7,9 +7,13 @@ import net.minecraft.client.MinecraftClient
import net.minecraft.client.network.ClientPlayNetworkHandler
import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf
import net.shadowfacts.phycon.PhysicalConnectivityClient
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.TerminalSettings
import net.shadowfacts.phycon.util.readItemStackWithoutCount
import net.shadowfacts.phycon.util.writeItemStackWithoutCount
/**
* @author shadowfacts
@ -17,7 +21,7 @@ import net.shadowfacts.phycon.util.SortMode
object S2CTerminalUpdateDisplayedItems: ClientReceiver {
override val CHANNEL = C2STerminalUpdateDisplayedItems.CHANNEL
operator fun invoke(terminal: TerminalBlockEntity, entries: List<TerminalScreenHandler.Entry>, query: String, sortMode: SortMode, scrollPosition: Float, totalEntries: Int): Packet<*> {
operator fun invoke(terminal: TerminalBlockEntity, entries: List<TerminalScreenHandler.Entry>, query: String, settings: TerminalSettings, scrollPosition: Float, totalEntries: Int): Packet<*> {
val buf = PacketByteBufs.create()
buf.writeIdentifier(terminal.world!!.registryKey.value)
@ -25,12 +29,12 @@ object S2CTerminalUpdateDisplayedItems: ClientReceiver {
buf.writeVarInt(entries.size)
for (e in entries) {
buf.writeItemStack(e.stack)
buf.writeItemStackWithoutCount(e.stack)
buf.writeVarInt(e.amount)
}
buf.writeString(query)
buf.writeVarInt(sortMode.ordinal)
buf.writeCompoundTag(settings.toTag())
buf.writeFloat(scrollPosition)
buf.writeInt(totalEntries)
@ -43,10 +47,10 @@ object S2CTerminalUpdateDisplayedItems: ClientReceiver {
val entryCount = buf.readVarInt()
val entries = ArrayList<TerminalScreenHandler.Entry>(entryCount)
for (i in 0 until entryCount) {
entries.add(TerminalScreenHandler.Entry(buf.readItemStack(), buf.readVarInt()))
entries.add(TerminalScreenHandler.Entry(buf.readItemStackWithoutCount(), buf.readVarInt()))
}
val query = buf.readString()
val sortMode = SortMode.values()[buf.readVarInt()]
PhysicalConnectivityClient.terminalSettings.fromTag(buf.readCompoundTag()!!)
val scrollPosition = buf.readFloat()
val totalEntries = buf.readInt()
@ -55,7 +59,7 @@ object S2CTerminalUpdateDisplayedItems: ClientReceiver {
val screenHandler = client.player!!.currentScreenHandler
if (screenHandler !is TerminalScreenHandler) return@execute
if (screenHandler.terminal.pos != pos) return@execute
screenHandler.receivedUpdatedItemsFromServer(entries, query, sortMode, scrollPosition, totalEntries)
screenHandler.receivedUpdatedItemsFromServer(entries, query, scrollPosition, totalEntries)
}
}
}

View File

@ -0,0 +1,37 @@
package net.shadowfacts.phycon.util
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.PacketByteBuf
/**
* @author shadowfacts
*/
fun PacketByteBuf.writeItemStackWithoutCount(stack: ItemStack) {
if (stack.isEmpty) {
writeBoolean(false)
} else {
writeBoolean(true)
writeVarInt(Item.getRawId(stack.item))
val tag: CompoundTag? =
if (stack.item.isDamageable || stack.item.shouldSyncTagToClient()) {
stack.tag
} else {
null
}
writeCompoundTag(tag)
}
}
fun PacketByteBuf.readItemStackWithoutCount(): ItemStack {
return if (!readBoolean()) {
ItemStack.EMPTY
} else {
val id = readVarInt()
val count = 1
val stack = ItemStack(Item.byRawId(id), count)
stack.tag = readCompoundTag()
stack
}
}

View File

@ -2,17 +2,27 @@ package net.shadowfacts.phycon.util
import net.minecraft.text.LiteralText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.TerminalSetting
/**
* @author shadowfacts
*/
enum class SortMode: RotatableEnum {
enum class SortMode: TerminalSetting {
COUNT_HIGH_FIRST,
COUNT_LOW_FIRST,
ALPHABETICAL;
val tooltip: Text
get() = when (this) {
override fun getIconTexture() = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png")
override fun getUV() = when (this) {
COUNT_HIGH_FIRST -> intArrayOf(0, 230)
COUNT_LOW_FIRST -> intArrayOf(16, 230)
ALPHABETICAL -> intArrayOf(32, 230)
}
override fun getTooltip() = when (this) {
COUNT_HIGH_FIRST -> LiteralText("Count, highest first")
COUNT_LOW_FIRST -> LiteralText("Count, lowest first")
ALPHABETICAL -> LiteralText("Name")

View File

@ -0,0 +1,83 @@
package net.shadowfacts.phycon.util
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivityClient
import net.shadowfacts.phycon.api.TerminalSettingKey
import net.shadowfacts.phycon.api.TerminalSetting
/**
* @author shadowfacts
*/
class TerminalSettings {
companion object {
private val SETTINGS = mutableMapOf<Identifier, SettingKey<*>>()
private val DEFAULTS = mutableMapOf<TerminalSettingKey<*>, Enum<*>>()
val allKeys: Collection<SettingKey<*>>
get() = SETTINGS.values
fun <E> register(id: Identifier, defaultValue: E): SettingKey<E> where E: Enum<E>, E: TerminalSetting {
val setting = SettingKey(id, defaultValue.javaClass)
SETTINGS[id] = setting
DEFAULTS[setting] = defaultValue
return setting
}
fun <E> getDefault(setting: TerminalSettingKey<E>): E where E: Enum<E>, E: TerminalSetting {
@Suppress("UNCHECKED_CAST")
return DEFAULTS[setting] as E
}
}
class SettingKey<E>(
val id: Identifier,
val clazz: Class<E>,
): TerminalSettingKey<E> where E: Enum<E>, E: TerminalSetting {
fun value(ordinal: Int): E? {
@Suppress("UNCHECKED_CAST")
return if (clazz.enumConstants.size <= ordinal) null
else clazz.enumConstants[ordinal] as E
}
override fun getID() = id
override fun getValue() = PhysicalConnectivityClient.terminalSettings[this]
}
private val settings = mutableMapOf<TerminalSettingKey<*>, Enum<*>>()
operator fun <E> get(key: TerminalSettingKey<E>): E where E: Enum<E>, E: TerminalSetting {
@Suppress("UNCHECKED_CAST")
return settings[key] as? E ?: getDefault(key)
}
operator fun <E> set(key: SettingKey<E>, value: E) where E: Enum<E>, E: TerminalSetting {
settings[key] = value
}
fun toTag(): CompoundTag {
return CompoundTag().also {
settings.forEach { (s, v) ->
it.putInt(s.id.toString(), v.ordinal)
}
}
}
fun fromTag(tag: CompoundTag) {
settings.clear()
tag.keys.forEach { k ->
val id = Identifier.tryParse(k) ?: return@forEach
val setting = SETTINGS[id] ?: return@forEach
setValueFromOrdinal(setting, tag.getInt(k))
}
}
private fun <E> setValueFromOrdinal(setting: SettingKey<E>, ordinal: Int) where E: Enum<E>, E: TerminalSetting {
val value = setting.value(ordinal)
if (value != null) {
this[setting] = value
}
}
}

View File

@ -35,6 +35,12 @@
"adapter": "kotlin",
"value": "net.shadowfacts.phycon.PhysicalConnectivityClient"
}
],
"phycon": [
{
"adapter": "kotlin",
"value": "net.shadowfacts.phycon.DefaultPlugin"
}
]
},
"mixins": [