Skip to content

Commit

Permalink
Bug fixes and Networking
Browse files Browse the repository at this point in the history
*Changed how datapack is synced S2C:

Before, sent raw NBT through network. Now:
1. converts NBT to SNBT;
2. uses Java's Deflater to compress the SNBT;
3. converts the compressed SNBT to byte array;
4. sends the byte array over network;
5. Reverses this using java's Inflater on the client.

This reduces the raw NBT network packet from aprox. 4KB to ~0.9KB. This was done in attempt to fix packet size issue with Velocity plugin, but also in general the player connection packet has the potential to get really massive really quickly depending on the datapack.

-Removed version checking between client and server. This was done in an attempt to make this compatible with Velocity plugin, which tends to get upset if we use regular Minecraft's connection packets.

*Changed MutableIntFlag to be implemented further up in the hierarchy (MutableWorldProperties). Added relevant casting checks. This is an attempt to fix bugs caused when other mods want to make 'dummy world properties' that don't respect the world properties hierarchy nor the client/server divide.

+Incremented version.
  • Loading branch information
CleverNucleus committed Jan 28, 2023
1 parent bf44102 commit c16acb5
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 50 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ minecraft_version=1.19.2
yarn_mappings=1.19.2+build.28
loader_version=0.14.10

mod_version = 1.4.0
mod_version = 1.4.1
maven_group = com.github.clevernucleus
archives_base_name = dataattributes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.resource.ResourceType;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerLoginNetworkHandler;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;

