Skip to content

Commit

Permalink
Merge pull request #89 from CleverNucleus/data-attributes-rewrite
Browse files Browse the repository at this point in the history
Data attributes rewrite
  • Loading branch information
CleverNucleus authored Jun 3, 2023
2 parents 151bcb5 + 7c14c55 commit 2004d7d
Show file tree
Hide file tree
Showing 22 changed files with 252 additions and 613 deletions.
30 changes: 11 additions & 19 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
### Changelog

+Added hierarchy entity types that can be used to apply attributes to all entities that are an instance of an entity class. Currently supported types are as follows:
This is primarily a bug-fixing and optimisation update.

| **Identifier** | **Class Type** |
| -------------- | -------------- |
| `dataattributes:living_entity` | `LivingEntity` |
| `dataattributes:mob_entity` | `MobEntity` |
| `dataattributes:path_aware_entity` | `PathAwareEntity` |
| `dataattributes:hostile_entity` | `HostileEntity` |
| `dataattributes:passive_entity` | `PassiveEntity` |
| `dataattributes:animal_entity` | `AnimalEntity` |
*Changed the way `/reload` works to refresh attributes:

These have a hierarchy of:
- No longer saves the `updateFlag` to the level's nbt data.
- No longer injects the `updateFlag` into vanilla packets.
- Instead, we only use the `updateFlag` in runtime - not saving it at all, anywhere.

