Re-implement DelegateSyncher using Mojang's StreamCodec
This commit is contained in:
parent
03cec07ffb
commit
aa5d36c488
@ -1,16 +1,15 @@
|
|||||||
package ru.dbotthepony.mc.otm.android
|
package ru.dbotthepony.mc.otm.android
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
|
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.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayer
|
import ru.dbotthepony.mc.otm.capability.MatteryPlayer
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
||||||
val ply get() = android.ply
|
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 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 {
|
override fun serializeNBT(registry: HolderLookup.Provider): CompoundTag {
|
||||||
return CompoundTag().also {
|
return CompoundTag().also {
|
||||||
it["level"] = level
|
it["level"] = level
|
||||||
|
@ -10,7 +10,7 @@ import net.minecraft.world.entity.player.Player
|
|||||||
import net.neoforged.bus.api.Event
|
import net.neoforged.bus.api.Event
|
||||||
import net.neoforged.neoforge.common.NeoForge
|
import net.neoforged.neoforge.common.NeoForge
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
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.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||||
@ -173,15 +173,6 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collectNetworkPayload(): FastByteArrayOutputStream? {
|
|
||||||
syncher.observe()
|
|
||||||
return syncherRemote.write()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun applyNetworkPayload(stream: InputStream) {
|
|
||||||
syncher.read(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
val canResearch: Boolean get() {
|
val canResearch: Boolean get() {
|
||||||
if (!consumeResearchCost(simulate = true)) {
|
if (!consumeResearchCost(simulate = true)) {
|
||||||
return false
|
return false
|
||||||
|
@ -88,7 +88,7 @@ object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().s
|
|||||||
if (SERVER_IS_LIVE) {
|
if (SERVER_IS_LIVE) {
|
||||||
onceServer {
|
onceServer {
|
||||||
for (ply in MINECRAFT_SERVER.playerList.players) {
|
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 {
|
context.enqueueWork {
|
||||||
minecraft.player?.matteryPlayer?.reloadResearch()
|
minecraft.player?.matteryPlayer?.reloadResearch(MINECRAFT_SERVER.registryAccess())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity
|
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.ints.Int2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
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.event.tick.LevelTickEvent
|
||||||
import net.neoforged.neoforge.network.PacketDistributor
|
import net.neoforged.neoforge.network.PacketDistributor
|
||||||
import org.apache.logging.log4j.LogManager
|
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.kommons.util.Listenable
|
||||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
|
||||||
import ru.dbotthepony.mc.otm.block.INeighbourChangeListener
|
import ru.dbotthepony.mc.otm.block.INeighbourChangeListener
|
||||||
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
|
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
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.get
|
||||||
import ru.dbotthepony.mc.otm.core.math.BlockRotation
|
import ru.dbotthepony.mc.otm.core.math.BlockRotation
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
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.IntCounter
|
||||||
import ru.dbotthepony.mc.otm.core.util.Savetables
|
import ru.dbotthepony.mc.otm.core.util.Savetables
|
||||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||||
import ru.dbotthepony.mc.otm.core.util.countingLazy
|
import ru.dbotthepony.mc.otm.core.util.countingLazy
|
||||||
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
||||||
import ru.dbotthepony.mc.otm.once
|
|
||||||
import ru.dbotthepony.mc.otm.onceServer
|
import ru.dbotthepony.mc.otm.onceServer
|
||||||
import ru.dbotthepony.mc.otm.sometimeServer
|
import ru.dbotthepony.mc.otm.sometimeServer
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.BooleanSupplier
|
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import kotlin.collections.ArrayList
|
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
|
* Absolute barebone (lol) block entity class in Overdrive that Matters, providing bare minimum (lulmao, minecraft engine) functionality
|
||||||
|
@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.block.entity
|
|||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
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.Listenable
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
|
@ -16,6 +16,7 @@ import net.minecraft.nbt.CompoundTag
|
|||||||
import net.minecraft.nbt.IntTag
|
import net.minecraft.nbt.IntTag
|
||||||
import net.minecraft.nbt.ListTag
|
import net.minecraft.nbt.ListTag
|
||||||
import net.minecraft.nbt.StringTag
|
import net.minecraft.nbt.StringTag
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
@ -57,12 +58,7 @@ import org.apache.logging.log4j.LogManager
|
|||||||
import org.joml.Vector4f
|
import org.joml.Vector4f
|
||||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
import ru.dbotthepony.kommons.collect.ListenableMap
|
||||||
import ru.dbotthepony.kommons.collect.ListenableSet
|
import ru.dbotthepony.kommons.collect.ListenableSet
|
||||||
import ru.dbotthepony.kommons.io.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.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.kommons.util.ListenableDelegate
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
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.getIntList
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.getStringList
|
import ru.dbotthepony.mc.otm.core.nbt.getStringList
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
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.Savetables
|
||||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||||
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
|
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
|
||||||
@ -235,7 +230,7 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
*/
|
*/
|
||||||
var exopackGlows by publicSyncher.boolean(true)
|
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
|
* Tick event schedulers
|
||||||
@ -249,8 +244,8 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
|
|
||||||
private val exopackSlotModifierMap = syncher.MapSlot(
|
private val exopackSlotModifierMap = syncher.MapSlot(
|
||||||
ListenableMap<UUID, Int>().also { it.addListener(Runnable { _recomputeModifiers() }) },
|
ListenableMap<UUID, Int>().also { it.addListener(Runnable { _recomputeModifiers() }) },
|
||||||
keyCodec = UUIDValueCodec,
|
keyCodec = StreamCodecs.UUID,
|
||||||
valueCodec = IntValueCodec,
|
valueCodec = StreamCodecs.VAR_INT,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -267,12 +262,12 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
}, backingMap = this.exopackSlotModifierMap.delegate)
|
}, backingMap = this.exopackSlotModifierMap.delegate)
|
||||||
|
|
||||||
val regularSlotFilters = immutableList(Inventory.INVENTORY_SIZE) {
|
val regularSlotFilters = immutableList(Inventory.INVENTORY_SIZE) {
|
||||||
syncher.add(null, ItemValueCodec.nullable())
|
syncher.add(null, StreamCodecs.ITEM_TYPE.nullable())
|
||||||
}
|
}
|
||||||
|
|
||||||
val slotsChargeFlag = syncher.SetSlot(
|
val slotsChargeFlag = syncher.SetSlot(
|
||||||
ListenableSet(IntAVLTreeSet()),
|
ListenableSet(IntAVLTreeSet()),
|
||||||
VarIntValueCodec,
|
StreamCodecs.VAR_INT,
|
||||||
).delegate
|
).delegate
|
||||||
|
|
||||||
private fun slotChargeToDefault() {
|
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)
|
val old = ArrayList<AndroidResearch>(research.size)
|
||||||
old.addAll(research.values)
|
old.addAll(research.values)
|
||||||
research.clear()
|
research.clear()
|
||||||
@ -788,7 +783,7 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
val new = AndroidResearchManager.get(v.type.id) ?: continue
|
val new = AndroidResearchManager.get(v.type.id) ?: continue
|
||||||
|
|
||||||
research[new] = AndroidResearch(new, this).also {
|
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) {
|
if (ply is ServerPlayer) {
|
||||||
feature.invalidateNetwork()
|
feature.invalidateNetwork()
|
||||||
val payload = feature.collectNetworkPayload()
|
val payload = feature.syncherRemote.write(ply.registryAccess())
|
||||||
|
|
||||||
if (payload != null) {
|
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) {
|
if (ply is ServerPlayer) {
|
||||||
factory.invalidateNetwork()
|
factory.invalidateNetwork()
|
||||||
val payload = factory.collectNetworkPayload()
|
val payload = factory.syncherRemote.write(ply.registryAccess())
|
||||||
|
|
||||||
if (payload != null) {
|
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 {
|
tag["research"] = ListTag().also {
|
||||||
for ((type, instance) in research) {
|
for ((type, instance) in research) {
|
||||||
it.add(instance.serializeNBT().also {
|
it.add(instance.serializeNBT(registry).also {
|
||||||
it["id"] = type.id.toString()
|
it["id"] = type.id.toString()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1031,7 +1026,7 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
|
|
||||||
if (research != null) {
|
if (research != null) {
|
||||||
val instance = AndroidResearch(research, this)
|
val instance = AndroidResearch(research, this)
|
||||||
instance.deserializeNBT(researchTag)
|
instance.deserializeNBT(registry, researchTag)
|
||||||
this.research[research] = instance
|
this.research[research] = instance
|
||||||
|
|
||||||
if (instance.isResearched && ply is ServerPlayer) {
|
if (instance.isResearched && ply is ServerPlayer) {
|
||||||
@ -1301,7 +1296,7 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
|
|
||||||
syncher.observe()
|
syncher.observe()
|
||||||
publicSyncher.observe()
|
publicSyncher.observe()
|
||||||
val payload = privateSyncherRemote.write()
|
val payload = privateSyncherRemote.write(ply.registryAccess())
|
||||||
|
|
||||||
if (payload != null) {
|
if (payload != null) {
|
||||||
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload, false))
|
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload, false))
|
||||||
@ -1315,14 +1310,14 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val payload2 = remote.write()
|
val payload2 = remote.write(ply.registryAccess())
|
||||||
|
|
||||||
if (payload2 != null) {
|
if (payload2 != null) {
|
||||||
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload2, true, this.ply.uuid))
|
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload2, true, this.ply.uuid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val payload3 = publicSyncherRemote.write()
|
val payload3 = publicSyncherRemote.write(ply.registryAccess())
|
||||||
|
|
||||||
if (payload3 != null) {
|
if (payload3 != null) {
|
||||||
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload3, true))
|
PacketDistributor.sendToPlayer(ply as ServerPlayer, MatteryPlayerDataPacket(payload3, true))
|
||||||
@ -1337,18 +1332,18 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (instance in research.values) {
|
for (instance in research.values) {
|
||||||
val researchPayload = instance.collectNetworkPayload()
|
val researchPayload = instance.syncherRemote.write(ply.registryAccess())
|
||||||
|
|
||||||
if (researchPayload != null) {
|
if (researchPayload != null) {
|
||||||
sendNetwork(AndroidResearchSyncPacket(instance.type, researchPayload, null))
|
sendNetwork(AndroidResearchSyncPacket(instance.type, researchPayload))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (instance in featureMap.values) {
|
for (instance in featureMap.values) {
|
||||||
val featurePayload = instance.collectNetworkPayload()
|
val featurePayload = instance.syncherRemote.write(ply.registryAccess())
|
||||||
|
|
||||||
if (featurePayload != null) {
|
if (featurePayload != null) {
|
||||||
sendNetwork(AndroidFeatureSyncPacket(instance.type, featurePayload, null))
|
sendNetwork(AndroidFeatureSyncPacket(instance.type, featurePayload))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import net.minecraft.world.item.ItemStack
|
|||||||
import net.minecraft.world.ticks.ContainerSingleItem
|
import net.minecraft.world.ticks.ContainerSingleItem
|
||||||
import net.neoforged.neoforge.capabilities.Capabilities
|
import net.neoforged.neoforge.capabilities.Capabilities
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
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.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
|
@ -24,7 +24,7 @@ import net.minecraft.world.item.Items
|
|||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
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.kommons.io.VarIntValueCodec
|
||||||
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
|
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
|
||||||
import ru.dbotthepony.mc.otm.core.addSorted
|
import ru.dbotthepony.mc.otm.core.addSorted
|
||||||
|
@ -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)
|
|
||||||
}
|
|
@ -2,14 +2,14 @@ package ru.dbotthepony.mc.otm.menu
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.mojang.datafixers.util.Pair
|
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.IntArrayList
|
||||||
import it.unimi.dsi.fastutil.ints.IntCollection
|
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.Reference2ObjectFunction
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList
|
import it.unimi.dsi.fastutil.objects.ReferenceArrayList
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.server.level.ServerPlayer
|
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.inventory.Slot
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
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.minecraft.world.level.block.entity.BlockEntity
|
||||||
import net.neoforged.neoforge.network.PacketDistributor
|
import net.neoforged.neoforge.network.PacketDistributor
|
||||||
import net.neoforged.neoforge.network.handling.IPayloadContext
|
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.Delegate
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
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.UpgradeType
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
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.cos.cosmeticArmorSlots
|
||||||
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
|
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
|
||||||
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
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.collect.ConditionalSet
|
||||||
import ru.dbotthepony.mc.otm.core.immutableList
|
import ru.dbotthepony.mc.otm.core.immutableList
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
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.input.InstantBooleanInput
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
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.MenuDataPacket
|
||||||
import ru.dbotthepony.mc.otm.network.SetCarriedPacket
|
import ru.dbotthepony.mc.otm.network.SetCarriedPacket
|
||||||
import java.io.DataInputStream
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
import java.io.DataOutputStream
|
import ru.dbotthepony.mc.otm.network.decode
|
||||||
import java.math.BigDecimal
|
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.*
|
||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
@ -115,13 +109,13 @@ abstract class MatteryMenu(
|
|||||||
|
|
||||||
private val playerInputs = ArrayList<PlayerInput<*>>()
|
private val playerInputs = ArrayList<PlayerInput<*>>()
|
||||||
|
|
||||||
class PlayerInputPacket(val containerId: Int, val inputId: Int, val payload: ByteArray) : CustomPacketPayload {
|
class PlayerInputPacket(val containerId: Int, val inputId: Int, val payload: ByteArrayList) : CustomPacketPayload {
|
||||||
constructor(buff: FriendlyByteBuf) : this(buff.readVarInt(), buff.readVarInt(), ByteArray(buff.readableBytes()).also { buff.readBytes(it) })
|
constructor(buff: FriendlyByteBuf) : this(buff.readVarInt(), buff.readVarInt(), buff.readByteListUnbounded())
|
||||||
|
|
||||||
fun write(buff: FriendlyByteBuf) {
|
fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeVarInt(containerId)
|
buff.writeVarInt(containerId)
|
||||||
buff.writeVarInt(inputId)
|
buff.writeVarInt(inputId)
|
||||||
buff.writeBytes(payload)
|
buff.writeByteListUnbounded(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play(context: IPayloadContext) {
|
fun play(context: IPayloadContext) {
|
||||||
@ -129,7 +123,7 @@ abstract class MatteryMenu(
|
|||||||
if (menu.containerId != containerId || !menu.stillValid(context.player())) return
|
if (menu.containerId != containerId || !menu.stillValid(context.player())) return
|
||||||
val input = menu.playerInputs.getOrNull(inputId) ?: return
|
val input = menu.playerInputs.getOrNull(inputId) ?: return
|
||||||
if (!input.test(context.player())) 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> {
|
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> {
|
||||||
@ -152,7 +146,7 @@ abstract class MatteryMenu(
|
|||||||
/**
|
/**
|
||||||
* Client->Server input
|
* 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
|
val id = playerInputs.size
|
||||||
var allowSpectators by mSynchronizer.boolean(allowSpectators)
|
var allowSpectators by mSynchronizer.boolean(allowSpectators)
|
||||||
|
|
||||||
@ -171,16 +165,13 @@ abstract class MatteryMenu(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun test(player: Player?): Boolean {
|
override fun test(player: Player): Boolean {
|
||||||
if (player == null) return false
|
|
||||||
return filters.all { it.test(player) }
|
return filters.all { it.test(player) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(value: V) {
|
override fun accept(value: V) {
|
||||||
if (test(minecraft.player as Player?)) {
|
if (test(player)) {
|
||||||
val stream = FastByteArrayOutputStream()
|
PacketDistributor.sendToServer(PlayerInputPacket(containerId, id, codec.encode(player.registryAccess(), value)))
|
||||||
codec.write(DataOutputStream(stream), value)
|
|
||||||
PacketDistributor.sendToServer(PlayerInputPacket(containerId, id, stream.array.copyOfRange(0, stream.length)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +181,7 @@ abstract class MatteryMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class SortInput(val container: Container, val settings: IItemStackSortingSettings) {
|
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)
|
container.sortWithIndices(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,20 +191,19 @@ abstract class MatteryMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun oneWayInput(allowSpectators: Boolean = false, handler: () -> Unit): PlayerInput<Nothing?> {
|
fun oneWayInput(allowSpectators: Boolean = false, handler: () -> Unit): PlayerInput<Nothing?> {
|
||||||
return PlayerInput(NullValueCodec, allowSpectators) {
|
return PlayerInput(StreamCodecs.NOTHING, allowSpectators) {
|
||||||
handler.invoke()
|
handler.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bigDecimalInput(allowSpectators: Boolean = false, handler: (BigDecimal) -> Unit) = PlayerInput(BigDecimalValueCodec, allowSpectators, handler)
|
fun decimalInput(allowSpectators: Boolean = false, handler: (Decimal) -> Unit) = PlayerInput(StreamCodecs.DECIMAL, allowSpectators, handler)
|
||||||
fun decimalInput(allowSpectators: Boolean = false, handler: (Decimal) -> Unit) = PlayerInput(DecimalValueCodec, allowSpectators, handler)
|
fun booleanInput(allowSpectators: Boolean = false, handler: (Boolean) -> Unit) = PlayerInput(StreamCodecs.BOOLEAN, allowSpectators, handler)
|
||||||
fun booleanInput(allowSpectators: Boolean = false, handler: (Boolean) -> Unit) = PlayerInput(BooleanValueCodec, allowSpectators, handler)
|
fun itemInput(allowSpectators: Boolean = false, handler: (Item) -> Unit) = PlayerInput(StreamCodecs.ITEM_TYPE, allowSpectators, handler)
|
||||||
fun itemInput(allowSpectators: Boolean = false, handler: (Item) -> Unit) = PlayerInput(ItemValueCodec, allowSpectators, handler)
|
fun itemStackInput(allowSpectators: Boolean = false, handler: (ItemStack) -> Unit) = PlayerInput(ItemStack.STREAM_CODEC.wrap(), allowSpectators, handler)
|
||||||
fun itemStackInput(allowSpectators: Boolean = false, handler: (ItemStack) -> Unit) = PlayerInput(ItemStackValueCodec, allowSpectators, handler)
|
fun nullableItemInput(allowSpectators: Boolean = false, handler: (Item?) -> Unit) = PlayerInput(StreamCodecs.ITEM_TYPE.nullable(), allowSpectators, handler)
|
||||||
fun nullableItemInput(allowSpectators: Boolean = false, handler: (Item?) -> Unit) = PlayerInput(ItemValueCodec.nullable(), allowSpectators, handler)
|
fun stringInput(allowSpectators: Boolean = false, handler: (String) -> Unit) = PlayerInput(StreamCodecs.STRING, allowSpectators, handler)
|
||||||
fun stringInput(allowSpectators: Boolean = false, handler: (String) -> Unit) = PlayerInput(BinaryStringCodec, allowSpectators, handler)
|
fun floatInput(allowSpectators: Boolean = false, handler: (Float) -> Unit) = PlayerInput(StreamCodecs.FLOAT, allowSpectators, handler)
|
||||||
fun floatInput(allowSpectators: Boolean = false, handler: (Float) -> Unit) = PlayerInput(FloatValueCodec, allowSpectators, handler)
|
fun intInput(allowSpectators: Boolean = false, handler: (Int) -> Unit) = PlayerInput(StreamCodecs.INT, allowSpectators, handler)
|
||||||
fun intInput(allowSpectators: Boolean = false, handler: (Int) -> Unit) = PlayerInput(VarIntValueCodec, allowSpectators, handler)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hotbar + inventory + Exopack (in this order)
|
* hotbar + inventory + Exopack (in this order)
|
||||||
@ -331,7 +321,7 @@ abstract class MatteryMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun mayPickup(player: Player): Boolean {
|
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 {
|
override fun getMaxStackSize(): Int {
|
||||||
@ -402,7 +392,7 @@ abstract class MatteryMenu(
|
|||||||
beforeBroadcast()
|
beforeBroadcast()
|
||||||
|
|
||||||
mSynchronizer.observe()
|
mSynchronizer.observe()
|
||||||
val payload = synchronizerRemote.write()
|
val payload = synchronizerRemote.write(player.registryAccess())
|
||||||
|
|
||||||
if (payload != null) {
|
if (payload != null) {
|
||||||
PacketDistributor.sendToPlayer(player as ServerPlayer, MenuDataPacket(containerId, payload))
|
PacketDistributor.sendToPlayer(player as ServerPlayer, MenuDataPacket(containerId, payload))
|
||||||
@ -459,11 +449,11 @@ abstract class MatteryMenu(
|
|||||||
val field: Delegate<Item?>
|
val field: Delegate<Item?>
|
||||||
|
|
||||||
if (container is IMatteryContainer) {
|
if (container is IMatteryContainer) {
|
||||||
input = PlayerInput(ItemValueCodec.nullable(), handler = { container.setSlotFilter(pSlot.slotIndex, it) })
|
input = PlayerInput(StreamCodecs.ITEM_TYPE_NULLABLE, handler = { container.setSlotFilter(pSlot.slotIndex, it) })
|
||||||
field = mSynchronizer.add(delegate = { container.getSlotFilter(pSlot.slotIndex) }, ItemValueCodec.nullable())
|
field = mSynchronizer.add(delegate = { container.getSlotFilter(pSlot.slotIndex) }, StreamCodecs.ITEM_TYPE_NULLABLE)
|
||||||
} else {
|
} else {
|
||||||
input = PlayerInput(ItemValueCodec.nullable(), handler = { throw UnsupportedOperationException() })
|
input = PlayerInput(StreamCodecs.ITEM_TYPE_NULLABLE, handler = { throw UnsupportedOperationException() })
|
||||||
field = mSynchronizer.add(delegate = { null }, ItemValueCodec.nullable())
|
field = mSynchronizer.add(delegate = { null }, StreamCodecs.ITEM_TYPE_NULLABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
pSlot.filter = Delegate.Of(getter = field::get, setter = input::accept)
|
pSlot.filter = Delegate.Of(getter = field::get, setter = input::accept)
|
||||||
|
@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.menu.widget
|
|||||||
|
|
||||||
import net.neoforged.neoforge.fluids.FluidStack
|
import net.neoforged.neoforge.fluids.FluidStack
|
||||||
import net.neoforged.neoforge.fluids.capability.IFluidHandler
|
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.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.container.get
|
import ru.dbotthepony.mc.otm.container.get
|
||||||
import ru.dbotthepony.mc.otm.core.util.FluidStackValueCodec
|
import ru.dbotthepony.mc.otm.core.util.FluidStackValueCodec
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.widget
|
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.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.widget
|
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.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.widget
|
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.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop
|
import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.network
|
package ru.dbotthepony.mc.otm.network
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
@ -32,17 +33,15 @@ import ru.dbotthepony.mc.otm.registry.MRegistry
|
|||||||
import ru.dbotthepony.mc.otm.registry.MSoundEvents
|
import ru.dbotthepony.mc.otm.registry.MSoundEvents
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val dataList: FastByteArrayOutputStream?, val dataBytes: ByteArray?) : CustomPacketPayload {
|
class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val data: ByteArrayList) : CustomPacketPayload {
|
||||||
fun write(buff: FriendlyByteBuf) {
|
fun write(buff: RegistryFriendlyByteBuf) {
|
||||||
dataList ?: throw NullPointerException("No byte list is present")
|
|
||||||
buff.writeInt(MRegistry.ANDROID_FEATURES.getId(type))
|
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) {
|
fun play(context: IPayloadContext) {
|
||||||
dataBytes ?: throw NullPointerException("No data bytes array is present")
|
|
||||||
val android = minecraft.player?.matteryPlayer ?: return
|
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> {
|
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)
|
StreamCodec.ofMember(AndroidFeatureSyncPacket::write, ::read)
|
||||||
|
|
||||||
fun read(buff: FriendlyByteBuf): AndroidFeatureSyncPacket {
|
fun read(buff: RegistryFriendlyByteBuf): AndroidFeatureSyncPacket {
|
||||||
return AndroidFeatureSyncPacket(
|
return AndroidFeatureSyncPacket(
|
||||||
MRegistry.ANDROID_FEATURES.byIdOrThrow(buff.readInt()),
|
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 {
|
CustomPacketPayload {
|
||||||
fun write(buff: FriendlyByteBuf) {
|
fun write(buff: FriendlyByteBuf) {
|
||||||
dataList ?: throw NullPointerException("No byte list is present")
|
|
||||||
buff.writeUtf(type.id.toString())
|
buff.writeUtf(type.id.toString())
|
||||||
buff.writeBytes(dataList.array, 0, dataList.length)
|
buff.writeBytes(dataList.elements(), 0, dataList.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play(context: IPayloadContext) {
|
fun play(context: IPayloadContext) {
|
||||||
dataBytes ?: throw NullPointerException("No data bytes array is present")
|
|
||||||
val android = minecraft.player?.matteryPlayer ?: return
|
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> {
|
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> {
|
||||||
@ -101,7 +98,7 @@ class AndroidResearchSyncPacket(val type: AndroidResearchType, val dataList: Fas
|
|||||||
fun read(buff: FriendlyByteBuf): AndroidResearchSyncPacket {
|
fun read(buff: FriendlyByteBuf): AndroidResearchSyncPacket {
|
||||||
return AndroidResearchSyncPacket(
|
return AndroidResearchSyncPacket(
|
||||||
AndroidResearchManager[net.minecraft.resources.ResourceLocation.parse(buff.readUtf())] ?: throw NoSuchElementException(),
|
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) })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
857
src/main/kotlin/ru/dbotthepony/mc/otm/network/DelegateSyncher.kt
Normal file
857
src/main/kotlin/ru/dbotthepony/mc/otm/network/DelegateSyncher.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,14 @@
|
|||||||
package ru.dbotthepony.mc.otm.network
|
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.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import net.minecraft.network.codec.StreamCodec
|
import net.minecraft.network.codec.StreamCodec
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
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.handling.IPayloadContext
|
||||||
import net.neoforged.neoforge.network.registration.PayloadRegistrar
|
import net.neoforged.neoforge.network.registration.PayloadRegistrar
|
||||||
import kotlin.reflect.KFunction1
|
import kotlin.reflect.KFunction1
|
||||||
@ -18,3 +24,63 @@ fun <T : CustomPacketPayload> PayloadRegistrar.playToServer(
|
|||||||
codec: StreamCodec<FriendlyByteBuf, T>,
|
codec: StreamCodec<FriendlyByteBuf, T>,
|
||||||
handler: KFunction1<IPayloadContext, Unit>
|
handler: KFunction1<IPayloadContext, Unit>
|
||||||
): PayloadRegistrar = playToServer(type, codec) { _, context -> handler(context) }
|
): 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.network
|
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.core.particles.ParticleTypes
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
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.readItem
|
||||||
import ru.dbotthepony.mc.otm.core.writeItem
|
import ru.dbotthepony.mc.otm.core.writeItem
|
||||||
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
|
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
class MatteryPlayerDataPacket(val bytes: ByteArrayList, val isPublic: Boolean, val target: UUID? = null) : CustomPacketPayload {
|
||||||
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)
|
|
||||||
|
|
||||||
fun write(buff: FriendlyByteBuf) {
|
fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeBoolean(target != null)
|
buff.writeBoolean(target != null)
|
||||||
|
|
||||||
@ -43,7 +38,7 @@ class MatteryPlayerDataPacket(val bytes: ByteArray, val length: Int, val isPubli
|
|||||||
buff.writeUUID(target)
|
buff.writeUUID(target)
|
||||||
|
|
||||||
buff.writeBoolean(isPublic)
|
buff.writeBoolean(isPublic)
|
||||||
buff.writeBytes(bytes, 0, length)
|
buff.writeBytes(bytes.elements(), 0, bytes.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play(context: IPayloadContext) {
|
fun play(context: IPayloadContext) {
|
||||||
@ -56,9 +51,9 @@ class MatteryPlayerDataPacket(val bytes: ByteArray, val length: Int, val isPubli
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isPublic) {
|
if (isPublic) {
|
||||||
player.publicSyncher.read(ByteArrayInputStream(bytes, 0, length))
|
player.publicSyncher.read(bytes, context.player().registryAccess())
|
||||||
} else {
|
} 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 isPublic = buff.readBoolean()
|
||||||
val readable = buff.readableBytes()
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.network
|
package ru.dbotthepony.mc.otm.network
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
import net.minecraft.network.codec.StreamCodec
|
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 ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
class MenuDataPacket(val containerId: Int, val bytes: ByteArray, val length: Int) : CustomPacketPayload {
|
class MenuDataPacket(val containerId: Int, val bytes: ByteArrayList) : CustomPacketPayload {
|
||||||
constructor(containerId: Int, stream: FastByteArrayOutputStream) : this(containerId, stream.array, stream.length)
|
|
||||||
|
|
||||||
fun write(buff: FriendlyByteBuf) {
|
fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeVarInt(containerId)
|
buff.writeVarInt(containerId)
|
||||||
buff.writeBytes(bytes, 0, length)
|
buff.writeByteListUnbounded(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play(context: IPayloadContext) {
|
fun play(context: IPayloadContext) {
|
||||||
if (containerId == ExopackInventoryMenu.CONTAINER_ID) {
|
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 {
|
} else {
|
||||||
val menu = minecraft.player?.containerMenu as? MatteryMenu ?: return
|
val menu = context.player().containerMenu as? MatteryMenu ?: return
|
||||||
|
|
||||||
if (menu.containerId == containerId)
|
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 {
|
fun read(buff: FriendlyByteBuf): MenuDataPacket {
|
||||||
val containerId = buff.readVarInt()
|
val containerId = buff.readVarInt()
|
||||||
val readable = buff.readableBytes()
|
return MenuDataPacket(containerId, buff.readByteListUnbounded())
|
||||||
return MenuDataPacket(containerId, ByteArray(readable).also(buff::readBytes), readable)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,37 @@
|
|||||||
package ru.dbotthepony.mc.otm.network
|
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.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import net.minecraft.network.codec.StreamCodec
|
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
|
// because mojang didn't bother
|
||||||
object StreamCodecs {
|
object StreamCodecs {
|
||||||
val INT: StreamCodec<FriendlyByteBuf, Int> = StreamCodec.of(FriendlyByteBuf::writeInt, FriendlyByteBuf::readInt)
|
val NOTHING: MatteryStreamCodec<ByteBuf, Nothing?> = StreamCodec.of<ByteBuf, Nothing?>({ _, _ -> }, { null }).wrap()
|
||||||
val LONG: StreamCodec<FriendlyByteBuf, Long> = StreamCodec.of(FriendlyByteBuf::writeLong, FriendlyByteBuf::readLong)
|
|
||||||
val DOUBLE: StreamCodec<FriendlyByteBuf, Double> = StreamCodec.of(FriendlyByteBuf::writeDouble, FriendlyByteBuf::readDouble)
|
val BYTE: MatteryStreamCodec<ByteBuf, Byte> = StreamCodec.of({ s, v -> s.writeByte(v.toInt()) }, ByteBuf::readByte).wrap()
|
||||||
val FLOAT: StreamCodec<FriendlyByteBuf, Float> = StreamCodec.of(FriendlyByteBuf::writeFloat, FriendlyByteBuf::readFloat)
|
val SHORT = StreamCodec.of({ s, v -> s.writeShort(v.toInt()) }, ByteBuf::readShort).wrap()
|
||||||
val BOOLEAN: StreamCodec<FriendlyByteBuf, Boolean> = StreamCodec.of(FriendlyByteBuf::writeBoolean, FriendlyByteBuf::readBoolean)
|
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()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user