Re-implement DelegateSyncher using Mojang's StreamCodec

This commit is contained in:
DBotThePony 2024-08-10 23:26:17 +07:00
parent 03cec07ffb
commit aa5d36c488
Signed by: DBot
GPG Key ID: DCC23B5715498507
21 changed files with 1177 additions and 200 deletions

View File

@ -1,16 +1,15 @@
package ru.dbotthepony.mc.otm.android
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
import net.neoforged.neoforge.common.util.INBTSerializable
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.capability.MatteryPlayer
import ru.dbotthepony.mc.otm.core.nbt.set
import java.io.InputStream
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayer) : INBTSerializable<CompoundTag> {
val ply get() = android.ply
@ -39,15 +38,6 @@ abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: Matt
open fun onHurt(event: LivingIncomingDamageEvent) {}
open fun collectNetworkPayload(): FastByteArrayOutputStream? {
syncher.observe()
return syncherRemote.write()
}
open fun applyNetworkPayload(stream: InputStream) {
syncher.read(stream)
}
override fun serializeNBT(registry: HolderLookup.Provider): CompoundTag {
return CompoundTag().also {
it["level"] = level

View File

@ -10,7 +10,7 @@ import net.minecraft.world.entity.player.Player
import net.neoforged.bus.api.Event
import net.neoforged.neoforge.common.NeoForge
import net.neoforged.neoforge.common.util.INBTSerializable
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.OverdriveThatMatters
@ -173,15 +173,6 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
return true
}
fun collectNetworkPayload(): FastByteArrayOutputStream? {
syncher.observe()
return syncherRemote.write()
}
fun applyNetworkPayload(stream: InputStream) {
syncher.read(stream)
}
val canResearch: Boolean get() {
if (!consumeResearchCost(simulate = true)) {
return false

View File

@ -88,7 +88,7 @@ object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().s
if (SERVER_IS_LIVE) {
onceServer {
for (ply in MINECRAFT_SERVER.playerList.players) {
ply.matteryPlayer.reloadResearch()
ply.matteryPlayer.reloadResearch(MINECRAFT_SERVER.registryAccess())
}
}
}
@ -134,7 +134,7 @@ object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().s
}
context.enqueueWork {
minecraft.player?.matteryPlayer?.reloadResearch()
minecraft.player?.matteryPlayer?.reloadResearch(MINECRAFT_SERVER.registryAccess())
}
}

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.block.entity
import com.google.common.collect.ImmutableList
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
@ -37,9 +36,8 @@ import net.neoforged.neoforge.event.server.ServerStoppingEvent
import net.neoforged.neoforge.event.tick.LevelTickEvent
import net.neoforged.neoforge.network.PacketDistributor
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.Listenable
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
import ru.dbotthepony.mc.otm.block.INeighbourChangeListener
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.capability.MatteryCapability
@ -48,25 +46,20 @@ import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.util.IntCounter
import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.core.util.TickList
import ru.dbotthepony.mc.otm.core.util.countingLazy
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
import ru.dbotthepony.mc.otm.once
import ru.dbotthepony.mc.otm.onceServer
import ru.dbotthepony.mc.otm.sometimeServer
import java.lang.ref.WeakReference
import java.util.*
import java.util.function.BooleanSupplier
import java.util.function.Consumer
import java.util.function.Predicate
import java.util.function.Supplier
import java.util.stream.Stream
import kotlin.collections.ArrayList
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* Absolute barebone (lol) block entity class in Overdrive that Matters, providing bare minimum (lulmao, minecraft engine) functionality

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.neoforged.neoforge.common.util.INBTSerializable
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.Listenable
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue

View File

@ -16,6 +16,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.IntTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.StringTag
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
import net.minecraft.resources.ResourceLocation
@ -57,12 +58,7 @@ import org.apache.logging.log4j.LogManager
import org.joml.Vector4f
import ru.dbotthepony.kommons.collect.ListenableMap
import ru.dbotthepony.kommons.collect.ListenableSet
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.kommons.io.IntValueCodec
import ru.dbotthepony.kommons.io.RGBCodec
import ru.dbotthepony.kommons.io.UUIDValueCodec
import ru.dbotthepony.kommons.io.VarIntValueCodec
import ru.dbotthepony.kommons.io.nullable
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.ListenableDelegate
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
@ -103,7 +99,6 @@ import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
import ru.dbotthepony.mc.otm.core.nbt.getIntList
import ru.dbotthepony.mc.otm.core.nbt.getStringList
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.core.util.TickList
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
@ -235,7 +230,7 @@ class MatteryPlayer(val ply: Player) {
*/
var exopackGlows by publicSyncher.boolean(true)
var exopackColor by publicSyncher.add(ListenableDelegate.Box(null), RGBCodec.nullable())
var exopackColor by publicSyncher.add(ListenableDelegate.Box(null), StreamCodecs.RGBA.nullable())
/**
* Tick event schedulers
@ -249,8 +244,8 @@ class MatteryPlayer(val ply: Player) {
private val exopackSlotModifierMap = syncher.MapSlot(
ListenableMap<UUID, Int>().also { it.addListener(Runnable { _recomputeModifiers() }) },
keyCodec = UUIDValueCodec,
valueCodec = IntValueCodec,
keyCodec = StreamCodecs.UUID,
valueCodec = StreamCodecs.VAR_INT,
)
/**
@ -267,12 +262,12 @@ class MatteryPlayer(val ply: Player) {
}, backingMap = this.exopackSlotModifierMap.delegate)
val regularSlotFilters = immutableList(Inventory.INVENTORY_SIZE) {
syncher.add(null, ItemValueCodec.nullable())
syncher.add(null, StreamCodecs.ITEM_TYPE.nullable())
}
val slotsChargeFlag = syncher.SetSlot(
ListenableSet(IntAVLTreeSet()),
VarIntValueCodec,
StreamCodecs.VAR_INT,
).delegate
private fun slotChargeToDefault() {
@ -779,7 +774,7 @@ class MatteryPlayer(val ply: Player) {
}
}
internal fun reloadResearch() {
internal fun reloadResearch(registry: HolderLookup.Provider) {
val old = ArrayList<AndroidResearch>(research.size)
old.addAll(research.values)
research.clear()
@ -788,7 +783,7 @@ class MatteryPlayer(val ply: Player) {
val new = AndroidResearchManager.get(v.type.id) ?: continue
research[new] = AndroidResearch(new, this).also {
it.deserializeNBT(v.serializeNBT())
it.deserializeNBT(registry, v.serializeNBT(registry))
}
}
}
@ -808,10 +803,10 @@ class MatteryPlayer(val ply: Player) {
if (ply is ServerPlayer) {
feature.invalidateNetwork()
val payload = feature.collectNetworkPayload()
val payload = feature.syncherRemote.write(ply.registryAccess())
if (payload != null) {
sendNetwork(AndroidFeatureSyncPacket(feature.type, payload, null))
sendNetwork(AndroidFeatureSyncPacket(feature.type, payload))
}
}
@ -836,10 +831,10 @@ class MatteryPlayer(val ply: Player) {
if (ply is ServerPlayer) {
factory.invalidateNetwork()
val payload = factory.collectNetworkPayload()
val payload = factory.syncherRemote.write(ply.registryAccess())
if (payload != null) {
sendNetwork(AndroidFeatureSyncPacket(feature, payload, null))
sendNetwork(AndroidFeatureSyncPacket(feature, payload))
}
}
@ -942,7 +937,7 @@ class MatteryPlayer(val ply: Player) {
tag["research"] = ListTag().also {
for ((type, instance) in research) {
it.add(instance.serializeNBT().also {
it.add(instance.serializeNBT(registry).also {
it["id"] = type.id.toString()
})
}
@ -1031,7 +1026,7 @@ class MatteryPlayer(val ply: Player) {
if (research != null) {
val instance = AndroidResearch(research, this)
instance.deserializeNBT(researchTag)
instance.deserializeNBT(registry, researchTag)
this.research[research] = instance
if (instance.isResearched && ply is ServerPlayer) {
@ -1301,7 +1296,7 @@ class MatteryPlayer(val ply: Player) {
syncher.observe()
publicSyncher.observe()
val payload = privateSyncherRemote.write()
val payload = privateSyncherRemote.write(ply.registryAccess())
if (payload != null) {
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload, false))
@ -1315,14 +1310,14 @@ class MatteryPlayer(val ply: Player) {
continue
}
val payload2 = remote.write()
val payload2 = remote.write(ply.registryAccess())
if (payload2 != null) {
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload2, true, this.ply.uuid))
}
}
val payload3 = publicSyncherRemote.write()
val payload3 = publicSyncherRemote.write(ply.registryAccess())
if (payload3 != null) {
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload3, true))
@ -1337,18 +1332,18 @@ class MatteryPlayer(val ply: Player) {
}
for (instance in research.values) {
val researchPayload = instance.collectNetworkPayload()
val researchPayload = instance.syncherRemote.write(ply.registryAccess())
if (researchPayload != null) {
sendNetwork(AndroidResearchSyncPacket(instance.type, researchPayload, null))
sendNetwork(AndroidResearchSyncPacket(instance.type, researchPayload))
}
}
for (instance in featureMap.values) {
val featurePayload = instance.collectNetworkPayload()
val featurePayload = instance.syncherRemote.write(ply.registryAccess())
if (featurePayload != null) {
sendNetwork(AndroidFeatureSyncPacket(instance.type, featurePayload, null))
sendNetwork(AndroidFeatureSyncPacket(instance.type, featurePayload))
}
}

View File

@ -8,7 +8,7 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.ticks.ContainerSingleItem
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.common.util.INBTSerializable
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.capability.FlowDirection

View File

@ -24,7 +24,7 @@ import net.minecraft.world.item.Items
import net.neoforged.neoforge.common.util.INBTSerializable
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.collect.ListenableMap
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.io.VarIntValueCodec
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
import ru.dbotthepony.mc.otm.core.addSorted

View File

@ -1,46 +0,0 @@
package ru.dbotthepony.mc.otm.core.util
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.fluids.FluidStack
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.kommons.io.StreamCodec
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.DelegateGetter
import ru.dbotthepony.kommons.util.DelegateSetter
import ru.dbotthepony.kommons.util.ListenableDelegate
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.readDecimal
import ru.dbotthepony.mc.otm.core.math.writeDecimal
import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.writeItemType
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.*
import java.util.function.Supplier
val ItemStackValueCodec = StreamCodec.Impl(DataInputStream::readItem, DataOutputStream::writeItem, ItemStack::copy) { a, b -> a.equals(b, true) }
val FluidStackValueCodec = StreamCodec.Impl(DataInputStream::readFluidStack, DataOutputStream::writeFluidStack, FluidStack::copy) { a, b -> a == b && a.amount == b.amount }
val ItemValueCodec = StreamCodec.Impl(DataInputStream::readItemType, DataOutputStream::writeItemType) { a, b -> a === b }
val DecimalValueCodec = StreamCodec.Impl(DataInputStream::readDecimal, DataOutputStream::writeDecimal)
val BigDecimalValueCodec = StreamCodec.Impl(DataInputStream::readBigDecimal, DataOutputStream::writeBigDecimal)
val ResourceLocationValueCodec = StreamCodec.Impl(DataInputStream::readResourceLocation, DataOutputStream::writeResourceLocation)
fun DelegateSyncher.decimal(value: Decimal = Decimal.ZERO, setter: DelegateSetter<Decimal> = DelegateSetter.passthrough(), getter: DelegateGetter<Decimal> = DelegateGetter.passthrough()): DelegateSyncher.Slot<Decimal> {
return add(ListenableDelegate.maskSmart(value, getter, setter), DecimalValueCodec)
}
fun DelegateSyncher.computedDecimal(delegate: Supplier<Decimal>): DelegateSyncher.Slot<Decimal> {
return add(delegate, DecimalValueCodec)
}
fun DelegateSyncher.item(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): DelegateSyncher.Slot<ItemStack> {
return add(ListenableDelegate.maskSmart(value, getter, setter), ItemStackValueCodec)
}
fun DelegateSyncher.computedItem(delegate: Supplier<ItemStack>): DelegateSyncher.Slot<ItemStack> {
return computed(delegate, ItemStackValueCodec)
}
fun DelegateSyncher.observedItem(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): DelegateSyncher.ObservedSlot<ItemStack> {
return add(Delegate.maskSmart(value, getter, setter), ItemStackValueCodec)
}

View File

@ -2,14 +2,14 @@ package ru.dbotthepony.mc.otm.menu
import com.google.common.collect.ImmutableList
import com.mojang.datafixers.util.Pair
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntCollection
import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ReferenceArrayList
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
@ -23,17 +23,12 @@ import net.minecraft.world.inventory.MenuType
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.enchantment.EnchantmentEffectComponents
import net.minecraft.world.item.enchantment.EnchantmentHelper
import net.minecraft.world.item.enchantment.Enchantments
import net.minecraft.world.level.block.entity.BlockEntity
import net.neoforged.neoforge.network.PacketDistributor
import net.neoforged.neoforge.network.handling.IPayloadContext
import ru.dbotthepony.kommons.io.BinaryStringCodec
import ru.dbotthepony.kommons.io.BooleanValueCodec
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.kommons.io.FloatValueCodec
import ru.dbotthepony.kommons.io.NullValueCodec
import ru.dbotthepony.kommons.io.StreamCodec
import ru.dbotthepony.kommons.io.VarIntValueCodec
import ru.dbotthepony.kommons.io.nullable
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
@ -43,7 +38,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
@ -57,19 +51,19 @@ import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec
import ru.dbotthepony.mc.otm.core.util.DecimalValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemStackValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
import ru.dbotthepony.mc.otm.core.util.computedDecimal
import ru.dbotthepony.mc.otm.core.util.computedItem
import ru.dbotthepony.mc.otm.menu.input.InstantBooleanInput
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
import ru.dbotthepony.mc.otm.network.MenuDataPacket
import ru.dbotthepony.mc.otm.network.SetCarriedPacket
import java.io.DataInputStream
import java.io.DataOutputStream
import java.math.BigDecimal
import ru.dbotthepony.mc.otm.network.StreamCodecs
import ru.dbotthepony.mc.otm.network.decode
import ru.dbotthepony.mc.otm.network.encode
import ru.dbotthepony.mc.otm.network.nullable
import ru.dbotthepony.mc.otm.network.readByteListUnbounded
import ru.dbotthepony.mc.otm.network.wrap
import ru.dbotthepony.mc.otm.network.writeByteListUnbounded
import java.util.*
import java.util.function.BooleanSupplier
import java.util.function.Consumer
@ -115,13 +109,13 @@ abstract class MatteryMenu(
private val playerInputs = ArrayList<PlayerInput<*>>()
class PlayerInputPacket(val containerId: Int, val inputId: Int, val payload: ByteArray) : CustomPacketPayload {
constructor(buff: FriendlyByteBuf) : this(buff.readVarInt(), buff.readVarInt(), ByteArray(buff.readableBytes()).also { buff.readBytes(it) })
class PlayerInputPacket(val containerId: Int, val inputId: Int, val payload: ByteArrayList) : CustomPacketPayload {
constructor(buff: FriendlyByteBuf) : this(buff.readVarInt(), buff.readVarInt(), buff.readByteListUnbounded())
fun write(buff: FriendlyByteBuf) {
buff.writeVarInt(containerId)
buff.writeVarInt(inputId)
buff.writeBytes(payload)
buff.writeByteListUnbounded(payload)
}
fun play(context: IPayloadContext) {
@ -129,7 +123,7 @@ abstract class MatteryMenu(
if (menu.containerId != containerId || !menu.stillValid(context.player())) return
val input = menu.playerInputs.getOrNull(inputId) ?: return
if (!input.test(context.player())) return
input.invoke(input.codec.read(DataInputStream(FastByteArrayInputStream(payload))))
input.invoke(input.codec.decode(context.player().registryAccess(), payload))
}
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> {
@ -152,7 +146,7 @@ abstract class MatteryMenu(
/**
* Client->Server input
*/
inner class PlayerInput<V>(val codec: StreamCodec<V>, allowSpectators: Boolean = false, val handler: (V) -> Unit) : Consumer<V>, Predicate<Player?> {
inner class PlayerInput<V>(val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>, allowSpectators: Boolean = false, val handler: (V) -> Unit) : Consumer<V>, Predicate<Player> {
val id = playerInputs.size
var allowSpectators by mSynchronizer.boolean(allowSpectators)
@ -171,16 +165,13 @@ abstract class MatteryMenu(
return this
}
override fun test(player: Player?): Boolean {
if (player == null) return false
override fun test(player: Player): Boolean {
return filters.all { it.test(player) }
}
override fun accept(value: V) {
if (test(minecraft.player as Player?)) {
val stream = FastByteArrayOutputStream()
codec.write(DataOutputStream(stream), value)
PacketDistributor.sendToServer(PlayerInputPacket(containerId, id, stream.array.copyOfRange(0, stream.length)))
if (test(player)) {
PacketDistributor.sendToServer(PlayerInputPacket(containerId, id, codec.encode(player.registryAccess(), value)))
}
}
@ -190,7 +181,7 @@ abstract class MatteryMenu(
}
inner class SortInput(val container: Container, val settings: IItemStackSortingSettings) {
val input = PlayerInput(StreamCodec.Collection<Int, IntCollection>(VarIntValueCodec, ::IntArrayList)) {
val input = PlayerInput(MatteryStreamCodec.Collection<RegistryFriendlyByteBuf, Int, IntCollection>(StreamCodecs.VAR_INT, ::IntArrayList)) {
container.sortWithIndices(it)
}
@ -200,20 +191,19 @@ abstract class MatteryMenu(
}
fun oneWayInput(allowSpectators: Boolean = false, handler: () -> Unit): PlayerInput<Nothing?> {
return PlayerInput(NullValueCodec, allowSpectators) {
return PlayerInput(StreamCodecs.NOTHING, allowSpectators) {
handler.invoke()
}
}
fun bigDecimalInput(allowSpectators: Boolean = false, handler: (BigDecimal) -> Unit) = PlayerInput(BigDecimalValueCodec, allowSpectators, handler)
fun decimalInput(allowSpectators: Boolean = false, handler: (Decimal) -> Unit) = PlayerInput(DecimalValueCodec, allowSpectators, handler)
fun booleanInput(allowSpectators: Boolean = false, handler: (Boolean) -> Unit) = PlayerInput(BooleanValueCodec, allowSpectators, handler)
fun itemInput(allowSpectators: Boolean = false, handler: (Item) -> Unit) = PlayerInput(ItemValueCodec, allowSpectators, handler)
fun itemStackInput(allowSpectators: Boolean = false, handler: (ItemStack) -> Unit) = PlayerInput(ItemStackValueCodec, allowSpectators, handler)
fun nullableItemInput(allowSpectators: Boolean = false, handler: (Item?) -> Unit) = PlayerInput(ItemValueCodec.nullable(), allowSpectators, handler)
fun stringInput(allowSpectators: Boolean = false, handler: (String) -> Unit) = PlayerInput(BinaryStringCodec, allowSpectators, handler)
fun floatInput(allowSpectators: Boolean = false, handler: (Float) -> Unit) = PlayerInput(FloatValueCodec, allowSpectators, handler)
fun intInput(allowSpectators: Boolean = false, handler: (Int) -> Unit) = PlayerInput(VarIntValueCodec, allowSpectators, handler)
fun decimalInput(allowSpectators: Boolean = false, handler: (Decimal) -> Unit) = PlayerInput(StreamCodecs.DECIMAL, allowSpectators, handler)
fun booleanInput(allowSpectators: Boolean = false, handler: (Boolean) -> Unit) = PlayerInput(StreamCodecs.BOOLEAN, allowSpectators, handler)
fun itemInput(allowSpectators: Boolean = false, handler: (Item) -> Unit) = PlayerInput(StreamCodecs.ITEM_TYPE, allowSpectators, handler)
fun itemStackInput(allowSpectators: Boolean = false, handler: (ItemStack) -> Unit) = PlayerInput(ItemStack.STREAM_CODEC.wrap(), allowSpectators, handler)
fun nullableItemInput(allowSpectators: Boolean = false, handler: (Item?) -> Unit) = PlayerInput(StreamCodecs.ITEM_TYPE.nullable(), allowSpectators, handler)
fun stringInput(allowSpectators: Boolean = false, handler: (String) -> Unit) = PlayerInput(StreamCodecs.STRING, allowSpectators, handler)
fun floatInput(allowSpectators: Boolean = false, handler: (Float) -> Unit) = PlayerInput(StreamCodecs.FLOAT, allowSpectators, handler)
fun intInput(allowSpectators: Boolean = false, handler: (Int) -> Unit) = PlayerInput(StreamCodecs.INT, allowSpectators, handler)
/**
* hotbar + inventory + Exopack (in this order)
@ -331,7 +321,7 @@ abstract class MatteryMenu(
}
override fun mayPickup(player: Player): Boolean {
return super.mayPickup(player) && !item.isEmpty && (player.isCreative || !hasBindingCurse(item))
return super.mayPickup(player) && !item.isEmpty && (player.isCreative || !EnchantmentHelper.has(item, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP))
}
override fun getMaxStackSize(): Int {
@ -402,7 +392,7 @@ abstract class MatteryMenu(
beforeBroadcast()
mSynchronizer.observe()
val payload = synchronizerRemote.write()
val payload = synchronizerRemote.write(player.registryAccess())
if (payload != null) {
PacketDistributor.sendToPlayer(player as ServerPlayer, MenuDataPacket(containerId, payload))
@ -459,11 +449,11 @@ abstract class MatteryMenu(
val field: Delegate<Item?>
if (container is IMatteryContainer) {
input = PlayerInput(ItemValueCodec.nullable(), handler = { container.setSlotFilter(pSlot.slotIndex, it) })
field = mSynchronizer.add(delegate = { container.getSlotFilter(pSlot.slotIndex) }, ItemValueCodec.nullable())
input = PlayerInput(StreamCodecs.ITEM_TYPE_NULLABLE, handler = { container.setSlotFilter(pSlot.slotIndex, it) })
field = mSynchronizer.add(delegate = { container.getSlotFilter(pSlot.slotIndex) }, StreamCodecs.ITEM_TYPE_NULLABLE)
} else {
input = PlayerInput(ItemValueCodec.nullable(), handler = { throw UnsupportedOperationException() })
field = mSynchronizer.add(delegate = { null }, ItemValueCodec.nullable())
input = PlayerInput(StreamCodecs.ITEM_TYPE_NULLABLE, handler = { throw UnsupportedOperationException() })
field = mSynchronizer.add(delegate = { null }, StreamCodecs.ITEM_TYPE_NULLABLE)
}
pSlot.filter = Delegate.Of(getter = field::get, setter = input::accept)

View File

@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.menu.widget
import net.neoforged.neoforge.fluids.FluidStack
import net.neoforged.neoforge.fluids.capability.IFluidHandler
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.core.util.FluidStackValueCodec

View File

@ -1,6 +1,6 @@
package ru.dbotthepony.mc.otm.menu.widget
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage

View File

@ -1,6 +1,6 @@
package ru.dbotthepony.mc.otm.menu.widget
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage

View File

@ -1,6 +1,6 @@
package ru.dbotthepony.mc.otm.menu.widget
import ru.dbotthepony.kommons.io.DelegateSyncher
import ru.dbotthepony.mc.otm.network.DelegateSyncher
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.network
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@ -32,17 +33,15 @@ import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.MSoundEvents
import java.io.ByteArrayInputStream
class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val dataList: FastByteArrayOutputStream?, val dataBytes: ByteArray?) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
dataList ?: throw NullPointerException("No byte list is present")
class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val data: ByteArrayList) : CustomPacketPayload {
fun write(buff: RegistryFriendlyByteBuf) {
buff.writeInt(MRegistry.ANDROID_FEATURES.getId(type))
buff.writeBytes(dataList.array, 0, dataList.length)
buff.writeBytes(data.elements(), 0, data.size)
}
fun play(context: IPayloadContext) {
dataBytes ?: throw NullPointerException("No data bytes array is present")
val android = minecraft.player?.matteryPlayer ?: return
android.computeIfAbsent(type).applyNetworkPayload(ByteArrayInputStream(dataBytes))
android.computeIfAbsent(type).syncher.read(data, context.player().registryAccess())
}
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> {
@ -57,30 +56,28 @@ class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val dataList: Fa
)
)
val CODEC: StreamCodec<FriendlyByteBuf, AndroidFeatureSyncPacket> =
val CODEC: StreamCodec<RegistryFriendlyByteBuf, AndroidFeatureSyncPacket> =
StreamCodec.ofMember(AndroidFeatureSyncPacket::write, ::read)
fun read(buff: FriendlyByteBuf): AndroidFeatureSyncPacket {
fun read(buff: RegistryFriendlyByteBuf): AndroidFeatureSyncPacket {
return AndroidFeatureSyncPacket(
MRegistry.ANDROID_FEATURES.byIdOrThrow(buff.readInt()),
null, ByteArray(buff.readableBytes()).also { buff.readBytes(it) }
ByteArrayList.wrap(ByteArray(buff.readableBytes()).also { buff.readBytes(it) })
)
}
}
}
class AndroidResearchSyncPacket(val type: AndroidResearchType, val dataList: FastByteArrayOutputStream?, val dataBytes: ByteArray?) :
class AndroidResearchSyncPacket(val type: AndroidResearchType, val dataList: ByteArrayList) :
CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
dataList ?: throw NullPointerException("No byte list is present")
buff.writeUtf(type.id.toString())
buff.writeBytes(dataList.array, 0, dataList.length)
buff.writeBytes(dataList.elements(), 0, dataList.size)
}
fun play(context: IPayloadContext) {
dataBytes ?: throw NullPointerException("No data bytes array is present")
val android = minecraft.player?.matteryPlayer ?: return
android.getResearch(type).applyNetworkPayload(ByteArrayInputStream(dataBytes))
android.getResearch(type).syncher.read(dataList, context.player().registryAccess())
}
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> {
@ -101,7 +98,7 @@ class AndroidResearchSyncPacket(val type: AndroidResearchType, val dataList: Fas
fun read(buff: FriendlyByteBuf): AndroidResearchSyncPacket {
return AndroidResearchSyncPacket(
AndroidResearchManager[net.minecraft.resources.ResourceLocation.parse(buff.readUtf())] ?: throw NoSuchElementException(),
null, ByteArray(buff.readableBytes()).also { buff.readBytes(it) }
ByteArrayList.wrap(ByteArray(buff.readableBytes()).also { buff.readBytes(it) })
)
}
}

View File

@ -0,0 +1,857 @@
package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBufAllocator
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import net.minecraft.core.RegistryAccess
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.network.connection.ConnectionType
import ru.dbotthepony.kommons.collect.ListenableMap
import ru.dbotthepony.kommons.collect.ListenableSet
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.DelegateGetter
import ru.dbotthepony.kommons.util.DelegateSetter
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kommons.util.Listenable
import ru.dbotthepony.kommons.util.ListenableDelegate
import ru.dbotthepony.kommons.util.Observer
import ru.dbotthepony.kommons.util.ValueObserver
import ru.dbotthepony.mc.otm.core.math.Decimal
import java.io.Closeable
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.ReentrantLock
import java.util.function.BooleanSupplier
import java.util.function.Consumer
import java.util.function.DoubleSupplier
import java.util.function.IntSupplier
import java.util.function.LongSupplier
import java.util.function.Supplier
import kotlin.concurrent.withLock
/**
* Universal, one-to-many delegate/value synchronizer.
*
* Values and delegates can be attached using [add], or by constructing subclassed directly.
* Delta changes are tracked by [DelegateSyncher.Remote] instances.
*
* In general, this class is not meant to be _structurally_ concurrently mutated by different threads,
* to avoid structure corruption a lock is employed.
*
* Attached delegates can be safely mutated by multiple threads concurrently
* (attached [ListenableDelegate] can call [Listenable.addListener] callback from different threads).
*/
@Suppress("UNCHECKED_CAST", "UNUSED")
class DelegateSyncher : Observer {
private val lock = ReentrantLock()
private val slots = ArrayList<AbstractSlot?>()
private val gaps = IntAVLTreeSet()
private val observers = ArrayList<AbstractSlot>()
private val remotes = ArrayList<Remote>()
private var isRemote = false
override fun observe(): Boolean {
var any = false
observers.forEach { any = it.observe() || any }
return any
}
fun read(stream: RegistryFriendlyByteBuf) {
isRemote = true
var readID = stream.readVarInt()
while (readID > 0) {
val slot = slots.getOrNull(readID - 1) ?: throw IndexOutOfBoundsException("Unknown networked slot ${readID - 1}!")
slot.read(stream)
readID = stream.readVarInt()
}
}
fun read(registry: RegistryAccess, bytes: ByteArrayList) {
return decodePayload(registry, bytes) {
read(RegistryFriendlyByteBuf(it, registry, ConnectionType.NEOFORGE))
}
}
abstract inner class AbstractSlot : Closeable, Observer, Comparable<AbstractSlot> {
val id: Int
protected val isRemoved = AtomicBoolean()
fun remove() {
if (isRemoved.compareAndSet(false, true)) {
lock.withLock {
slots[id] = null
gaps.add(id)
remoteSlots.forEach {
it.remove()
}
remoteSlots.clear()
remove0()
}
}
}
final override fun close() {
return remove()
}
final override fun compareTo(other: AbstractSlot): Int {
return id.compareTo(other.id)
}
protected abstract fun remove0()
internal abstract fun read(stream: RegistryFriendlyByteBuf)
internal abstract fun write(stream: RegistryFriendlyByteBuf)
internal val remoteSlots = CopyOnWriteArrayList<Remote.RemoteSlot>()
protected fun markDirty() {
if (!isRemote && !isRemoved.get()) {
remoteSlots.forEach {
it.markDirty()
}
}
}
init {
lock.withLock {
if (gaps.isNotEmpty()) {
val gap = gaps.firstInt()
gaps.remove(gap)
id = gap
check(slots[id] == null) { "Expected slot $id to be empty" }
slots[id] = this
} else {
id = slots.size
slots.add(this)
}
remotes.forEach {
it.addSlot(this)
}
}
}
}
inner class Slot<V>(val delegate: ListenableDelegate<V>, val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot(), ListenableDelegate<V> {
constructor(delegate: Supplier<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : this(ListenableDelegate.CustomShadow(delegate, codec::compare, codec::copy), codec)
private val l = delegate.addListener(Consumer {
markDirty()
listeners.accept(it)
})
init {
if (delegate is ListenableDelegate.AbstractShadow) {
observers.add(this)
}
}
private val listeners = Listenable.Impl<V>()
override fun remove0() {
l.remove()
listeners.clear()
}
override fun read(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
delegate.accept(codec.decode(stream))
}
override fun write(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
codec.encode(stream, delegate.get())
}
override fun get(): V {
check(!isRemoved.get()) { "This network slot was removed" }
return delegate.get()
}
override fun accept(t: V) {
check(!isRemoved.get()) { "This network slot was removed" }
delegate.accept(t)
}
override fun addListener(listener: Consumer<V>): Listenable.L {
check(!isRemoved.get()) { "This network slot was removed" }
return listeners.addListener(listener)
}
override fun observe(): Boolean {
check(!isRemoved.get()) { "This network slot was removed" }
if (delegate is ListenableDelegate.AbstractShadow) {
return delegate.observe()
}
return false
}
}
inner class ObservedSlot<V>(val delegate: Delegate<V>, val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot(), ListenableDelegate<V>, ValueObserver<V> {
private val listeners = Listenable.Impl<V>()
private var observed: Any? = Mark
private val observeLock = ReentrantLock()
init {
observers.add(this)
}
override fun remove0() {
listeners.clear()
}
override fun read(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
val read = codec.decode(stream)
observed = codec.copy(read)
delegate.accept(read)
}
override fun write(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
codec.encode(stream, delegate.get())
}
override fun observe(): Boolean {
val get = delegate.get()
if (observed === Mark || !codec.compare(get, observed as V)) {
observeLock.withLock {
if (observed === Mark || !codec.compare(get, observed as V)) {
observed = codec.copy(get)
markDirty()
listeners.accept(get)
}
return true
}
}
return false
}
override fun getAndObserve(): Pair<V, Boolean> {
val get = delegate.get()
if (observed === Mark || !codec.compare(get, observed as V)) {
observeLock.withLock {
if (observed === Mark || !codec.compare(get, observed as V)) {
observed = codec.copy(get)
markDirty()
listeners.accept(get)
}
return get to true
}
}
return get to false
}
override fun get(): V {
check(!isRemoved.get()) { "This network slot was removed" }
return delegate.get()
}
override fun accept(t: V) {
check(!isRemoved.get()) { "This network slot was removed" }
delegate.accept(t)
}
override fun addListener(listener: Consumer<V>): Listenable.L {
check(!isRemoved.get()) { "This network slot was removed" }
return listeners.addListener(listener)
}
}
private object Mark
internal data class SetAction<V>(val value: KOptional<V>, val action: Int)
inner class SetSlot<V>(val delegate: ListenableSet<V>, val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot() {
private val listener = object : ListenableSet.SetListener<V> {
override fun onClear() {
remoteSlots.forEach {
(it as Remote.RemoteSetSlot<V>).changelist.clear()
synchronized(it.changelistLock) {
it.changelist.add(SetAction(KOptional(), CLEAR))
}
listeners.forEach {
it.listener.onClear()
}
it.markDirty()
}
}
override fun onValueAdded(element: V) {
remoteSlots.forEach {
it as Remote.RemoteSetSlot<V>
synchronized(it.changelistLock) {
it.changelist.removeIf { it.value.valueEquals(element) }
it.changelist.add(SetAction(KOptional(codec.copy(element)), ADD))
}
listeners.forEach {
it.listener.onValueAdded(element)
}
it.markDirty()
}
}
override fun onValueRemoved(element: V) {
remoteSlots.forEach {
it as Remote.RemoteSetSlot<V>
synchronized(it.changelistLock) {
it.changelist.removeIf { it.value.valueEquals(element) }
it.changelist.add(SetAction(KOptional(codec.copy(element)), REMOVE))
}
listeners.forEach {
it.listener.onValueRemoved(element)
}
it.markDirty()
}
}
}
private val l = delegate.addListener(listener)
private val listeners = CopyOnWriteArrayList<Listener>()
override fun remove0() {
l.remove()
listeners.clear()
}
private inner class Listener(val listener: ListenableSet.SetListener<V>): Listenable.L {
private var isRemoved = false
init {
listeners.add(this)
}
override fun remove() {
if (!isRemoved) {
isRemoved = true
listeners.remove(this)
}
}
}
fun addListener(listener: ListenableSet.SetListener<V>): Listenable.L {
check(!isRemoved.get()) { "This network slot was removed" }
return Listener(listener)
}
fun addListener(listener: Runnable): Listenable.L {
check(!isRemoved.get()) { "This network slot was removed" }
return Listener(ListenableSet.RunnableAdapter(listener))
}
override fun read(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
var action = stream.readByte().toInt()
while (true) {
when (action) {
ADD -> delegate.add(codec.decode(stream))
REMOVE -> delegate.remove(codec.decode(stream))
CLEAR -> delegate.clear()
else -> break
}
action = stream.readByte().toInt()
}
}
override fun write(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
throw RuntimeException("unreachable code")
}
override fun observe(): Boolean {
check(!isRemoved.get()) { "This network slot was removed" }
return false
}
}
internal data class MapAction<K, V>(val key: KOptional<K>, val value: KOptional<V>, val action: Int)
inner class MapSlot<K, V>(val delegate: ListenableMap<K, V>, val keyCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, K>, val valueCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot() {
private val listener = object : ListenableMap.MapListener<K, V> {
override fun onClear() {
remoteSlots.forEach {
it as Remote.RemoteMapSlot<K, V>
synchronized(it.changelistLock) {
it.changelist.clear()
it.changelist.add(MapAction(KOptional(), KOptional(), CLEAR))
}
listeners.forEach {
it.listener.onClear()
}
it.markDirty()
}
}
override fun onValueAdded(key: K, value: V) {
remoteSlots.forEach {
it as Remote.RemoteMapSlot<K, V>
synchronized(it.changelistLock) {
it.changelist.removeIf { it.key.valueEquals(key) }
it.changelist.add(MapAction(KOptional(keyCodec.copy(key)), KOptional(valueCodec.copy(value)), ADD))
}
listeners.forEach {
it.listener.onValueAdded(key, value)
}
it.markDirty()
}
}
override fun onValueRemoved(key: K, value: V) {
remoteSlots.forEach {
it as Remote.RemoteMapSlot<K, V>
synchronized(it.changelistLock) {
it.changelist.removeIf { it.key.valueEquals(key) }
it.changelist.add(MapAction(KOptional(keyCodec.copy(key)), KOptional(), REMOVE))
}
listeners.forEach {
it.listener.onValueRemoved(key, value)
}
it.markDirty()
}
}
}
private val l = delegate.addListener(listener)
private val listeners = CopyOnWriteArrayList<Listener>()
override fun remove0() {
l.remove()
listeners.clear()
}
private inner class Listener(val listener: ListenableMap.MapListener<K, V>): Listenable.L {
private var isRemoved = false
init {
listeners.add(this)
}
override fun remove() {
if (!isRemoved) {
isRemoved = true
listeners.remove(this)
}
}
}
fun addListener(listener: ListenableMap.MapListener<K, V>): Listenable.L {
check(!isRemoved.get()) { "This network slot was removed" }
return Listener(listener)
}
fun addListener(listener: Runnable): Listenable.L {
check(!isRemoved.get()) { "This network slot was removed" }
return Listener(ListenableMap.RunnableAdapter(listener))
}
override fun read(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
var action = stream.readByte().toInt()
while (true) {
when (action) {
ADD -> delegate.put(keyCodec.decode(stream), valueCodec.decode(stream))
REMOVE -> delegate.remove(keyCodec.decode(stream))
CLEAR -> delegate.clear()
else -> break
}
action = stream.readByte().toInt()
}
}
override fun write(stream: RegistryFriendlyByteBuf) {
check(!isRemoved.get()) { "This network slot was removed" }
throw RuntimeException("unreachable code")
}
override fun observe(): Boolean {
check(!isRemoved.get()) { "This network slot was removed" }
return false
}
}
fun <V> add(value: V, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>, setter: DelegateSetter<V> = DelegateSetter.passthrough(), getter: DelegateGetter<V> = DelegateGetter.passthrough()): Slot<V> {
return Slot(ListenableDelegate.maskSmart(value, getter, setter), codec)
}
fun <V> add(delegate: ListenableDelegate<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): Slot<V> {
return Slot(delegate, codec)
}
fun <V> add(delegate: Delegate<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): ObservedSlot<V> {
return ObservedSlot(delegate, codec)
}
fun <V> add(delegate: Supplier<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): Slot<V> {
return computed(delegate, codec)
}
fun <E> add(delegate: ListenableSet<E>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, E>): SetSlot<E> {
return SetSlot(delegate, codec)
}
fun <K, V> add(delegate: ListenableMap<K, V>, keyCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, K>, valueCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): MapSlot<K, V> {
return MapSlot(delegate, keyCodec, valueCodec)
}
fun computedByte(delegate: Supplier<Byte>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.BYTE)
fun computedShort(delegate: Supplier<Short>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.SHORT)
fun computedInt(delegate: Supplier<Int>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.INT)
fun computedLong(delegate: Supplier<Long>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.VAR_LONG)
fun computedFloat(delegate: Supplier<Float>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.FLOAT)
fun computedDouble(delegate: Supplier<Double>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.DOUBLE)
fun computedBoolean(delegate: Supplier<Boolean>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.BOOLEAN)
fun computedString(delegate: Supplier<String>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.STRING)
fun computedUUID(delegate: Supplier<UUID>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.UUID)
fun <V> computed(delegate: Supplier<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) = Slot(ListenableDelegate.CustomShadow(delegate, codec::compare, codec::copy), codec)
fun computedInt(delegate: IntSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsInt), StreamCodecs.VAR_INT)
fun computedLong(delegate: LongSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsLong), StreamCodecs.VAR_LONG)
fun computedDouble(delegate: DoubleSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsDouble), StreamCodecs.DOUBLE)
fun computedBoolean(delegate: BooleanSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsBoolean), StreamCodecs.BOOLEAN)
@JvmName("vbyte")
@JvmOverloads
fun byte(value: Byte = 0, setter: DelegateSetter<Byte> = DelegateSetter.passthrough(), getter: DelegateGetter<Byte> = DelegateGetter.passthrough()): Slot<Byte> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.BYTE)
}
@JvmName("vshort")
@JvmOverloads
fun short(value: Short = 0, setter: DelegateSetter<Short> = DelegateSetter.passthrough(), getter: DelegateGetter<Short> = DelegateGetter.passthrough()): Slot<Short> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.SHORT)
}
@JvmName("vint")
@JvmOverloads
fun int(value: Int = 0, setter: DelegateSetter<Int> = DelegateSetter.passthrough(), getter: DelegateGetter<Int> = DelegateGetter.passthrough()): Slot<Int> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.VAR_INT)
}
@JvmName("vlong")
@JvmOverloads
fun long(value: Long = 0L, setter: DelegateSetter<Long> = DelegateSetter.passthrough(), getter: DelegateGetter<Long> = DelegateGetter.passthrough()): Slot<Long> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.VAR_LONG)
}
@JvmName("vfloat")
@JvmOverloads
fun float(value: Float = 0f, setter: DelegateSetter<Float> = DelegateSetter.passthrough(), getter: DelegateGetter<Float> = DelegateGetter.passthrough()): Slot<Float> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.FLOAT)
}
@JvmName("vdouble")
@JvmOverloads
fun double(value: Double = 0.0, setter: DelegateSetter<Double> = DelegateSetter.passthrough(), getter: DelegateGetter<Double> = DelegateGetter.passthrough()): Slot<Double> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.DOUBLE)
}
@JvmName("vboolean")
@JvmOverloads
fun boolean(value: Boolean = false, setter: DelegateSetter<Boolean> = DelegateSetter.passthrough(), getter: DelegateGetter<Boolean> = DelegateGetter.passthrough()): Slot<Boolean> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.BOOLEAN)
}
@JvmOverloads
fun string(value: String, setter: DelegateSetter<String> = DelegateSetter.passthrough(), getter: DelegateGetter<String> = DelegateGetter.passthrough()): Slot<String> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.STRING)
}
@JvmOverloads
fun uuid(value: UUID, setter: DelegateSetter<UUID> = DelegateSetter.passthrough(), getter: DelegateGetter<UUID> = DelegateGetter.passthrough()): Slot<UUID> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.UUID)
}
@JvmOverloads
fun <E> set(codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, E>, backing: MutableSet<E> = ObjectOpenHashSet()): SetSlot<E> {
return add(ListenableSet(backing), codec)
}
@JvmOverloads
fun <K, V> map(keyCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, K>, valueCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>, backing: MutableMap<K, V> = Object2ObjectOpenHashMap()): MapSlot<K, V> {
return add(ListenableMap(backing), keyCodec, valueCodec)
}
@JvmOverloads
fun <E : Enum<E>> enum(value: E, setter: DelegateSetter<E> = DelegateSetter.passthrough(), getter: DelegateGetter<E> = DelegateGetter.passthrough()): Slot<E> {
return add(ListenableDelegate.maskSmart(value, getter, setter), MatteryStreamCodec.Enum(value::class.java))
}
fun decimal(value: Decimal = Decimal.ZERO, setter: DelegateSetter<Decimal> = DelegateSetter.passthrough(), getter: DelegateGetter<Decimal> = DelegateGetter.passthrough()): DelegateSyncher.Slot<Decimal> {
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.DECIMAL)
}
fun computedDecimal(delegate: Supplier<Decimal>): DelegateSyncher.Slot<Decimal> {
return add(delegate, StreamCodecs.DECIMAL)
}
fun item(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): DelegateSyncher.Slot<ItemStack> {
return add(ListenableDelegate.maskSmart(value, getter, setter), ItemStack.STREAM_CODEC.wrap())
}
fun computedItem(delegate: Supplier<ItemStack>): DelegateSyncher.Slot<ItemStack> {
return computed(delegate, ItemStack.STREAM_CODEC.wrap())
}
fun observedItem(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): DelegateSyncher.ObservedSlot<ItemStack> {
return add(Delegate.maskSmart(value, getter, setter), ItemStack.STREAM_CODEC.wrap())
}
/**
* Remotes must be [close]d when no longer in use, otherwise they will
* leak memory and decrease performance of syncher.
*
* To get changes to be networked to remote client, [write] should be called.
*/
inner class Remote : Closeable {
@Volatile
private var isRemoved = false
internal val dirty = ConcurrentLinkedQueue<RemoteSlot>()
private val remoteSlots = CopyOnWriteArrayList<RemoteSlot>()
internal open inner class RemoteSlot(val parent: AbstractSlot) : Comparable<RemoteSlot> {
private val isDirty = AtomicBoolean()
final override fun compareTo(other: RemoteSlot): Int {
return parent.compareTo(other.parent)
}
init {
remoteSlots.add(this)
dirty.add(this)
}
fun markDirty() {
if (!isRemoved && isDirty.compareAndSet(false, true)) {
dirty.add(this)
}
}
fun markClean() {
isDirty.set(false)
}
open fun invalidate() {
markDirty()
}
open fun remove() {
isDirty.set(true)
remoteSlots.remove(this)
dirty.remove(this)
}
open fun write(stream: RegistryFriendlyByteBuf) {
parent.write(stream)
}
override fun hashCode(): Int {
return parent.hashCode()
}
override fun equals(other: Any?): Boolean {
return this === other || other is Remote.RemoteSlot && other.parent == parent
}
}
internal inner class RemoteSetSlot<V>(parent: SetSlot<V>) : RemoteSlot(parent) {
val changelist = ArrayList<SetAction<V>>()
val changelistLock = Any()
override fun remove() {
super.remove()
changelist.clear()
}
override fun invalidate() {
synchronized(changelistLock) {
changelist.clear()
changelist.add(SetAction(KOptional(), CLEAR))
parent as SetSlot<V>
for (v in parent.delegate) {
changelist.add(SetAction(KOptional(parent.codec.copy(v)), ADD))
}
}
super.invalidate()
}
override fun write(stream: RegistryFriendlyByteBuf) {
synchronized(changelistLock) {
for (change in changelist) {
stream.writeByte(change.action)
change.value.ifPresent { (parent as SetSlot<V>).codec.encode(stream, it) }
}
changelist.clear()
}
stream.writeByte(0)
}
}
internal inner class RemoteMapSlot<K, V>(parent: MapSlot<K, V>) : RemoteSlot(parent) {
val changelist = ArrayList<MapAction<K, V>>()
val changelistLock = Any()
override fun remove() {
super.remove()
changelist.clear()
}
override fun invalidate() {
synchronized(changelistLock) {
changelist.clear()
changelist.add(MapAction(KOptional(), KOptional(), CLEAR))
parent as MapSlot<K, V>
for ((k, v) in parent.delegate) {
changelist.add(MapAction(KOptional(parent.keyCodec.copy(k)), KOptional(parent.valueCodec.copy(v)), ADD))
}
}
super.invalidate()
}
override fun write(stream: RegistryFriendlyByteBuf) {
synchronized(changelistLock) {
for (change in changelist) {
stream.writeByte(change.action)
change.key.ifPresent { (parent as MapSlot<K, V>).keyCodec.encode(stream, it) }
change.value.ifPresent { (parent as MapSlot<K, V>).valueCodec.encode(stream, it) }
}
changelist.clear()
}
stream.writeByte(0)
}
}
init {
lock.withLock {
slots.forEach {
if (it != null) {
addSlot(it)
}
}
remotes.add(this)
}
}
internal fun addSlot(slot: AbstractSlot) {
if (slot is SetSlot<*>) {
slot.remoteSlots.add(RemoteSetSlot(slot))
} else if (slot is MapSlot<*, *>) {
slot.remoteSlots.add(RemoteMapSlot(slot))
} else {
slot.remoteSlots.add(RemoteSlot(slot))
}
}
/**
* Returns null if this remote is clean.
*
* [DelegateSyncher.observe] is not called automatically for performance
* reasons, you must call it manually.
*/
fun write(registry: RegistryAccess): ByteArrayList? {
if (dirty.isNotEmpty()) {
val sorted = ObjectAVLTreeSet<RemoteSlot>()
var next = dirty.poll()
while (next != null) {
sorted.add(next)
next.markClean()
next = dirty.poll()
}
return encodePayload(registry) {
for (slot in sorted) {
it.writeVarInt(slot.parent.id + 1)
slot.write(it)
}
it.writeByte(0)
}
}
return null
}
/**
* Marks all networked slots dirty
*/
fun invalidate() {
remoteSlots.forEach { it.invalidate() }
}
override fun close() {
if (!isRemoved) {
lock.withLock {
if (!isRemoved) {
remoteSlots.forEach {
it.remove()
it.parent.remoteSlots.remove(it)
}
remotes.remove(this)
}
}
dirty.clear()
}
}
}
companion object {
private const val END = 0
private const val CLEAR = 1
private const val ADD = 2
private const val REMOVE = 3
}
}