```
LivingEntity
┗ MobEntity
┗ PathAwareEntity
┣ HostileEntity
┗ PassiveEntity
┗ AnimalEntity
```
*Fixed [#80](https://github.com/CleverNucleus/data-attributes/issues/80): attribute tracking is handled differently now.

This feature is useful for when you want to modify the attributes of many different mobs, but do not know every mob's `EntityType` identifier.
*Likely fixed an incompatibility between Data Attributes and ReplayMod: we no longer mess around with world properties at all.

**May* have fixed long-standing issues [24](https://github.com/CleverNucleus/data-attributes/issues/24) and [10](https://github.com/CleverNucleus/data-attributes/issues/10): almost all networking has been removed - now we only send/receive two custom packets in the whole mod: on game join and when `/reload` is executed.

*Various performance improvements.
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.18

mod_version=1.4.4+1.19.2
mod_version=1.4.5+1.19.2
maven_group=com.github.clevernucleus
archives_base_name=dataattributes

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package com.github.clevernucleus.dataattributes;

import java.util.Arrays;

import org.jetbrains.annotations.Nullable;

import com.github.clevernucleus.dataattributes.api.DataAttributesAPI;
import com.github.clevernucleus.dataattributes.api.event.EntityAttributeModifiedEvents;
import com.github.clevernucleus.dataattributes.api.util.Maths;
import com.github.clevernucleus.dataattributes.impl.AttributeManager;
import com.github.clevernucleus.dataattributes.mutable.MutableAttributeContainer;

Expand All @@ -18,12 +15,12 @@
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking.LoginSynchronizer;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
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;
Expand All @@ -34,22 +31,18 @@ public class DataAttributes implements ModInitializer {
public static final Identifier HANDSHAKE = new Identifier(DataAttributesAPI.MODID, "handshake");
public static final Identifier RELOAD = new Identifier(DataAttributesAPI.MODID, "reload");
public static final AttributeManager MANAGER = new AttributeManager();
protected static String version = "";
protected static byte[] semVer;
private static final byte VER_SIZE = 3;

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

private static void loginQueryResponse(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender) {
// Does doing nothing here make us compatible with Velocity plugin?
}
private static void loginQueryResponse(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender) {}

private static void refreshAttributes(final Entity entity) {
public static void refreshAttributes(final Entity entity) {
if(!(entity instanceof LivingEntity)) return;
((MutableAttributeContainer)((LivingEntity)entity).getAttributes()).refresh();
}
Expand All @@ -67,14 +60,6 @@ private static void healthModified(final EntityAttribute attribute, final @Nulla

@Override
public void onInitialize() {
version = FabricLoader.getInstance().getModContainer(DataAttributesAPI.MODID).get().getMetadata().getVersion().getFriendlyString();
String[] versionArray = Arrays.copyOf(version.split("\\."), VER_SIZE);
semVer = new byte[Math.max(versionArray.length, VER_SIZE)];

for(int i = 0; i < semVer.length; i++) {
semVer[i] = (byte)Maths.parse(versionArray[i]);
}

ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(MANAGER);
ServerLoginConnectionEvents.QUERY_START.register(DataAttributes::loginQueryStart);
ServerLoginNetworking.registerGlobalReceiver(HANDSHAKE, DataAttributes::loginQueryResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

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

import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.fabricmc.api.ClientModInitializer;
Expand All @@ -15,42 +13,30 @@
import net.minecraft.client.MinecraftClient;
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) {
final byte[] entityAttributeData = buf.readByteArray();
final byte[] entityTypeData = buf.readByteArray();

client.execute(() -> {
DataAttributes.MANAGER.setEntityAttributeData(entityAttributeData);
DataAttributes.MANAGER.setEntityTypeData(entityTypeData);
DataAttributes.MANAGER.apply();
});

onPacketReceived(client, buf);
return CompletableFuture.completedFuture(PacketByteBufs.empty());
}

private static void updateReceived(MinecraftClient client, ClientPlayNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender) {
final byte[] entityAttributeData = buf.readByteArray();
final byte[] entityTypeData = buf.readByteArray();
final int updateFlag = buf.readInt();
onPacketReceived(client, buf);
}

private static void onPacketReceived(MinecraftClient client, PacketByteBuf buf) {
NbtCompound tag = buf.readNbt();

client.execute(() -> {
DataAttributes.MANAGER.setEntityAttributeData(entityAttributeData);
DataAttributes.MANAGER.setEntityTypeData(entityTypeData);
DataAttributes.MANAGER.apply();

ClientWorld world = client.world;

if(world != null) {
ClientWorld.Properties properties = world.getLevelProperties();
((MutableIntFlag)properties).setUpdateFlag(updateFlag);
if(tag != null) {
DataAttributes.MANAGER.fromNbt(tag);
DataAttributes.MANAGER.apply();
}
});
}

@Override
public void onInitializeClient() {
ClientLoginNetworking.registerGlobalReceiver(DataAttributes.HANDSHAKE, DataAttributesClient::loginQueryReceived);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.github.clevernucleus.dataattributes.impl;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import com.github.clevernucleus.dataattributes.impl.AttributeManager.Tuple;
import com.github.clevernucleus.dataattributes.mutable.MutableAttributeContainer;
import com.github.clevernucleus.dataattributes.mutable.MutableDefaultAttributeContainer;
import com.google.common.collect.ImmutableMap;

import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.AttributeContainer;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.DefaultAttributeRegistry;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;

public final class AttributeContainerHandler {
private Map<Integer, Tuple<DefaultAttributeContainer>> implicitContainers;
private Map<EntityType<? extends LivingEntity>, DefaultAttributeContainer> explicitContainers;

protected AttributeContainerHandler() {
this.implicitContainers = ImmutableMap.of();
this.explicitContainers = ImmutableMap.of();
}

protected AttributeContainer getContainer(final EntityType<? extends LivingEntity> entityType, final LivingEntity livingEntity) {
DefaultAttributeContainer.Builder builder = DefaultAttributeContainer.builder();
((MutableDefaultAttributeContainer)DefaultAttributeRegistry.get(entityType)).copy(builder);

for(int i = 0; i < this.implicitContainers.size(); i++) {
Tuple<DefaultAttributeContainer> tuple = this.implicitContainers.get(i);
Class<? extends LivingEntity> type = tuple.livingEntity();

if(type.isInstance(livingEntity)) {
((MutableDefaultAttributeContainer)tuple.value()).copy(builder);
}
}

if(this.explicitContainers.containsKey(entityType)) {
((MutableDefaultAttributeContainer)this.explicitContainers.get(entityType)).copy(builder);
}

AttributeContainer container = new AttributeContainer(builder.build());
((MutableAttributeContainer)container).setLivingEntity(livingEntity);

return container;
}

@SuppressWarnings("unchecked")
protected void buildContainers(final Map<Identifier, EntityTypeData> entityTypeDataIn, Map<Identifier, Tuple<Integer>> entityTypeInstances) {
Collection<Identifier> entityTypes = Registry.ENTITY_TYPE.getIds().stream().filter(id -> DefaultAttributeRegistry.hasDefinitionFor(Registry.ENTITY_TYPE.get(id))).collect(Collectors.toSet());
ImmutableMap.Builder<Integer, Tuple<DefaultAttributeContainer>> implicitContainers = ImmutableMap.builder();
ImmutableMap.Builder<EntityType<? extends LivingEntity>, DefaultAttributeContainer> explicitContainers = ImmutableMap.builder();
Map<Integer, Tuple<Identifier>> orderedEntityTypes = new HashMap<>();

for(Identifier identifier : entityTypeDataIn.keySet()) {
if(entityTypeInstances.containsKey(identifier)) {
Tuple<Integer> tuple = entityTypeInstances.get(identifier);
orderedEntityTypes.put(tuple.value(), new Tuple<Identifier>(tuple.livingEntity(), identifier));
}
if(!entityTypes.contains(identifier)) continue;
EntityType<? extends LivingEntity> entityType = (EntityType<? extends LivingEntity>)Registry.ENTITY_TYPE.get(identifier);
DefaultAttributeContainer.Builder builder = DefaultAttributeContainer.builder();
EntityTypeData entityTypeData = entityTypeDataIn.get(identifier);
entityTypeData.build(builder, DefaultAttributeRegistry.get(entityType));
explicitContainers.put(entityType, builder.build());
}

final int size = orderedEntityTypes.size();
final int max = orderedEntityTypes.keySet().stream().mapToInt(Integer::intValue).max().orElse(0);

for(Map.Entry<Integer, Tuple<Identifier>> entry : orderedEntityTypes.entrySet()) {
Tuple<Identifier> tuple = entry.getValue();
Identifier identifier = tuple.value();
final int hierarchy = entry.getKey();
final int index = Math.round((float)size * (float)hierarchy / (float)max) - 1;
DefaultAttributeContainer.Builder builder = DefaultAttributeContainer.builder();
EntityTypeData entityTypeData = entityTypeDataIn.get(identifier);
entityTypeData.build(builder, null);
implicitContainers.put(index, new Tuple<DefaultAttributeContainer>(tuple.livingEntity(), builder.build()));
}

this.implicitContainers = implicitContainers.build();
this.explicitContainers = explicitContainers.build();
}
}
Loading

0 comments on commit 2004d7d

Please sign in to comment.