Skip to content

Commit de27412

Browse files
committed
Implement new geyser entity data override system
1 parent 92a507c commit de27412

7 files changed

Lines changed: 150 additions & 137 deletions

File tree

core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*
22+
* @author GeyserMC
23+
* @link https://github.com/GeyserMC/Geyser
24+
*/
25+
26+
package org.geysermc.geyser.entity;
27+
28+
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
29+
import org.checkerframework.checker.nullness.qual.NonNull;
30+
import org.checkerframework.checker.nullness.qual.Nullable;
31+
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataMap;
32+
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType;
33+
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
34+
35+
import java.util.HashSet;
36+
import java.util.Map;
37+
import java.util.Set;
38+
39+
/**
40+
* A wrapper for temporarily storing entity metadata that will be sent to Bedrock.
41+
*/
42+
public final class GeyserEntityDataManager {
43+
44+
private static final Set<EntityDataType<?>> TRACKED_METADATA = new HashSet<>();
45+
46+
static {
47+
TRACKED_METADATA.add(EntityDataTypes.WIDTH);
48+
TRACKED_METADATA.add(EntityDataTypes.HEIGHT);
49+
TRACKED_METADATA.add(EntityDataTypes.SCALE);
50+
TRACKED_METADATA.add(EntityDataTypes.VARIANT);
51+
TRACKED_METADATA.add(EntityDataTypes.COLOR);
52+
TRACKED_METADATA.add(EntityDataTypes.SEAT_OFFSET);
53+
TRACKED_METADATA.add(EntityDataTypes.HITBOX);
54+
}
55+
56+
/**
57+
* Map storing all current metadata
58+
* Note: Only tracks the metadata listed above that's made available in the API.
59+
*/
60+
private final Map<EntityDataType<?>, Object> metadata = new Object2ObjectLinkedOpenHashMap<>();
61+
62+
/**
63+
* Map storing the metadata updates until they're sent to Bedrock, then cleared.
64+
*/
65+
private final Map<EntityDataType<?>, Object> dirtyMetadata = new Object2ObjectLinkedOpenHashMap<>();
66+
67+
/**
68+
* Map storing all overridden metadata
69+
*/
70+
private final Map<EntityDataType<?>, Object> overrides = new Object2ObjectLinkedOpenHashMap<>();
71+
72+
public <T> void put(EntityDataType<T> entityData, T value) {
73+
if (TRACKED_METADATA.contains(entityData)) {
74+
// Track the original value
75+
metadata.put(entityData, value);
76+
// But use the override if it exists
77+
Object override = overrides.get(entityData);
78+
if (override != null) {
79+
value = (T) override;
80+
}
81+
}
82+
dirtyMetadata.put(entityData, value);
83+
}
84+
85+
public <T> void updateOverride(@NonNull EntityDataType<T> entityData, @Nullable T value) {
86+
if (!TRACKED_METADATA.contains(entityData)) {
87+
throw new IllegalArgumentException("Entity data type not tracked: " + entityData.getClass().getSimpleName());
88+
}
89+
90+
if (value == null) {
91+
overrides.remove(entityData);
92+
Object currentValue = metadata.get(entityData);
93+
if (currentValue != null) {
94+
dirtyMetadata.put(entityData, currentValue);
95+
}
96+
} else {
97+
overrides.put(entityData, value);
98+
dirtyMetadata.put(entityData, value);
99+
}
100+
}
101+
102+
public <T> @Nullable T value(EntityDataType<T> entityData) {
103+
if (!TRACKED_METADATA.contains(entityData)) {
104+
throw new IllegalArgumentException("Entity data type not tracked: " + entityData.getClass().getSimpleName());
105+
}
106+
Object override = overrides.get(entityData);
107+
return override != null ? (T) override : (T) metadata.get(entityData);
108+
}
109+
110+
/**
111+
* Applies the contents of the dirty metadata into the input and clears the contents of our map.
112+
*/
113+
public void apply(EntityDataMap map) {
114+
map.putAll(dirtyMetadata);
115+
dirtyMetadata.clear();
116+
}
117+
118+
public boolean hasEntries() {
119+
return !dirtyMetadata.isEmpty();
120+
}
121+
122+
/**
123+
* Intended for testing purposes only
124+
*/
125+
public <T> T get(EntityDataType<T> entityData) {
126+
//noinspection unchecked
127+
return (T) dirtyMetadata.get(entityData);
128+
}
129+
130+
@Override
131+
public String toString() {
132+
return dirtyMetadata.toString();
133+
}
134+
}

core/src/main/java/org/geysermc/geyser/entity/type/Entity.java

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
package org.geysermc.geyser.entity.type;
2727