View File

@ -1,8 +1,14 @@
package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufAllocator
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import net.minecraft.core.RegistryAccess
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
import net.neoforged.neoforge.network.connection.ConnectionType
import net.neoforged.neoforge.network.handling.IPayloadContext
import net.neoforged.neoforge.network.registration.PayloadRegistrar
import kotlin.reflect.KFunction1
@ -18,3 +24,63 @@ fun <T : CustomPacketPayload> PayloadRegistrar.playToServer(
codec: StreamCodec<FriendlyByteBuf, T>,
handler: KFunction1<IPayloadContext, Unit>
): PayloadRegistrar = playToServer(type, codec) { _, context -> handler(context) }
inline fun encodePayload(registry: RegistryAccess, block: (RegistryFriendlyByteBuf) -> Unit): ByteArrayList {
val underlying = ByteBufAllocator.DEFAULT.buffer()
val buf = RegistryFriendlyByteBuf(underlying, registry, ConnectionType.NEOFORGE)
try {
block.invoke(buf)
val bytes = ByteArrayList(buf.readableBytes())
check(bytes.size == buf.readableBytes())
buf.readBytes(bytes.elements())
return bytes
} finally {
underlying.release()
}
}
inline fun <T> decodePayload(registry: RegistryAccess, data: ByteArrayList, block: (RegistryFriendlyByteBuf) -> T): T {
val underlying = ByteBufAllocator.DEFAULT.buffer(data.size)
val buf = RegistryFriendlyByteBuf(underlying, registry, ConnectionType.NEOFORGE)
try {
underlying.writeBytes(data.elements(), 0, data.size)
return block.invoke(buf)
} finally {
underlying.release()
}
}
fun FriendlyByteBuf.writeByteListUnbounded(list: ByteArrayList) {
writeBytes(list.elements(), 0, list.size)
}
fun FriendlyByteBuf.readByteListUnbounded(): ByteArrayList {
return ByteArrayList.wrap(ByteArray(readableBytes()).also(::readBytes))
}
fun FriendlyByteBuf.writeByteList(list: ByteArrayList) {
writeVarInt(list.size)
writeBytes(list.elements(), 0, list.size)
}
fun FriendlyByteBuf.readByteList(): ByteArrayList {
return ByteArrayList.wrap(ByteArray(readVarInt()).also(::readBytes))
}
fun <S : ByteBuf, V> StreamCodec<S, V>.wrap(): MatteryStreamCodec<S, V> = MatteryStreamCodec.Wrapper(this)
fun <S : ByteBuf, V> MatteryStreamCodec<S, V>.nullable(): MatteryStreamCodec<S, V?> = MatteryStreamCodec.Nullable(this)
fun <V> StreamCodec<in RegistryFriendlyByteBuf, V>.encode(registry: RegistryAccess, value: V): ByteArrayList {
return encodePayload(registry) {
encode(it, value)
}
}
fun <V> StreamCodec<in RegistryFriendlyByteBuf, V>.decode(registry: RegistryAccess, value: ByteArrayList): V {
return decodePayload(registry, value) {
decode(it)
}
}

