first commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
modApi(libs.bundles.fabric)
|
||||
modApi(libs.bundles.silk)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package gg.norisk.datatracker.mixin;
|
||||
|
||||
import gg.norisk.datatracker.entity.ISyncedEntity;
|
||||
import gg.norisk.datatracker.entity.ISyncedEntityKt;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class EntityMixin implements ISyncedEntity {
|
||||
@Unique
|
||||
private final Map<String, Object> syncedValues = new HashMap<>();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> getSyncedValuesMap() {
|
||||
return syncedValues;
|
||||
}
|
||||
|
||||
//this is used for quest-api to trigger nbt condition like isSubwaySurfers
|
||||
@Inject(method = "writeNbt", at = @At("HEAD"))
|
||||
private void injected(NbtCompound nbtCompound, CallbackInfoReturnable<NbtCompound> cir) {
|
||||
ISyncedEntityKt.writeSyncedNbtData((Entity) (Object) this, nbtCompound);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package gg.norisk.datatracker.mixin;
|
||||
|
||||
import gg.norisk.datatracker.entity.ISyncedEntityKt;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.EntityTrackerEntry;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(EntityTrackerEntry.class)
|
||||
public abstract class EntityTrackerEntryMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private Entity entity;
|
||||
|
||||
@Inject(method = "startTracking", at = @At("TAIL"))
|
||||
private void startTrackingSync(ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) {
|
||||
ISyncedEntityKt.syncValues(this.entity, serverPlayerEntity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package gg.norisk.datatracker.mixin.accessor;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.entity.EntityLookup;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(World.class)
|
||||
public interface WorldAccessor {
|
||||
@Invoker("getEntityLookup")
|
||||
EntityLookup<Entity> invokeGetEntityLookup();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package gg.norisk.datatracker
|
||||
|
||||
import gg.norisk.datatracker.entity.initSyncedEntitiesClient
|
||||
import net.fabricmc.api.ClientModInitializer
|
||||
import net.fabricmc.api.ModInitializer
|
||||
import net.minecraft.util.Identifier
|
||||
import org.apache.logging.log4j.LogManager
|
||||
|
||||
object DataTracker : ModInitializer, ClientModInitializer {
|
||||
private const val MOD_ID = "datatracker"
|
||||
fun String.toId() = Identifier.of(MOD_ID, this)
|
||||
val logger = LogManager.getLogger(MOD_ID)
|
||||
override fun onInitialize() {
|
||||
}
|
||||
|
||||
override fun onInitializeClient() {
|
||||
initSyncedEntitiesClient()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package gg.norisk.datatracker.entity
|
||||
|
||||
import gg.norisk.datatracker.DataTracker.logger
|
||||
import gg.norisk.datatracker.DataTracker.toId
|
||||
import gg.norisk.datatracker.serialization.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtHelper
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.silkmc.silk.core.event.Event
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.nbt.toNbt
|
||||
import net.silkmc.silk.network.packet.s2cPacket
|
||||
import org.joml.Vector3f
|
||||
import java.util.*
|
||||
|
||||
interface ISyncedEntity {
|
||||
fun getSyncedValuesMap(): MutableMap<String, Any>
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class EntityWrapper(
|
||||
val entityId: Int,
|
||||
val key: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DataWrapper(
|
||||
val value: String,
|
||||
val clazz: String,
|
||||
)
|
||||
|
||||
open class SyncedValueChangeEvent(val key: String, val entity: Entity, val oldValue: Any?)
|
||||
|
||||
val syncedValueChangeEvent = Event.onlySync<SyncedValueChangeEvent>()
|
||||
|
||||
fun initSyncedEntitiesClient() {
|
||||
addSyncedData.receiveOnClient { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = true) {
|
||||
//logger.info("###Received Packet {}", packet)
|
||||
val entity = context.client.world?.getEntityById(packet.first.entityId)
|
||||
//logger.info("###Found Entity {}", entity)
|
||||
|
||||
if (registeredTypes.none { packet.second.clazz == it.key.toString() }) {
|
||||
throw Error("Please register a Serializer for $packet")
|
||||
}
|
||||
|
||||
for ((clazz, serializer) in registeredTypes) {
|
||||
if (packet.second.clazz == clazz.toString()) {
|
||||
val decodedValue = runCatching { dataTrackerJson.decodeFromString(serializer as KSerializer<Any>, packet.second.value) }.onFailure { it.printStackTrace() }.getOrNull()
|
||||
logger.info("Setting $entity $packet")
|
||||
entity?.setSyncedData(packet.first.key, decodedValue)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for ((clazz, value) in registeredTypes) {
|
||||
logger.info("Registering {} {}", clazz, value)
|
||||
}
|
||||
removeSyncedData.receiveOnClient { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = true) {
|
||||
val entity = context.client.world?.getEntityById(packet.entityId)
|
||||
entity?.unsetSyncedData(packet.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//hier kann man noch weitere sachen registrieren
|
||||
val registeredTypes = buildMap {
|
||||
put(Boolean::class, BooleanSerializer)
|
||||
put(String::class, StringSerializer)
|
||||
put(Float::class, FloatSerializer)
|
||||
put(Double::class, DoubleSerializer)
|
||||
put(UUID::class, UUIDSerializer)
|
||||
put(Int::class, IntSerializer)
|
||||
put(Long::class, LongSerializer)
|
||||
put(BlockPos::class, BlockPosSerializer)
|
||||
put(Vector3f::class, Vector3fSerializer)
|
||||
}.toMutableMap()
|
||||
|
||||
val addSyncedData = s2cPacket<Pair<EntityWrapper, DataWrapper>>("add-sync".toId())
|
||||
val removeSyncedData = s2cPacket<EntityWrapper>("remove-sync".toId())
|
||||
|
||||
internal val dataTrackerJson = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
fun Entity.syncValue(key: String, value: Any, player: ServerPlayerEntity? = null) {
|
||||
if (registeredTypes.none { value::class == it.key }) {
|
||||
throw Error("Please register a Serializer for $value $key ${value::class}")
|
||||
}
|
||||
|
||||
for ((clazz, serializer) in registeredTypes) {
|
||||
logger.debug("Value Class: {} Registered Class: {} {}", value::class, clazz, value::class == clazz)
|
||||
if (value::class == clazz) {
|
||||
val encodedValue = dataTrackerJson.encodeToString(serializer as KSerializer<Any>, value)
|
||||
val pair = Pair(EntityWrapper(id, key), DataWrapper(encodedValue, clazz.toString()))
|
||||
logger.debug("Sending {}", pair)
|
||||
if (player != null) {
|
||||
addSyncedData.send(pair, player)
|
||||
} else {
|
||||
addSyncedData.sendToAll(pair)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Entity.writeSyncedNbtData(nbtCompound: NbtCompound) {
|
||||
for ((key, value) in (this as ISyncedEntity).getSyncedValuesMap()) {
|
||||
runCatching {
|
||||
//JUP, wäre irgendwie so geil wenn man den registered type code von oben smarter hier einbauen kann
|
||||
//das es direkt für alles geht aber erstmal low prio...
|
||||
when (value) {
|
||||
is Int -> {
|
||||
nbtCompound.put(key, value.toNbt())
|
||||
}
|
||||
|
||||
is Boolean -> {
|
||||
nbtCompound.put(key, value.toNbt())
|
||||
}
|
||||
|
||||
is String -> {
|
||||
nbtCompound.put(key, value.toNbt())
|
||||
}
|
||||
|
||||
is Double -> {
|
||||
nbtCompound.put(key, value.toNbt())
|
||||
}
|
||||
|
||||
is BlockPos -> {
|
||||
nbtCompound.put(key, NbtHelper.fromBlockPos(value))
|
||||
}
|
||||
|
||||
is Float -> {
|
||||
nbtCompound.put(key, value.toNbt())
|
||||
}
|
||||
|
||||
is Long -> {
|
||||
nbtCompound.put(key, value.toNbt())
|
||||
}
|
||||
|
||||
else -> {
|
||||
logger.info("NOT SUPPORTED: [$key/$value] ${value::class}")
|
||||
}
|
||||
}
|
||||
}.onSuccess {}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Entity.getSyncedData(key: String): T? {
|
||||
return (this as ISyncedEntity).getSyncedValuesMap()[key] as? T?
|
||||
}
|
||||
|
||||
fun <T> Entity.hasSyncedData(key: String): Boolean {
|
||||
return (this as ISyncedEntity).getSyncedValuesMap().containsKey(key)
|
||||
}
|
||||
|
||||
fun Entity.syncValues(player: ServerPlayerEntity? = null) {
|
||||
for ((key, value) in (this as ISyncedEntity).getSyncedValuesMap()) {
|
||||
if (!world.isClient) {
|
||||
syncValue(key, value, player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Entity.unsetSyncedData(key: String, player: ServerPlayerEntity? = null) {
|
||||
logger.debug("Client={} Unset Synced Data {} {} {}", world.isClient, key, player)
|
||||
val oldValue = this.getSyncedData<Any?>(key)
|
||||
(this as ISyncedEntity).getSyncedValuesMap().remove(key)
|
||||
syncedValueChangeEvent.invoke(SyncedValueChangeEvent(key, this, oldValue))
|
||||
if (!world.isClient) {
|
||||
if (player != null) {
|
||||
removeSyncedData.send(EntityWrapper(id, key), player)
|
||||
} else {
|
||||
//TODO ka ob das jemals probleme machen sollte aber eig nicht oder
|
||||
removeSyncedData.sendToAll(EntityWrapper(id, key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun Entity.setSyncedData(key: String, value: Any?, player: ServerPlayerEntity? = null) {
|
||||
logger.debug("Client={} Synced Data {} {} {}", world.isClient, key, value, player)
|
||||
val oldValue = this.getSyncedData<Any?>(key)
|
||||
if (value == null) {
|
||||
(this as ISyncedEntity).getSyncedValuesMap().remove(key)
|
||||
} else {
|
||||
(this as ISyncedEntity).getSyncedValuesMap()[key] = value
|
||||
}
|
||||
syncedValueChangeEvent.invoke(SyncedValueChangeEvent(key, this, oldValue))
|
||||
if (!world.isClient) {
|
||||
if (value != null) {
|
||||
syncValue(key, value, player)
|
||||
} else {
|
||||
unsetSyncedData(key, player)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package gg.norisk.datatracker.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.element
|
||||
import kotlinx.serialization.encoding.*
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
object BlockPosSerializer : KSerializer<BlockPos> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("BlockPos") {
|
||||
element<Int>("x")
|
||||
element<Int>("y")
|
||||
element<Int>("z")
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: BlockPos) {
|
||||
encoder.encodeStructure(descriptor) {
|
||||
encodeIntElement(descriptor, 0, value.x)
|
||||
encodeIntElement(descriptor, 1, value.y)
|
||||
encodeIntElement(descriptor, 2, value.z)
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): BlockPos {
|
||||
return decoder.decodeStructure(descriptor) {
|
||||
var x = 0
|
||||
var y = 0
|
||||
var z = 0
|
||||
while (true) {
|
||||
when (val index = decodeElementIndex(descriptor)) {
|
||||
0 -> x = decodeIntElement(descriptor, 0)
|
||||
1 -> y = decodeIntElement(descriptor, 1)
|
||||
2 -> z = decodeIntElement(descriptor, 2)
|
||||
CompositeDecoder.DECODE_DONE -> break
|
||||
else -> throw SerializationException("Unknown index $index")
|
||||
}
|
||||
}
|
||||
BlockPos(x, y, z)
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package gg.norisk.datatracker.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@PublishedApi
|
||||
internal object StringSerializer : KSerializer<String> {
|
||||
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||
override fun serialize(encoder: Encoder, value: String): Unit = String.serializer().serialize(encoder, value)
|
||||
override fun deserialize(decoder: Decoder): String = String.serializer().deserialize(decoder)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal object BooleanSerializer : KSerializer<Boolean> {
|
||||
override val descriptor: SerialDescriptor = Boolean.serializer().descriptor
|
||||
override fun serialize(encoder: Encoder, value: Boolean): Unit = Boolean.serializer().serialize(encoder, value)
|
||||
override fun deserialize(decoder: Decoder): Boolean = Boolean.serializer().deserialize(decoder)
|
||||
}
|
||||
|
||||
|
||||
@PublishedApi
|
||||
internal object IntSerializer : KSerializer<Int> {
|
||||
override val descriptor: SerialDescriptor = Int.serializer().descriptor
|
||||
override fun serialize(encoder: Encoder, value: Int): Unit = Int.serializer().serialize(encoder, value)
|
||||
override fun deserialize(decoder: Decoder): Int = Int.serializer().deserialize(decoder)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal object FloatSerializer : KSerializer<Float> {
|
||||
override val descriptor: SerialDescriptor = Float.serializer().descriptor
|
||||
override fun serialize(encoder: Encoder, value: Float): Unit = Float.serializer().serialize(encoder, value)
|
||||
override fun deserialize(decoder: Decoder): Float = Float.serializer().deserialize(decoder)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal object DoubleSerializer : KSerializer<Double> {
|
||||
override val descriptor: SerialDescriptor = Double.serializer().descriptor
|
||||
override fun serialize(encoder: Encoder, value: Double): Unit = Double.serializer().serialize(encoder, value)
|
||||
override fun deserialize(decoder: Decoder): Double = Double.serializer().deserialize(decoder)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal object LongSerializer : KSerializer<Long> {
|
||||
override val descriptor: SerialDescriptor = Long.serializer().descriptor
|
||||
override fun serialize(encoder: Encoder, value: Long): Unit = Long.serializer().serialize(encoder, value)
|
||||
override fun deserialize(decoder: Decoder): Long = Long.serializer().deserialize(decoder)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package gg.norisk.datatracker.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.util.*
|
||||
|
||||
object UUIDSerializer : KSerializer<UUID> {
|
||||
override val descriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
|
||||
|
||||
override fun deserialize(decoder: Decoder): UUID {
|
||||
return UUID.fromString(decoder.decodeString())
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: UUID) {
|
||||
encoder.encodeString(value.toString())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package gg.norisk.datatracker.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.element
|
||||
import kotlinx.serialization.encoding.*
|
||||
import org.joml.Vector3f
|
||||
|
||||
object Vector3fSerializer : KSerializer<Vector3f> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Vector3f") {
|
||||
element<Float>("x")
|
||||
element<Float>("y")
|
||||
element<Float>("z")
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Vector3f) {
|
||||
encoder.encodeStructure(descriptor) {
|
||||
encodeFloatElement(descriptor, 0, value.x)
|
||||
encodeFloatElement(descriptor, 1, value.y)
|
||||
encodeFloatElement(descriptor, 2, value.z)
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Vector3f {
|
||||
return decoder.decodeStructure(descriptor) {
|
||||
var x = 0f
|
||||
var y = 0f
|
||||
var z = 0f
|
||||
|
||||
while (true) {
|
||||
when (val index = decodeElementIndex(descriptor)) {
|
||||
0 -> x = decodeFloatElement(descriptor, index)
|
||||
1 -> y = decodeFloatElement(descriptor, index)
|
||||
2 -> z = decodeFloatElement(descriptor, index)
|
||||
CompositeDecoder.DECODE_DONE -> break
|
||||
else -> throw SerializationException("Unknown index $index")
|
||||
}
|
||||
}
|
||||
Vector3f(x, y, z)
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
@@ -0,0 +1 @@
|
||||
accessWidener v2 named
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "gg.norisk.datatracker.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"mixins": [
|
||||
"EntityMixin",
|
||||
"EntityTrackerEntryMixin",
|
||||
"accessor.WorldAccessor"
|
||||
],
|
||||
"client": [
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "DataTracker",
|
||||
"id": "datatracker",
|
||||
"version": "${version}",
|
||||
"description": "easy syncing of data between server and client",
|
||||
"authors": [
|
||||
"NoRiskk"
|
||||
],
|
||||
"icon": "assets/datatracker/icon.png",
|
||||
"license": "ARR",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.datatracker.DataTracker"
|
||||
}
|
||||
],
|
||||
"client": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.datatracker.DataTracker"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"datatracker.mixins.json"
|
||||
],
|
||||
"accessWidener": "datatracker.accesswidener",
|
||||
"depends": {
|
||||
},
|
||||
"custom": {
|
||||
"modmenu": {
|
||||
"badges": [
|
||||
"library"
|
||||
],
|
||||
"parent": {
|
||||
"id": "noriskclient"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user