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

View File

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

View File

@ -34,8 +34,8 @@ object PhyConPlugin: ClientModInitializer, REIPluginV0 {
override fun registerBounds(helper: DisplayHelper) { override fun registerBounds(helper: DisplayHelper) {
BaseBoundsHandler.getInstance().registerExclusionZones(TerminalScreen::class.java) { BaseBoundsHandler.getInstance().registerExclusionZones(TerminalScreen::class.java) {
val screen = MinecraftClient.getInstance().currentScreen as TerminalScreen val screen = MinecraftClient.getInstance().currentScreen as TerminalScreen
val button = screen.terminalVC.sortMode val view = screen.terminalVC.settingsView
val rect = button.convert(button.bounds, to = null) val rect = view.convert(view.bounds, to = null)
listOf( listOf(
Rectangle(rect.left.toInt(), rect.top.toInt(), 20, 20) 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("kiwi-java")
include("plugin:mousewheelie") include("plugin:mousewheelie")
include("plugin:rei") 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.util.RenderHelper
import net.shadowfacts.cacao.window.ScreenHandlerWindow import net.shadowfacts.cacao.window.ScreenHandlerWindow
import net.shadowfacts.cacao.window.Window import net.shadowfacts.cacao.window.Window
import org.lwjgl.glfw.GLFW
import java.util.* import java.util.*
/** /**
@ -48,6 +49,9 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
override fun removeWindow(window: Window) { override fun removeWindow(window: Window) {
_windows.remove(window) _windows.remove(window)
if (windows.isEmpty()) {
onClose()
}
} }
override fun init() { override fun init() {
@ -151,12 +155,17 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
} }
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { 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) } val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) { if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {
return true return true
} }
return super.keyPressed(keyCode, scanCode, modifiers) return super.keyPressed(keyCode, scanCode, modifiers)
} }
}
override fun charTyped(char: Char, modifiers: Int): Boolean { override fun charTyped(char: Char, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) } val modifiersSet by lazy { KeyModifiers(modifiers) }
@ -166,4 +175,8 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
return super.charTyped(char, modifiers) 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.MouseButton
import net.shadowfacts.cacao.util.RenderHelper import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.window.Window import net.shadowfacts.cacao.window.Window
import org.lwjgl.glfw.GLFW
import java.util.* import java.util.*
/** /**
@ -60,7 +61,9 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
*/ */
override fun removeWindow(window: Window) { override fun removeWindow(window: Window) {
_windows.remove(window) _windows.remove(window)
// todo: VC callbacks if (windows.isEmpty()) {
onClose()
}
} }
override fun init() { 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 { 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) } val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) { if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {
return true return true
} }
return super.keyPressed(keyCode, scanCode, modifiers) return super.keyPressed(keyCode, scanCode, modifiers)
} }
}
override fun keyReleased(i: Int, j: Int, k: Int): Boolean { override fun keyReleased(i: Int, j: Int, k: Int): Boolean {
return super.keyReleased(i, j, k) return super.keyReleased(i, j, k)
@ -145,6 +153,10 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
return super.charTyped(char, modifiers) return super.charTyped(char, modifiers)
} }
override fun shouldCloseOnEsc(): Boolean {
return false
}
} }
fun AbstractCacaoScreen.findResponder(fn: (Responder) -> Boolean): Boolean { fun AbstractCacaoScreen.findResponder(fn: (Responder) -> Boolean): Boolean {

View File

@ -11,10 +11,12 @@ import net.shadowfacts.cacao.util.texture.Texture
* *
* @author shadowfacts * @author shadowfacts
*/ */
class TextureView(var texture: Texture): View() { class TextureView(var texture: Texture?): View() {
override fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) { 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.api.ModInitializer
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking 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.PhyBlockEntities
import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyItems import net.shadowfacts.phycon.init.PhyItems
@ -27,6 +29,10 @@ object PhysicalConnectivity: ModInitializer {
registerGlobalReceiver(C2STerminalRequestItem) registerGlobalReceiver(C2STerminalRequestItem)
registerGlobalReceiver(C2STerminalUpdateDisplayedItems) registerGlobalReceiver(C2STerminalUpdateDisplayedItems)
registerGlobalReceiver(C2SConfigureDevice) registerGlobalReceiver(C2SConfigureDevice)
for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) {
it.initializePhyCon(PhyConAPIImpl)
}
} }
private fun registerGlobalReceiver(receiver: ServerReceiver) { 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.client.PhyModelProvider
import net.shadowfacts.phycon.networking.ClientReceiver import net.shadowfacts.phycon.networking.ClientReceiver
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.TerminalSettings
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
object PhysicalConnectivityClient: ClientModInitializer { object PhysicalConnectivityClient: ClientModInitializer {
val terminalSettings = TerminalSettings()
var screenMaterial: RenderMaterial? = null var screenMaterial: RenderMaterial? = null
private set 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 private var observers = 0
val cachedNetItems = ItemStackCollections.intMap() val cachedNetItems = ItemStackCollections.intMap()
// todo: multiple players could have the terminal open simultaneously
var netItemObserver: WeakReference<NetItemObserver>? = null var netItemObserver: WeakReference<NetItemObserver>? = null
init { init {
@ -117,7 +118,9 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
private fun updateAndSync() { private fun updateAndSync() {
updateNetItems() updateNetItems()
// syncs the internal buffer to the client
sync() sync()
// syncs the open container (if any) to the client
netItemObserver?.get()?.netItemsChanged() netItemObserver?.get()?.netItemsChanged()
} }
@ -153,14 +156,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
finishable.forEach(::stackLocateRequestCompleted) finishable.forEach(::stackLocateRequestCompleted)
} }
fun addObserver() {
observers++
}
fun removeObserver() {
observers--
}
override fun tick() { override fun tick() {
super.tick() super.tick()
@ -171,10 +166,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
if (counter % 20 == 0L && !world!!.isClient) { if (counter % 20 == 0L && !world!!.isClient) {
beginInsertions() beginInsertions()
if (observers > 0) {
updateAndSync()
}
} }
} }
@ -197,7 +188,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
} }
player.openHandledScreen(factory) player.openHandledScreen(factory)
} }
addObserver()
} }
fun requestItem(stack: ItemStack, amount: Int = stack.count) { fun requestItem(stack: ItemStack, amount: Int = stack.count) {

View File

@ -53,7 +53,6 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
var searchQuery = "" var searchQuery = ""
var scrollPosition = 0.0 var scrollPosition = 0.0
var sortMode = SortMode.COUNT_HIGH_FIRST
init { init {
backgroundWidth = 252 backgroundWidth = 252
@ -72,7 +71,7 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
fun requestUpdatedItems() { fun requestUpdatedItems() {
val player = MinecraftClient.getInstance().player!! 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) { 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.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.DefaultPlugin
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.SortMode import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.TerminalSettings
import net.shadowfacts.phycon.util.copyWithCount import net.shadowfacts.phycon.util.copyWithCount
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import kotlin.math.ceil import kotlin.math.ceil
@ -25,7 +27,11 @@ import kotlin.math.roundToInt
/** /**
* @author shadowfacts * @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 { TerminalBlockEntity.NetItemObserver {
companion object { companion object {
@ -36,8 +42,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
private val fakeInv = FakeInventory(this) private val fakeInv = FakeInventory(this)
private var searchQuery: String = "" private var searchQuery: String = ""
var sortMode = SortMode.COUNT_HIGH_FIRST private var settings = TerminalSettings()
private set
var totalEntries = 0 var totalEntries = 0
private set private set
var scrollPosition = 0f var scrollPosition = 0f
@ -54,12 +59,17 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
private set private set
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf): 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 { init {
if (!terminal.world!!.isClient) { if (!terminal.world!!.isClient) {
assert(terminal.netItemObserver?.get() === null)
terminal.netItemObserver = WeakReference(this) terminal.netItemObserver = WeakReference(this)
netItemsChanged() // intentionally don't call netItemsChanged immediately, we need to wait for the client to send us its settings
} }
// network // network
@ -107,7 +117,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
totalEntries = filtered.size totalEntries = filtered.size
val sorted = val sorted =
when (sortMode) { when (settings[DefaultPlugin.SORT_MODE]) {
SortMode.COUNT_HIGH_FIRST -> filtered.sortedByDescending { it.intValue } SortMode.COUNT_HIGH_FIRST -> filtered.sortedByDescending { it.intValue }
SortMode.COUNT_LOW_FIRST -> filtered.sortedBy { it.intValue } SortMode.COUNT_LOW_FIRST -> filtered.sortedBy { it.intValue }
SortMode.ALPHABETICAL -> filtered.sortedBy { it.key.name.string } 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) } // 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 { fun totalRows(): Int {
@ -139,18 +149,17 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
return currentScrollOffsetInRows() * 9 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.searchQuery = query
this.sortMode = sortMode this.settings = settings
this.scrollPosition = scrollPosition this.scrollPosition = scrollPosition
netItemsChanged() 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) assert(playerInv.player.world.isClient)
this.searchQuery = query this.searchQuery = query
this.sortMode = sortMode
this.scrollPosition = scrollPosition this.scrollPosition = scrollPosition
this.totalEntries = totalEntries this.totalEntries = totalEntries
itemEntries = entries itemEntries = entries
@ -163,7 +172,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
override fun close(player: PlayerEntity) { override fun close(player: PlayerEntity) {
super.close(player) super.close(player)
terminal.removeObserver() terminal.netItemObserver = null
} }
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack { 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.text.TranslatableText
import net.minecraft.util.math.MathHelper import net.minecraft.util.math.MathHelper
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.view.Label import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.view.View
import net.shadowfacts.cacao.view.textfield.TextField import net.shadowfacts.cacao.view.textfield.TextField
import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.util.SortMode import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.TerminalSettings
/** /**
* @author shadowfacts * @author shadowfacts
@ -22,7 +25,7 @@ class TerminalViewController(
): ViewController() { ): ViewController() {
private lateinit var scrollTrack: ScrollTrackView private lateinit var scrollTrack: ScrollTrackView
lateinit var sortMode: SortModeButton lateinit var settingsView: View
private set private set
lateinit var searchField: TextField lateinit var searchField: TextField
private set private set
@ -84,8 +87,12 @@ class TerminalViewController(
scrollTrack = view.addSubview(ScrollTrackView(::scrollPositionChanged)) scrollTrack = view.addSubview(ScrollTrackView(::scrollPositionChanged))
sortMode = view.addSubview(SortModeButton(SortMode.COUNT_HIGH_FIRST)).apply { val settingsStack = view.addSubview(StackView(Axis.VERTICAL, spacing = 2.0))
handler = ::sortModeChanged settingsView = settingsStack
TerminalSettings.allKeys.forEach { key ->
val button = SettingButton(key)
button.handler = { settingsChanged() }
settingsStack.addArrangedSubview(button)
} }
view.solver.dsl { view.solver.dsl {
@ -108,10 +115,8 @@ class TerminalViewController(
scrollTrack.bottomAnchor equalTo (network.bottomAnchor - 1) scrollTrack.bottomAnchor equalTo (network.bottomAnchor - 1)
scrollTrack.widthAnchor equalTo 12 scrollTrack.widthAnchor equalTo 12
sortMode.leftAnchor equalTo pane.rightAnchor + 4 settingsStack.leftAnchor equalTo (pane.rightAnchor + 4)
sortMode.topAnchor equalTo pane.topAnchor settingsStack.topAnchor equalTo pane.topAnchor
sortMode.widthAnchor equalTo 20
sortMode.heightAnchor equalTo 20
} }
} }
@ -131,8 +136,7 @@ class TerminalViewController(
} }
} }
private fun sortModeChanged(button: SortModeButton) { private fun settingsChanged() {
screen.sortMode = button.value
screen.requestUpdatedItems() screen.requestUpdatedItems()
} }

View File

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

View File

@ -10,9 +10,11 @@ import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.PhysicalConnectivityClient
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.TerminalSettings
/** /**
* @author shadowfacts * @author shadowfacts
@ -21,14 +23,14 @@ object C2STerminalUpdateDisplayedItems: ServerReceiver {
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "terminal_update_displayed") 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() val buf = PacketByteBufs.create()
buf.writeIdentifier(terminal.world!!.registryKey.value) buf.writeIdentifier(terminal.world!!.registryKey.value)
buf.writeBlockPos(terminal.pos) buf.writeBlockPos(terminal.pos)
buf.writeString(query) buf.writeString(query)
buf.writeVarInt(sortMode.ordinal) buf.writeCompoundTag(PhysicalConnectivityClient.terminalSettings.toTag())
buf.writeFloat(scrollPosition) buf.writeFloat(scrollPosition)
return ClientPlayNetworking.createC2SPacket(CHANNEL, buf) return ClientPlayNetworking.createC2SPacket(CHANNEL, buf)
@ -38,7 +40,8 @@ object C2STerminalUpdateDisplayedItems: ServerReceiver {
val dimID = buf.readIdentifier() val dimID = buf.readIdentifier()
val pos = buf.readBlockPos() val pos = buf.readBlockPos()
val query = buf.readString() val query = buf.readString()
val sortMode = SortMode.values()[buf.readVarInt()] val settings = TerminalSettings()
settings.fromTag(buf.readCompoundTag()!!)
val scrollPosition = buf.readFloat() val scrollPosition = buf.readFloat()
server.execute { server.execute {
@ -46,7 +49,7 @@ object C2STerminalUpdateDisplayedItems: ServerReceiver {
val screenHandler = player.currentScreenHandler val screenHandler = player.currentScreenHandler
if (screenHandler !is TerminalScreenHandler) return@execute if (screenHandler !is TerminalScreenHandler) return@execute
if (screenHandler.terminal.pos != pos) 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.client.network.ClientPlayNetworkHandler
import net.minecraft.network.Packet import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf import net.minecraft.network.PacketByteBuf
import net.shadowfacts.phycon.PhysicalConnectivityClient
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode 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 * @author shadowfacts
@ -17,7 +21,7 @@ import net.shadowfacts.phycon.util.SortMode
object S2CTerminalUpdateDisplayedItems: ClientReceiver { object S2CTerminalUpdateDisplayedItems: ClientReceiver {
override val CHANNEL = C2STerminalUpdateDisplayedItems.CHANNEL 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() val buf = PacketByteBufs.create()
buf.writeIdentifier(terminal.world!!.registryKey.value) buf.writeIdentifier(terminal.world!!.registryKey.value)
@ -25,12 +29,12 @@ object S2CTerminalUpdateDisplayedItems: ClientReceiver {
buf.writeVarInt(entries.size) buf.writeVarInt(entries.size)
for (e in entries) { for (e in entries) {
buf.writeItemStack(e.stack) buf.writeItemStackWithoutCount(e.stack)
buf.writeVarInt(e.amount) buf.writeVarInt(e.amount)
} }
buf.writeString(query) buf.writeString(query)
buf.writeVarInt(sortMode.ordinal) buf.writeCompoundTag(settings.toTag())
buf.writeFloat(scrollPosition) buf.writeFloat(scrollPosition)
buf.writeInt(totalEntries) buf.writeInt(totalEntries)
@ -43,10 +47,10 @@ object S2CTerminalUpdateDisplayedItems: ClientReceiver {
val entryCount = buf.readVarInt() val entryCount = buf.readVarInt()
val entries = ArrayList<TerminalScreenHandler.Entry>(entryCount) val entries = ArrayList<TerminalScreenHandler.Entry>(entryCount)
for (i in 0 until 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 query = buf.readString()
val sortMode = SortMode.values()[buf.readVarInt()] PhysicalConnectivityClient.terminalSettings.fromTag(buf.readCompoundTag()!!)
val scrollPosition = buf.readFloat() val scrollPosition = buf.readFloat()
val totalEntries = buf.readInt() val totalEntries = buf.readInt()
@ -55,7 +59,7 @@ object S2CTerminalUpdateDisplayedItems: ClientReceiver {
val screenHandler = client.player!!.currentScreenHandler val screenHandler = client.player!!.currentScreenHandler
if (screenHandler !is TerminalScreenHandler) return@execute if (screenHandler !is TerminalScreenHandler) return@execute
if (screenHandler.terminal.pos != pos) 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.LiteralText
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.TerminalSetting
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
enum class SortMode: RotatableEnum { enum class SortMode: TerminalSetting {
COUNT_HIGH_FIRST, COUNT_HIGH_FIRST,
COUNT_LOW_FIRST, COUNT_LOW_FIRST,
ALPHABETICAL; ALPHABETICAL;
val tooltip: Text override fun getIconTexture() = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png")
get() = when (this) {
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_HIGH_FIRST -> LiteralText("Count, highest first")
COUNT_LOW_FIRST -> LiteralText("Count, lowest first") COUNT_LOW_FIRST -> LiteralText("Count, lowest first")
ALPHABETICAL -> LiteralText("Name") 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", "adapter": "kotlin",
"value": "net.shadowfacts.phycon.PhysicalConnectivityClient" "value": "net.shadowfacts.phycon.PhysicalConnectivityClient"
} }
],
"phycon": [
{
"adapter": "kotlin",
"value": "net.shadowfacts.phycon.DefaultPlugin"
}
] ]
}, },
"mixins": [ "mixins": [