View File

@ -1,6 +1,6 @@
package ru.dbotthepony.mc.otm.network
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@ -28,14 +28,9 @@ import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.core.readItem
import ru.dbotthepony.mc.otm.core.writeItem
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import java.io.ByteArrayInputStream
import java.util.*
class MatteryPlayerDataPacket(val bytes: ByteArray, val length: Int, val isPublic: Boolean, val target: UUID? = null) :
CustomPacketPayload {
constructor(stream: FastByteArrayOutputStream, isPublic: Boolean, target: UUID? = null) : this(stream.array, stream.length, isPublic, target)
class MatteryPlayerDataPacket(val bytes: ByteArrayList, val isPublic: Boolean, val target: UUID? = null) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(target != null)
@ -43,7 +38,7 @@ class MatteryPlayerDataPacket(val bytes: ByteArray, val length: Int, val isPubli
buff.writeUUID(target)
buff.writeBoolean(isPublic)
buff.writeBytes(bytes, 0, length)
buff.writeBytes(bytes.elements(), 0, bytes.size)
}
fun play(context: IPayloadContext) {
@ -56,9 +51,9 @@ class MatteryPlayerDataPacket(val bytes: ByteArray, val length: Int, val isPubli
}
if (isPublic) {
player.publicSyncher.read(ByteArrayInputStream(bytes, 0, length))
player.publicSyncher.read(bytes, context.player().registryAccess())
} else {
player.syncher.read(ByteArrayInputStream(bytes, 0, length))
player.syncher.read(bytes, context.player().registryAccess())
}
}
@ -84,7 +79,7 @@ class MatteryPlayerDataPacket(val bytes: ByteArray, val length: Int, val isPubli
val isPublic = buff.readBoolean()
val readable = buff.readableBytes()
return MatteryPlayerDataPacket(ByteArray(readable).also(buff::readBytes), readable, isPublic, target)
return MatteryPlayerDataPacket(ByteArrayList.wrap(ByteArray(readable).also(buff::readBytes)), isPublic, target)
}
}
}

