first commit

This commit is contained in:
Patrick
2026-05-01 19:42:33 +02:00
commit e448a542dc
402 changed files with 23697 additions and 0 deletions
+4
View File
@@ -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)
}
}
}
@@ -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"
}
}
}
}