28-
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
2928
import lombok.AccessLevel;
3029
import lombok.Getter;
3130
import lombok.Setter;
@@ -35,7 +34,6 @@
3534
import org.checkerframework.checker.nullness.qual.Nullable;
3635
import org.cloudburstmc.math.vector.Vector2f;
3736
import org.cloudburstmc.math.vector.Vector3f;
38-
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType;
3937
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
4038
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
4139
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
@@ -52,7 +50,7 @@
5250
import org.geysermc.geyser.api.entity.type.GeyserEntity;
5351
import org.geysermc.geyser.entity.BedrockEntityDefinition;
5452
import org.geysermc.geyser.entity.EntityTypeDefinition;
55-
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
53+
import org.geysermc.geyser.entity.GeyserEntityDataManager;
5654
import org.geysermc.geyser.entity.properties.GeyserEntityProperties;
5755
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
5856
import org.geysermc.geyser.entity.properties.type.PropertyType;
@@ -81,7 +79,6 @@
8179
import java.util.Collections;
8280
import java.util.EnumMap;
8381
import java.util.List;
84-
import java.util.Map;
8582
import java.util.Objects;
8683
import java.util.Optional;
8784
import java.util.UUID;
@@ -144,26 +141,13 @@ public class Entity implements GeyserEntity {
144141
protected boolean silent = false;
145142
/* Metadata end */
146143

147-
// hacky temp solution till i can think of something better
148-
// this would be the height / width of the bounding box regardless of pose etc
149-
private Float customBoundingBoxHeight;
150-
private Float customBoundingBoxWidth;
151-
152144
protected List<Entity> passengers = Collections.emptyList();
153145
protected Entity vehicle;
154146

155-
@Getter
156-
private Vector3f riderSeatPosition = Vector3f.ZERO;
157-
158147
/**
159148
* A container to store temporary metadata before it's sent to Bedrock.
160149
*/
161-
protected final GeyserDirtyMetadata dirtyMetadata = new GeyserDirtyMetadata();
162-
/**
163-
* A container storing all current metadata for an entity.
164-
*/
165-
// TODO only store what is needed for API
166-
protected final Map<EntityDataType<?>, Object> metadata = new Object2ObjectLinkedOpenHashMap<>();
150+
protected final GeyserEntityDataManager dirtyMetadata = new GeyserEntityDataManager();
167151

168152
/**
169153
* The entity flags for the Bedrock entity.
@@ -677,30 +661,6 @@ public void setBoundingBoxWidth(float width) {
677661
}
678662
}
679663

680-
public float getBoundingBoxHeight() {
681-
if (customBoundingBoxHeight != null) {
682-
return customBoundingBoxHeight;
683-
}
684-
return boundingBoxHeight;
685-
}
686-
687-
public float getBoundingBoxWidth() {
688-
if (customBoundingBoxWidth != null) {
689-
return customBoundingBoxWidth;
690-
}
691-
return boundingBoxWidth;
692-
}
693-
694-
public void setCustomBoundingBoxWidth(float width) {
695-
this.customBoundingBoxWidth = width;
696-
dirtyMetadata.put(EntityDataTypes.WIDTH, customBoundingBoxWidth);
697-
}
698-
699-
public void setCustomBoundingBoxHeight(float height) {
700-
this.customBoundingBoxHeight = height;
701-
dirtyMetadata.put(EntityDataTypes.HEIGHT, customBoundingBoxHeight);
702-
}
703-
704664
public void setScale(float scale) {
705665
this.scale = scale;
706666
applyScale();
@@ -723,7 +683,6 @@ public float setFreezing(IntEntityMetadata entityMetadata) {
723683
}
724684

725685
public void setRiderSeatPosition(Vector3f position) {
726-
riderSeatPosition = position;
727686
dirtyMetadata.put(EntityDataTypes.SEAT_OFFSET, position);
728687
}
729688

@@ -770,11 +729,9 @@ public Vector3f bedrockPosition() {
770729
protected void updatePassengerOffsets() {
771730
for (int i = 0; i < passengers.size(); i++) {
772731
Entity passenger = passengers.get(i);
773-
if (passenger != null) {
774-
boolean rider = i == 0;
775-
EntityUtils.updateMountOffset(passenger, this, rider, true, i, passengers.size());
776-
passenger.updateBedrockMetadata();
777-
}
732+
boolean rider = i == 0;
733+
EntityUtils.updateMountOffset(passenger, this, rider, true, i, passengers.size());
734+
passenger.updateBedrockMetadata();
778735
}
779736
}
780737

core/src/main/java/org/geysermc/geyser/impl/entity/GeyserEntityDataImpl.java

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ public class GeyserEntityDataImpl<T> implements GeyserEntityDataType<T> {
4747
TYPES = new Object2ObjectOpenHashMap<>();
4848
TYPES.put("color", new GeyserEntityDataImpl<>(Byte.class, "color", EntityDataTypes.COLOR));
4949
TYPES.put("variant", new GeyserEntityDataImpl<>(Integer.class, "variant", EntityDataTypes.VARIANT));
50-
TYPES.put("width", new GeyserEntityDataImpl<>(Float.class, "width", Entity::setCustomBoundingBoxWidth, Entity::getCustomBoundingBoxWidth));
51-
TYPES.put("height", new GeyserEntityDataImpl<>(Float.class, "height", Entity::setCustomBoundingBoxHeight, Entity::getCustomBoundingBoxHeight));
52-
TYPES.put("scale", new GeyserEntityDataImpl<>(Float.class, "scale", Entity::setScale, Entity::getScale));
53-
TYPES.put("seat_offset", new GeyserEntityDataImpl<>(Vector3f.class, "seat_offset", Entity::setRiderSeatPosition, Entity::getRiderSeatPosition));
50+
TYPES.put("width", new GeyserEntityDataImpl<>(Float.class, "width", EntityDataTypes.WIDTH));
51+
TYPES.put("height", new GeyserEntityDataImpl<>(Float.class, "height", EntityDataTypes.HEIGHT));
52+
TYPES.put("scale", new GeyserEntityDataImpl<>(Float.class, "scale", EntityDataTypes.SCALE));
53+
TYPES.put("seat_offset", new GeyserEntityDataImpl<>(Vector3f.class, "seat_offset", EntityDataTypes.SEAT_OFFSET));
5454

5555
// "custom"
5656
TYPES.put("vertical_offset", new GeyserEntityDataImpl<>(Float.class, "vertical_offset", (entity, value) -> entity.offset(value, true), Entity::getOffset));
@@ -75,16 +75,8 @@ public static GeyserEntityDataImpl<?> lookup(Class<?> clazz, String name) {
7575
public GeyserEntityDataImpl(Class<T> typeClass, String name, EntityDataType<T> type) {
7676
this.typeClass = typeClass;
7777
this.name = name;
78-
this.consumer = (entity, data) -> entity.getDirtyMetadata().put(type, data);
79-
this.getter = entity -> {
80-
var value = entity.getMetadata().get(type);
81-
// Always the case!
82-
if (typeClass.isInstance(value)) {
83-
return typeClass.cast(value);
84-
} else {
85-
return null;
86-
}
87-
};
78+
this.consumer = (entity, data) -> entity.getDirtyMetadata().updateOverride(type, data);
79+
this.getter = entity -> entity.getDirtyMetadata().value(type);
8880
}
8981

9082
public GeyserEntityDataImpl(Class<T> typeClass, String name, EntityFlag flag) {

core/src/main/java/org/geysermc/geyser/impl/entity/GeyserListEntityDataImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
2929
import org.checkerframework.checker.nullness.qual.NonNull;
30-
import org.cloudburstmc.nbt.NbtMap;
3130
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
3231
import org.geysermc.geyser.api.entity.data.GeyserListEntityDataType;
3332
import org.geysermc.geyser.api.entity.data.types.Hitbox;
@@ -45,8 +44,8 @@ public class GeyserListEntityDataImpl<ListType> extends GeyserEntityDataImpl<Lis
4544
static {
4645
TYPES = new Object2ObjectOpenHashMap<>();
4746
TYPES.put("hitboxes", new GeyserListEntityDataImpl<>(Hitbox.class, "hitboxes",
48-
(entity, hitboxes) -> entity.getDirtyMetadata().put(EntityDataTypes.HITBOX, HitboxImpl.toNbtMap(hitboxes)),
49-
(entity -> HitboxImpl.fromMetaData((NbtMap) entity.getMetadata().get(EntityDataTypes.HITBOX)))));
47+
(entity, hitboxes) -> entity.getDirtyMetadata().updateOverride(EntityDataTypes.HITBOX, HitboxImpl.toNbtMap(hitboxes)),
48+
(entity -> HitboxImpl.fromMetaData(entity.getDirtyMetadata().value(EntityDataTypes.HITBOX)))));
5049
}
5150

5251
private final Class<ListType> listTypeClass;

core/src/main/java/org/geysermc/geyser/impl/entity/HitboxImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,21 +87,21 @@ public static class BuilderImpl implements Hitbox.Builder {
8787
Vector3f min, max, pivot;
8888

8989
@Override
90-
public Hitbox.Builder min(@NonNull Vector3f min) {
90+
public Hitbox.@NonNull Builder min(@NonNull Vector3f min) {
9191
Objects.requireNonNull(min, "min");
9292
this.min = min;
9393
return this;
9494
}
9595

9696
@Override
97-
public Hitbox.Builder max(@NonNull Vector3f max) {
97+
public Hitbox.@NonNull Builder max(@NonNull Vector3f max) {
9898
Objects.requireNonNull(max, "max");
9999
this.max = max;
100100
return this;
101101
}
102102

103103
@Override
104-
public Hitbox.Builder pivot(@NonNull Vector3f pivot) {
104+
public Hitbox.@NonNull Builder pivot(@NonNull Vector3f pivot) {
105105
Objects.requireNonNull(pivot, "pivot");
106106
this.pivot = pivot;
107107
return this;

0 commit comments

Comments
 (0)