View File

@ -0,0 +1,127 @@
package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import net.minecraft.core.RegistryAccess
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import ru.dbotthepony.kommons.io.readVarInt
import ru.dbotthepony.kommons.io.writeVarInt
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.NoSuchElementException
interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
fun copy(value: V): V {
return value
}
fun compare(a: V, b: V): Boolean {
return a == b
}
// ignore kotlin compiler warning; StreamCodec<> has wrong package-wide nullability annotation
override fun decode(stream: S): V
override fun encode(stream: S, value: V)
class Wrapper<S : ByteBuf, V>(parent: StreamCodec<S, V>) : MatteryStreamCodec<S, V>, StreamCodec<S, V> by parent
class Nullable<S : ByteBuf, V>(val parent: MatteryStreamCodec<S, V>) : MatteryStreamCodec<S, V?> {
override fun decode(stream: S): V? {
return if (!stream.readBoolean()) null else parent.decode(stream)
}
override fun encode(stream: S, value: V?) {
if (value === null)
stream.writeBoolean(false)
else {
stream.writeBoolean(true)
parent.encode(stream, value)
}
}
override fun copy(value: V?): V? {
return if (value === null) null else parent.copy(value)
}
override fun compare(a: V?, b: V?): Boolean {
if (a === null && b === null) return true
if (a === null || b === null) return false
return parent.compare(a, b)
}
}
class Collection<S : FriendlyByteBuf, E, C : MutableCollection<E>>(val elementCodec: MatteryStreamCodec<in S, E>, val collectionFactory: (Int) -> C) : MatteryStreamCodec<S, C> {
override fun decode(stream: S): C {
val size = stream.readVarInt()
if (size <= 0) {
return collectionFactory.invoke(0)
}
val collection = collectionFactory.invoke(size)
for (i in 0 until size) {
collection.add(elementCodec.decode(stream))
}
return collection
}
override fun encode(stream: S, value: C) {
stream.writeVarInt(value.size)
value.forEach { elementCodec.encode(stream, it) }
}
override fun copy(value: C): C {
val new = collectionFactory.invoke(value.size)
value.forEach { new.add(elementCodec.copy(it)) }
return new
}
}
class Enum<S : FriendlyByteBuf, V : kotlin.Enum<V>>(clazz: Class<out V>) : MatteryStreamCodec<S, V> {
val clazz = searchClass(clazz)
val values: List<V> = listOf(*this.clazz.enumConstants!!)
val valuesMap = values.associateBy { it.name }
override fun decode(stream: S): V {
val id = stream.readVarInt()
return values.getOrNull(id) ?: throw NoSuchElementException("No such enum with index $id")
}
override fun encode(stream: S, value: V) {
stream.writeVarInt(value.ordinal)
}
override fun copy(value: V): V {
return value
}
override fun compare(a: V, b: V): Boolean {
return a === b
}
companion object {
/**
* FIXME: enums with abstract methods which get compiled to subclasses, whose DO NOT expose "parent's" enum constants array
*
* is there an already existing solution?
*/
fun <V : kotlin.Enum<V>> searchClass(clazz: Class<out V>): Class<out V> {
var search: Class<*> = clazz
while (search.enumConstants == null && search.superclass != null) {
search = search.superclass
}
if (search.enumConstants == null) {
throw ClassCastException("$clazz does not represent an enum or enum subclass")
}
return search as Class<out V>
}
}
}
}

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.network
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
@ -13,22 +14,20 @@ import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import java.io.ByteArrayInputStream
class MenuDataPacket(val containerId: Int, val bytes: ByteArray, val length: Int) : CustomPacketPayload {
constructor(containerId: Int, stream: FastByteArrayOutputStream) : this(containerId, stream.array, stream.length)
class MenuDataPacket(val containerId: Int, val bytes: ByteArrayList) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
buff.writeVarInt(containerId)
buff.writeBytes(bytes, 0, length)
buff.writeByteListUnbounded(bytes)
}
fun play(context: IPayloadContext) {
if (containerId == ExopackInventoryMenu.CONTAINER_ID) {
minecraft.player?.matteryPlayer?.exoPackMenu?.mSynchronizer?.read(ByteArrayInputStream(bytes, 0, length))
context.player().matteryPlayer.exoPackMenu.mSynchronizer.read(context.player().registryAccess(), bytes)
} else {
val menu = minecraft.player?.containerMenu as? MatteryMenu ?: return
val menu = context.player().containerMenu as? MatteryMenu ?: return
if (menu.containerId == containerId)
menu.mSynchronizer.read(ByteArrayInputStream(bytes, 0, length))
menu.mSynchronizer.read(context.player().registryAccess(), bytes)
}
}
@ -49,8 +48,7 @@ class MenuDataPacket(val containerId: Int, val bytes: ByteArray, val length: Int
fun read(buff: FriendlyByteBuf): MenuDataPacket {
val containerId = buff.readVarInt()
val readable = buff.readableBytes()
return MenuDataPacket(containerId, ByteArray(readable).also(buff::readBytes), readable)
return MenuDataPacket(containerId, buff.readByteListUnbounded())
}
}
}