public class DataAttributes implements ModInitializer {
Expand All @@ -42,22 +40,13 @@ public class DataAttributes implements ModInitializer {

private static void loginQueryStart(ServerLoginNetworkHandler handler, MinecraftServer server, PacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer) {
PacketByteBuf buf = PacketByteBufs.create();
NbtCompound tag = new NbtCompound();
DataAttributes.MANAGER.toNbt(tag);
buf.writeNbt(tag);
final byte[] bytes = DataAttributes.MANAGER.getCurrentData();
buf.writeByteArray(bytes);
sender.sendPacket(HANDSHAKE, buf);
}

private static void loginQueryResponse(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender) {
if(understood) {
byte[] verClient = buf.readByteArray();

if(verClient[0] != DataAttributes.semVer[0] || verClient[1] != DataAttributes.semVer[1]) {
handler.disconnect(Text.literal("Disconnected: version mismatch. Client has version " + verClient + ". Server has version " + DataAttributes.version + "."));
}
} else {
handler.disconnect(Text.literal("Disconnected: network communication issue."));
}
// Does doing nothing here make us compatible with Velocity plugin?
}

private static void refreshAttributes(final Entity entity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,15 @@
import net.minecraft.client.network.ClientLoginNetworkHandler;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;

public class DataAttributesClient implements ClientModInitializer {
private static CompletableFuture<PacketByteBuf> loginQueryReceived(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf, Consumer<GenericFutureListener<? extends Future<? super Void>>> listenerAdder) {
NbtCompound tag = buf.readNbt();
final byte[] bytes = buf.readByteArray();

client.execute(() -> {
if(tag != null) {
DataAttributes.MANAGER.fromNbt(tag);
DataAttributes.MANAGER.apply();
}
DataAttributes.MANAGER.readFromData(bytes);
DataAttributes.MANAGER.apply();
});

PacketByteBuf bufOut = PacketByteBufs.create();
Expand All @@ -37,14 +34,12 @@ private static CompletableFuture<PacketByteBuf> loginQueryReceived(MinecraftClie
}

private static void updateReceived(MinecraftClient client, ClientPlayNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender) {
NbtCompound tag = buf.readNbt();
final byte[] bytes = buf.readByteArray();
final int updateFlag = buf.readInt();

client.execute(() -> {
if(tag != null) {
DataAttributes.MANAGER.fromNbt(tag);
DataAttributes.MANAGER.apply();
}
DataAttributes.MANAGER.readFromData(bytes);
DataAttributes.MANAGER.apply();

ClientWorld world = client.world;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

import org.slf4j.Logger;

Expand All @@ -23,6 +28,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.logging.LogUtils;

import net.fabricmc.fabric.api.resource.SimpleResourceReloadListener;
Expand All @@ -32,6 +38,8 @@
import net.minecraft.entity.attribute.DefaultAttributeRegistry;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.StringNbtReader;
import net.minecraft.nbt.visitor.StringNbtWriter;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
Expand All @@ -49,6 +57,7 @@ public final class AttributeManager implements SimpleResourceReloadListener<Attr
private Map<Identifier, EntityAttributeData> entityAttributeData = ImmutableMap.of();
private Map<Identifier, EntityTypeData> entityTypeData = ImmutableMap.of();
public Map<EntityType<? extends LivingEntity>, DefaultAttributeContainer> containers = ImmutableMap.of();
private byte[] currentData;

protected static class Wrapper {
public final Map<Identifier, EntityAttributeData> entityAttributeData;
Expand Down Expand Up @@ -248,11 +257,9 @@ private static void loadEntityTypes(ResourceManager manager, Map<Identifier, Ent
}
}

public DefaultAttributeContainer getContainer(EntityType<? extends LivingEntity> entityType) {
return this.containers.getOrDefault(entityType, DefaultAttributeRegistry.get(entityType));
}

public void toNbt(NbtCompound tag) {
private void generateCurrentData() {
StringNbtWriter writer = new StringNbtWriter();
NbtCompound nbt = new NbtCompound();
NbtCompound entityAttributeNbt = new NbtCompound();
NbtCompound entityTypeNbt = new NbtCompound();

Expand All @@ -268,14 +275,60 @@ public void toNbt(NbtCompound tag) {
entityTypeNbt.put(key.toString(), entry);
});

tag.put("Attributes", entityAttributeNbt);
tag.put("EntityTypes", entityTypeNbt);
nbt.put("Attributes", entityAttributeNbt);
nbt.put("EntityTypes", entityTypeNbt);

String snbt = writer.apply(nbt);
byte[] bytes;

try {
bytes = snbt.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
bytes = new byte[] {(byte)0};
}

Deflater deflater = new Deflater();
deflater.setInput(bytes);
deflater.finish();

byte[] compressed = new byte[8192];
int size = deflater.deflate(compressed);
deflater.end();
this.currentData = Arrays.copyOf(compressed, size);
}

public byte[] getCurrentData() {
if(this.currentData == null) return new byte[] {(byte)0};
return this.currentData;
}

public void fromNbt(NbtCompound tag) {
if(tag.contains("Attributes")) {

public void readFromData(byte[] data) {
Inflater inflater = new Inflater();
inflater.setInput(data);

byte[] cache = new byte[8192];
int size;
try {
size = inflater.inflate(cache);
} catch (DataFormatException e) {
size = 1;
}

inflater.end();
byte[] uncompressed = Arrays.copyOf(cache, size);
String snbt = new String(uncompressed);
NbtCompound nbt;

try {
nbt = StringNbtReader.parse(snbt);
} catch (CommandSyntaxException e) {
nbt = new NbtCompound();
}

if(nbt.contains("Attributes")) {
ImmutableMap.Builder<Identifier, EntityAttributeData> builder = ImmutableMap.builder();
NbtCompound nbtCompound = tag.getCompound("Attributes");
NbtCompound nbtCompound = nbt.getCompound("Attributes");
nbtCompound.getKeys().forEach(key -> {
NbtCompound entry = nbtCompound.getCompound(key);
EntityAttributeData entityAttributeData = new EntityAttributeData();
Expand All @@ -286,9 +339,9 @@ public void fromNbt(NbtCompound tag) {
this.entityAttributeData = builder.build();
}

if(tag.contains("EntityTypes")) {
if(nbt.contains("EntityTypes")) {
ImmutableMap.Builder<Identifier, EntityTypeData> builder = ImmutableMap.builder();
NbtCompound nbtCompound = tag.getCompound("EntityTypes");
NbtCompound nbtCompound = nbt.getCompound("EntityTypes");
nbtCompound.getKeys().forEach(key -> {
NbtCompound entry = nbtCompound.getCompound(key);
EntityTypeData entityTypeData = new EntityTypeData();
Expand All @@ -300,6 +353,10 @@ public void fromNbt(NbtCompound tag) {
}
}

public DefaultAttributeContainer getContainer(EntityType<? extends LivingEntity> entityType) {
return this.containers.getOrDefault(entityType, DefaultAttributeRegistry.get(entityType));
}

public void apply() {
MutableRegistryImpl.unregister(Registry.ATTRIBUTE);

Expand Down Expand Up @@ -370,6 +427,7 @@ public CompletableFuture<Void> apply(AttributeManager.Wrapper data, ResourceMana
data.entityTypeData.forEach(entityTypeData::put);
this.entityTypeData = entityTypeData.build();

this.generateCurrentData();
this.apply();
}, executor);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import net.minecraft.world.level.storage.SaveVersionInfo;

@Mixin(LevelProperties.class)
abstract class LevelPropertiesMixin implements MutableIntFlag {
abstract class LevelPropertiesMixin implements MutableWorldPropertiesMixin {

@Unique
private int data_updateFlag;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.AttributeContainer;
import net.minecraft.world.World;
import net.minecraft.world.WorldProperties;

@Mixin(value = LivingEntity.class, priority = 999)
abstract class LivingEntityMixin {
Expand All @@ -27,10 +28,16 @@ abstract class LivingEntityMixin {
@Unique
private int data_updateFlag;

private int data_checkedUpdateFlag(World world) {
WorldProperties worldProperties = world.getLevelProperties();
if(!(worldProperties instanceof MutableIntFlag)) return 0;
return ((MutableIntFlag)worldProperties).getUpdateFlag();
}

@Inject(method = "<init>", at = @At("TAIL"))
private void data_init(EntityType<? extends LivingEntity> entityType, World world, CallbackInfo ci) {
this.attributes = new AttributeContainer(DataAttributes.MANAGER.getContainer(entityType));
this.data_updateFlag = ((MutableIntFlag)world.getLevelProperties()).getUpdateFlag();
this.data_updateFlag = this.data_checkedUpdateFlag(world);
LivingEntity livingEntity = (LivingEntity)(Object)this;
((MutableAttributeContainer)livingEntity.getAttributes()).setLivingEntity(livingEntity);
livingEntity.setHealth(livingEntity.getMaxHealth());
Expand All @@ -39,7 +46,7 @@ private void data_init(EntityType<? extends LivingEntity> entityType, World worl
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;tickActiveItemStack()V"))
private void data_tick(CallbackInfo ci) {
LivingEntity livingEntity = (LivingEntity)(Object)this;
final int updateFlag = ((MutableIntFlag)livingEntity.world.getLevelProperties()).getUpdateFlag();
final int updateFlag = this.data_checkedUpdateFlag(livingEntity.world);

if(this.data_updateFlag != updateFlag) {
AttributeContainer container = livingEntity.getAttributes();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.clevernucleus.dataattributes.mixin;

import org.spongepowered.asm.mixin.Mixin;

import com.github.clevernucleus.dataattributes.mutable.MutableIntFlag;

import net.minecraft.world.MutableWorldProperties;

@Mixin(MutableWorldProperties.class)
public interface MutableWorldPropertiesMixin extends MutableIntFlag {}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ReloadCommand;
Expand All @@ -41,9 +40,8 @@ private static void data_tryReloadDataPacks(Collection<String> dataPacks, Server
mutableUpdateFlag.setUpdateFlag(updateFlag2);

PacketByteBuf buf = PacketByteBufs.create();
NbtCompound tag = new NbtCompound();
DataAttributes.MANAGER.toNbt(tag);
buf.writeNbt(tag);
final byte[] bytes = DataAttributes.MANAGER.getCurrentData();
buf.writeByteArray(bytes);
buf.writeInt(mutableUpdateFlag.getUpdateFlag());
PlayerLookup.all(server).forEach(player -> ServerPlayNetworking.send(player, DataAttributes.RELOAD, buf));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import net.minecraft.world.level.UnmodifiableLevelProperties;

@Mixin(UnmodifiableLevelProperties.class)
abstract class UnmodifiableLevelPropertiesMixin implements MutableIntFlag {
abstract class UnmodifiableLevelPropertiesMixin implements MutableWorldPropertiesMixin {

@Final
@Shadow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;

import com.github.clevernucleus.dataattributes.mutable.MutableIntFlag;
import com.github.clevernucleus.dataattributes.mixin.MutableWorldPropertiesMixin;

import net.minecraft.client.world.ClientWorld;

@Mixin(ClientWorld.Properties.class)
abstract class ClientWorldPropertiesMixin implements MutableIntFlag {
abstract class ClientWorldPropertiesMixin implements MutableWorldPropertiesMixin {

@Unique
private int data_updateFlag;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.github.clevernucleus.dataattributes.mutable;

public interface MutableIntFlag {
void setUpdateFlag(int flag);
int getUpdateFlag();
default void setUpdateFlag(int flag) {}
default int getUpdateFlag() { return 0; }
}
1 change: 1 addition & 0 deletions src/main/resources/dataattributes.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"PlayerEntityMixin",
"MobEntityMixin",
"ServerPlayerEntityMixin",
"MutableWorldPropertiesMixin",
"LevelPropertiesMixin",
"UnmodifiableLevelPropertiesMixin",
"GameJoinS2CPacketMixin",
Expand Down

0 comments on commit c16acb5

Please sign in to comment.