View File

@ -1,13 +1,37 @@
package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf
import net.minecraft.core.UUIDUtil
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.readDecimal
import ru.dbotthepony.mc.otm.core.math.writeDecimal
import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.writeItemType
// because mojang didn't bother
object StreamCodecs {
val INT: StreamCodec<FriendlyByteBuf, Int> = StreamCodec.of(FriendlyByteBuf::writeInt, FriendlyByteBuf::readInt)
val LONG: StreamCodec<FriendlyByteBuf, Long> = StreamCodec.of(FriendlyByteBuf::writeLong, FriendlyByteBuf::readLong)
val DOUBLE: StreamCodec<FriendlyByteBuf, Double> = StreamCodec.of(FriendlyByteBuf::writeDouble, FriendlyByteBuf::readDouble)
val FLOAT: StreamCodec<FriendlyByteBuf, Float> = StreamCodec.of(FriendlyByteBuf::writeFloat, FriendlyByteBuf::readFloat)
val BOOLEAN: StreamCodec<FriendlyByteBuf, Boolean> = StreamCodec.of(FriendlyByteBuf::writeBoolean, FriendlyByteBuf::readBoolean)
val NOTHING: MatteryStreamCodec<ByteBuf, Nothing?> = StreamCodec.of<ByteBuf, Nothing?>({ _, _ -> }, { null }).wrap()
val BYTE: MatteryStreamCodec<ByteBuf, Byte> = StreamCodec.of({ s, v -> s.writeByte(v.toInt()) }, ByteBuf::readByte).wrap()
val SHORT = StreamCodec.of({ s, v -> s.writeShort(v.toInt()) }, ByteBuf::readShort).wrap()
val INT = StreamCodec.of(ByteBuf::writeInt, ByteBuf::readInt).wrap()
val VAR_INT = StreamCodec.of(FriendlyByteBuf::writeVarInt, FriendlyByteBuf::readVarInt).wrap()
val LONG = StreamCodec.of(ByteBuf::writeLong, ByteBuf::readLong).wrap()
val VAR_LONG = StreamCodec.of(FriendlyByteBuf::writeVarLong, FriendlyByteBuf::readVarLong).wrap()
val DOUBLE = StreamCodec.of(ByteBuf::writeDouble, ByteBuf::readDouble).wrap()
val FLOAT = StreamCodec.of(ByteBuf::writeFloat, ByteBuf::readFloat).wrap()
val BOOLEAN = StreamCodec.of(ByteBuf::writeBoolean, ByteBuf::readBoolean).wrap()
val STRING = StreamCodec.of(FriendlyByteBuf::writeUtf, FriendlyByteBuf::readUtf).wrap()
val UUID = UUIDUtil.STREAM_CODEC.wrap()
val RGBA: MatteryStreamCodec<ByteBuf, RGBAColor> = StreamCodec.of<ByteBuf, RGBAColor>(
{ s, v -> s.writeFloat(v.red); s.writeFloat(v.green); s.writeFloat(v.blue); s.writeFloat(v.alpha) },
{ s -> RGBAColor(s.readFloat(), s.readFloat(), s.readFloat(), s.readFloat()) }).wrap()
val ITEM_TYPE = StreamCodec.of(FriendlyByteBuf::writeItemType, FriendlyByteBuf::readItemType).wrap()
val ITEM_TYPE_NULLABLE = ITEM_TYPE.nullable()
val DECIMAL = StreamCodec.of(FriendlyByteBuf::writeDecimal, FriendlyByteBuf::readDecimal).wrap()
}