From e349fe8c8ef76f8bb4c9652d14cbf6de40a42c33 Mon Sep 17 00:00:00 2001 From: aromaa Date: Sun, 21 Apr 2024 00:40:44 +0300 Subject: [PATCH 1/7] Initial contextual data work --- SpongeAPI | 2 +- .../common/bridge/data/VanishableBridge.java | 3 + .../common/data/ContextualDataHolder.java | 246 ++++++++++++++++++ .../data/ContextualDataHolderProvider.java | 169 ++++++++++++ .../data/DataPerspectiveResolverRegistry.java | 50 ++++ .../data/GenericDataPerspectiveResolver.java | 52 ++++ .../common/data/SpongeDataManager.java | 12 + .../common/data/SpongeDataRegistration.java | 8 + .../data/SpongeDataRegistrationBuilder.java | 9 + .../provider/DataProviderRegistrator.java | 80 +++++- .../data/provider/entity/VanishableData.java | 43 ++- .../common/entity/player/SpongeUserData.java | 11 + .../common/function/TriConsumer.java | 41 +++ .../world/entity/EntityMixin_API.java | 27 ++ .../world/scores/PlayerTeamMixin_API.java | 21 ++ .../level/ChunkMap_TrackedEntityMixin.java | 38 ++- .../mixin/core/world/entity/EntityMixin.java | 38 ++- 17 files changed, 810 insertions(+), 40 deletions(-) create mode 100644 src/main/java/org/spongepowered/common/data/ContextualDataHolder.java create mode 100644 src/main/java/org/spongepowered/common/data/ContextualDataHolderProvider.java create mode 100644 src/main/java/org/spongepowered/common/data/DataPerspectiveResolverRegistry.java create mode 100644 src/main/java/org/spongepowered/common/data/GenericDataPerspectiveResolver.java create mode 100644 src/main/java/org/spongepowered/common/function/TriConsumer.java diff --git a/SpongeAPI b/SpongeAPI index cb4ee935103..123754f46c3 160000 --- a/SpongeAPI +++ b/SpongeAPI @@ -1 +1 @@ -Subproject commit cb4ee935103e4cd09e9bcb7a4e8cd649e0287fc9 +Subproject commit 123754f46c374d4c111d66c6ecf7f2c6613fe058 diff --git a/src/main/java/org/spongepowered/common/bridge/data/VanishableBridge.java b/src/main/java/org/spongepowered/common/bridge/data/VanishableBridge.java index b8673470a2b..bc63ad39777 100644 --- a/src/main/java/org/spongepowered/common/bridge/data/VanishableBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/data/VanishableBridge.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.bridge.data; +import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.effect.VanishState; public interface VanishableBridge { @@ -32,6 +33,8 @@ public interface VanishableBridge { void bridge$vanishState(VanishState state); + void bridge$vanishState(VanishState state, DataPerspective perspective); + boolean bridge$isInvisible(); void bridge$setInvisible(boolean invisible); diff --git a/src/main/java/org/spongepowered/common/data/ContextualDataHolder.java b/src/main/java/org/spongepowered/common/data/ContextualDataHolder.java new file mode 100644 index 00000000000..e660f8588ee --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/ContextualDataHolder.java @@ -0,0 +1,246 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data; + +import com.google.common.collect.Maps; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.DataPerspectiveResolver; +import org.spongepowered.api.data.DataTransactionResult; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.scoreboard.Team; +import org.spongepowered.api.world.World; +import org.spongepowered.common.util.CopyHelper; +import org.spongepowered.plugin.PluginContainer; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +@SuppressWarnings("unchecked") +public final class ContextualDataHolder { + + private final DataHolder dataHolder; + private final Map> perspectives; + + public ContextualDataHolder(final DataHolder dataHolder) { + this.dataHolder = dataHolder; + + this.perspectives = Maps.newHashMap(); + } + + private PerspectiveContainer createDataPerception(final DataPerspective perspective) { + return this.perspectives.computeIfAbsent(perspective, p -> { + if (p instanceof final Entity entity) { + return new EntityPerspectiveContainer(entity); + } else if (p instanceof final Team team) { + return new TeamPerspectiveContainer(team); + } else if (p instanceof final World world) { + return new WorldPerspectiveContainer(world); + } + throw new UnsupportedOperationException(); + }); + } + + public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { + return new ContextualDataHolderProvider(plugin, this.createDataPerception(perspective)); + } + + public @Nullable ValueContainer get(final DataPerspective perspective) { + return this.perspectives.get(perspective); + } + + abstract class PerspectiveContainer implements ValueContainer { + + protected final T perspective; + + private final Map, Map> pluginValues; + protected final Map, Object> activeValues; + + protected PerspectiveContainer(final T perspective) { + this.perspective = perspective; + + this.pluginValues = Maps.newHashMap(); + this.activeValues = Maps.newHashMap(); + } + + DataTransactionResult offer(final PluginContainer pluginContainer, final Key> key, final E value) { + final @Nullable DataPerspectiveResolver, E> resolver = SpongeDataManager.getDataPerspectiveResolverRegistry().get(key); + if (resolver == null) { + return DataTransactionResult.failResult(Value.immutableOf(key, value)); + } + + final Map map = (Map) this.pluginValues.computeIfAbsent(key, k -> Maps.newLinkedHashMap()); + if (Objects.equals(value, map.put(pluginContainer, value))) { + return DataTransactionResult.successResult(Value.immutableOf(key, value)); + } + + final E mergedValue = resolver.merge(map.values()); + this.offer(key, resolver, mergedValue); + return DataTransactionResult.successResult(Value.immutableOf(key, mergedValue)); + } + + protected abstract void offer(final Key> key, final DataPerspectiveResolver, E> resolver, final E value); + + @Override + public Optional get(final Key> key) { + Objects.requireNonNull(key, "key"); + final @Nullable E value = (E) this.activeValues.get(key); + if (value == null) { + return ContextualDataHolder.this.dataHolder.get(key); + } + return Optional.of(CopyHelper.copy(value)); + } + + @Override + public > Optional getValue(final Key key) { + Objects.requireNonNull(key, "key"); + final @Nullable E value = (E) this.activeValues.get(key); + if (value == null) { + return ContextualDataHolder.this.dataHolder.getValue(key); + } + return Optional.of(Value.genericMutableOf(key, CopyHelper.copy(value))); + } + + @Override + public boolean supports(Key key) { + return ContextualDataHolder.this.dataHolder.supports(key); + } + + @Override + public Set> getKeys() { + return ContextualDataHolder.this.dataHolder.getKeys(); + } + + @Override + public Set> getValues() { + return ContextualDataHolder.this.dataHolder.getValues(); + } + } + + private abstract class NonEntityContainer extends PerspectiveContainer { + + protected NonEntityContainer(T perspective) { + super(perspective); + } + + protected abstract PerspectiveType type(); + + protected void offer(final Key> key, final DataPerspectiveResolver, E> resolver, final E value) { + if (Objects.equals(this.activeValues.put(key, value), value)) { + return; + } + + for (final DataPerspective perspective : this.perspective.perceives()) { + if (!(perspective instanceof Entity entity)) { + continue; + } + + final EntityPerspectiveContainer entityContainer = (EntityPerspectiveContainer) ContextualDataHolder.this.createDataPerception(entity); + entityContainer.offer(this.type(), key, resolver, value); + } + } + } + + private final class TeamPerspectiveContainer extends NonEntityContainer { + + public TeamPerspectiveContainer(final Team team) { + super(team); + } + + @Override + protected PerspectiveType type() { + return PerspectiveType.TEAM; + } + } + + private final class WorldPerspectiveContainer extends NonEntityContainer> { + + public WorldPerspectiveContainer(final World world) { + super(world); + } + + @Override + protected PerspectiveType type() { + return PerspectiveType.WORLD; + } + } + + private final class EntityPerspectiveContainer extends PerspectiveContainer { + + private final Map, EnumMap> perspectiveValues; + + public EntityPerspectiveContainer(final Entity entity) { + super(entity); + + this.perspectiveValues = Maps.newHashMap(); + } + + private EnumMap getValues(final Key> key) { + return (EnumMap) this.perspectiveValues.computeIfAbsent(key, k -> Maps.newEnumMap(PerspectiveType.class)); + } + + @Override + protected void offer(final Key> key, final DataPerspectiveResolver, E> resolver, final E value) { + this.getValues(key).put(PerspectiveType.ENTITY, value); + if (!Objects.equals(value, this.activeValues.put(key, value))) { + resolver.apply(ContextualDataHolder.this.dataHolder, this.perspective, value); + } + } + + void offer(final PerspectiveType type, final Key> key, final DataPerspectiveResolver, E> resolver, final E value) { + final EnumMap values = this.getValues(key); + values.put(type, value); + this.updatePerspective(key, values, resolver); + } + + private void updatePerspective(final Key> key, final EnumMap values, final DataPerspectiveResolver, E> resolver) { + for (final PerspectiveType type : PerspectiveType.values()) { + final @Nullable E value = values.get(type); + if (value == null) { + continue; + } + + if (!Objects.equals(this.activeValues.put(key, value), value)) { + resolver.apply(ContextualDataHolder.this.dataHolder, this.perspective, value); + } + + return; + } + } + } + + private enum PerspectiveType { + ENTITY, + TEAM, + WORLD + } +} diff --git a/src/main/java/org/spongepowered/common/data/ContextualDataHolderProvider.java b/src/main/java/org/spongepowered/common/data/ContextualDataHolderProvider.java new file mode 100644 index 00000000000..9e0ca25765c --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/ContextualDataHolderProvider.java @@ -0,0 +1,169 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data; + +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataTransactionResult; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.CollectionValue; +import org.spongepowered.api.data.value.MapValue; +import org.spongepowered.api.data.value.MergeFunction; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.plugin.PluginContainer; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +@SuppressWarnings("unchecked") +final class ContextualDataHolderProvider implements DataHolder.Mutable { + + private final PluginContainer plugin; + private final ContextualDataHolder.PerspectiveContainer container; + + ContextualDataHolderProvider(final PluginContainer plugin, final ContextualDataHolder.PerspectiveContainer container) { + this.plugin = plugin; + this.container = container; + } + + @Override + public DataTransactionResult offer(final Key> key, final E value) { + Objects.requireNonNull(key, "key"); + return this.container.offer(this.plugin, key, value); + } + + @Override + public DataTransactionResult offer(Value value) { + return this.container.offer(this.plugin, (Key>)value.key(), value.get()); + } + + @Override + public DataTransactionResult offerSingle(Key> key, E element) { + return null; + } + + @Override + public DataTransactionResult offerSingle(Key> key, K valueKey, V value) { + return null; + } + + @Override + public DataTransactionResult offerAll(Key> key, Map map) { + return null; + } + + @Override + public DataTransactionResult offerAll(MapValue value) { + return null; + } + + @Override + public DataTransactionResult offerAll(CollectionValue value) { + return null; + } + + @Override + public DataTransactionResult offerAll(Key> key, Collection elements) { + return null; + } + + @Override + public DataTransactionResult removeSingle(Key> key, E element) { + return null; + } + + @Override + public DataTransactionResult removeKey(Key> key, K mapKey) { + return null; + } + + @Override + public DataTransactionResult removeAll(CollectionValue value) { + return null; + } + + @Override + public DataTransactionResult removeAll(Key> key, Collection elements) { + return null; + } + + @Override + public DataTransactionResult removeAll(MapValue value) { + return null; + } + + @Override + public DataTransactionResult removeAll(Key> key, Map map) { + return null; + } + + @Override + public DataTransactionResult tryOffer(Key> key, E value) { + return null; + } + + @Override + public DataTransactionResult remove(Key key) { + return null; + } + + @Override + public DataTransactionResult undo(DataTransactionResult result) { + return null; + } + + @Override + public DataTransactionResult copyFrom(ValueContainer that, MergeFunction function) { + return null; + } + + @Override + public Optional get(Key> key) { + return Optional.empty(); + } + + @Override + public > Optional getValue(Key key) { + return Optional.empty(); + } + + @Override + public boolean supports(Key key) { + return false; + } + + @Override + public Set> getKeys() { + return null; + } + + @Override + public Set> getValues() { + return null; + } +} diff --git a/src/main/java/org/spongepowered/common/data/DataPerspectiveResolverRegistry.java b/src/main/java/org/spongepowered/common/data/DataPerspectiveResolverRegistry.java new file mode 100644 index 00000000000..1a2a26b2036 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/DataPerspectiveResolverRegistry.java @@ -0,0 +1,50 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data; + +import com.google.common.collect.Maps; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataPerspectiveResolver; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.Value; + +import java.util.Map; + +public class DataPerspectiveResolverRegistry { + + private final Map, DataPerspectiveResolver> resolvers; + + public DataPerspectiveResolverRegistry() { + this.resolvers = Maps.newHashMap(); + } + + public @Nullable DataPerspectiveResolver, E> get(final Key> key) { + return (DataPerspectiveResolver, E>)this.resolvers.get(key); + } + + public void register(final DataPerspectiveResolver resolver) { + this.resolvers.put(resolver.key(), resolver); + } +} diff --git a/src/main/java/org/spongepowered/common/data/GenericDataPerspectiveResolver.java b/src/main/java/org/spongepowered/common/data/GenericDataPerspectiveResolver.java new file mode 100644 index 00000000000..561be71aa30 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/GenericDataPerspectiveResolver.java @@ -0,0 +1,52 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data; + +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.DataPerspectiveResolver; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.Value; + +public abstract class GenericDataPerspectiveResolver implements DataPerspectiveResolver, E> { + + private final Key> key; + + public GenericDataPerspectiveResolver(final Key> key) { + this.key = (Key>)key; + } + + @Override + public Key> key() { + return this.key; + } + + @Override + public void apply(DataHolder dataHolder, DataPerspective perspective, E value) { + this.apply((H) dataHolder, perspective, value); + } + + public abstract void apply(H dataHolder, DataPerspective perspective, E value); +} diff --git a/src/main/java/org/spongepowered/common/data/SpongeDataManager.java b/src/main/java/org/spongepowered/common/data/SpongeDataManager.java index 56b219c64ea..6b66790994a 100644 --- a/src/main/java/org/spongepowered/common/data/SpongeDataManager.java +++ b/src/main/java/org/spongepowered/common/data/SpongeDataManager.java @@ -39,6 +39,7 @@ import org.spongepowered.api.data.DataHolderBuilder; import org.spongepowered.api.data.DataManager; import org.spongepowered.api.data.DataManipulator.Mutable; +import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.DataProvider; import org.spongepowered.api.data.Key; import org.spongepowered.api.data.meta.BannerPatternLayer; @@ -144,6 +145,7 @@ public final class SpongeDataManager implements DataManager { private final DataStoreRegistry dataStoreRegistry; private final DataProviderRegistry dataProviderRegistry; + private final DataPerspectiveResolverRegistry dataPerspectiveResolverRegistry; private final Map, DataBuilder> builders; private final Map>, DataHolderBuilder.Immutable> immutableDataBuilderMap; private final Map, List> updatersMap; @@ -158,6 +160,7 @@ private SpongeDataManager() { this.dataStoreRegistry = new DataStoreRegistry(); this.dataProviderRegistry = new DataProviderRegistry(); + this.dataPerspectiveResolverRegistry = new DataPerspectiveResolverRegistry(); this.builders = new HashMap<>(); this.immutableDataBuilderMap = new MapMaker() .concurrencyLevel(4) @@ -333,6 +336,9 @@ public void registerCustomDataRegistration(final SpongeDataRegistration registra for (final Key key : registration.keys()) { this.registerCustomDataProviderForKey(registration, key); + + final Optional> resolver = registration.dataPerspectiveResolverFor(key); + resolver.ifPresent(this.dataPerspectiveResolverRegistry::register); } } @@ -363,6 +369,8 @@ public void registerDataRegistration(final SpongeDataRegistration registration) for (DataProvider provider : providers) { this.dataProviderRegistry.register(provider); } + final Optional> resolver = registration.dataPerspectiveResolverFor(key); + resolver.ifPresent(this.dataPerspectiveResolverRegistry::register); } } @@ -389,6 +397,10 @@ public static DataProviderRegistry getProviderRegistry() { return SpongeDataManager.INSTANCE.dataProviderRegistry; } + public static DataPerspectiveResolverRegistry getDataPerspectiveResolverRegistry() { + return SpongeDataManager.INSTANCE.dataPerspectiveResolverRegistry; + } + public void registerDefaultBuilders() { this.registerBuilder(ItemStack.class, new SpongeItemStack.BuilderImpl()); this.registerBuilder(ItemStackSnapshot.class, new SpongeItemStackSnapshotDataBuilder()); diff --git a/src/main/java/org/spongepowered/common/data/SpongeDataRegistration.java b/src/main/java/org/spongepowered/common/data/SpongeDataRegistration.java index 5a42a79c5a9..881a4e7320f 100644 --- a/src/main/java/org/spongepowered/common/data/SpongeDataRegistration.java +++ b/src/main/java/org/spongepowered/common/data/SpongeDataRegistration.java @@ -28,6 +28,7 @@ import io.leangen.geantyref.GenericTypeReflector; import io.leangen.geantyref.TypeToken; import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.DataProvider; import org.spongepowered.api.data.DataRegistration; import org.spongepowered.api.data.Key; @@ -46,11 +47,13 @@ public final class SpongeDataRegistration implements DataRegistration { final List> keys; final Map dataStoreMap; final Multimap dataProviderMap; + final Map dataPerspectiveResolverMap; SpongeDataRegistration(final SpongeDataRegistrationBuilder builder) { this.keys = builder.keys; this.dataStoreMap = builder.dataStoreMap; this.dataProviderMap = builder.dataProviderMap; + this.dataPerspectiveResolverMap = builder.dataPerspectiveResolverMap; } @Override @@ -69,6 +72,11 @@ public Optional dataStore(final Class token) { return this.getDataStore0(token); } + @Override + public , E> Optional> dataPerspectiveResolverFor(Key key) { + return Optional.ofNullable(this.dataPerspectiveResolverMap.get(key)); + } + private Optional getDataStore0(final Type type) { DataStore dataStore = this.dataStoreMap.get(type); if (dataStore != null) { diff --git a/src/main/java/org/spongepowered/common/data/SpongeDataRegistrationBuilder.java b/src/main/java/org/spongepowered/common/data/SpongeDataRegistrationBuilder.java index aeecb69aab1..c08e46eadc8 100644 --- a/src/main/java/org/spongepowered/common/data/SpongeDataRegistrationBuilder.java +++ b/src/main/java/org/spongepowered/common/data/SpongeDataRegistrationBuilder.java @@ -26,6 +26,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.DataProvider; import org.spongepowered.api.data.DataRegistration; import org.spongepowered.api.data.DuplicateDataStoreException; @@ -45,6 +46,7 @@ public final class SpongeDataRegistrationBuilder implements DataRegistration.Bui Multimap dataProviderMap = HashMultimap.create(); Map dataStoreMap = new HashMap<>(); + Map dataPerspectiveResolverMap = new HashMap<>(); List> keys = new ArrayList<>(); @Override @@ -61,6 +63,12 @@ public DataRegistration.Builder provider(final DataProvider provider) thro return this; } + @Override + public DataRegistration.Builder perspectiveResolver(DataPerspectiveResolver resolver) { + this.dataPerspectiveResolverMap.put(resolver.key(), resolver); + return this; + } + @Override public DataRegistration.Builder dataKey(final Key key) { this.keys.add(key); @@ -89,6 +97,7 @@ public DataRegistration build() { public SpongeDataRegistrationBuilder reset() { this.dataProviderMap = HashMultimap.create(); this.dataStoreMap = new IdentityHashMap<>(); + this.dataPerspectiveResolverMap = new HashMap<>(); this.keys = new ArrayList<>(); return this; } diff --git a/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java b/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java index c1d471545fa..fe4de14fe34 100644 --- a/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java +++ b/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java @@ -31,6 +31,8 @@ import org.spongepowered.api.Sponge; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataManipulator; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.DataProvider; import org.spongepowered.api.data.DataRegistration; import org.spongepowered.api.data.DataTransactionResult; @@ -45,10 +47,12 @@ import org.spongepowered.api.registry.DefaultedRegistryReference; import org.spongepowered.api.util.OptBool; import org.spongepowered.common.bridge.data.DataContainerHolder; +import org.spongepowered.common.data.GenericDataPerspectiveResolver; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.data.SpongeDataRegistration; import org.spongepowered.common.data.SpongeDataRegistrationBuilder; import org.spongepowered.common.data.persistence.datastore.SpongeDataStoreBuilder; +import org.spongepowered.common.function.TriConsumer; import org.spongepowered.common.util.CopyHelper; import org.spongepowered.common.util.TypeTokenUtil; @@ -183,7 +187,7 @@ public MutableRegistrator(final SpongeDataRegistrationBuilder builder, final Cla * @param The key type * @return The registration */ - public MutableRegistration create(final Supplier>> suppliedKey) { + public > MutableRegistration create(final Supplier>> suppliedKey) { return this.create(suppliedKey.get()); } @@ -193,18 +197,25 @@ public MutableRegistration create(final Supplier The key type * @return The registration */ - public MutableRegistration create(final Key> key) { - final MutableRegistration registration = new MutableRegistration<>(this, key); + public > MutableRegistration create(final Key> key) { + final MutableRegistration registration = new MutableRegistration<>(this, key); this.register(registration); return registration; } @SuppressWarnings({"unchecked", "UnstableApiUsage"}) - protected > MutableRegistrator register(final MutableRegistration registration) { - final DataProvider provider = registration.build(this.target); + protected , R extends MutableRegistration> MutableRegistrator register(final MutableRegistration registration) { + final DataProvider provider = registration.buildProvider(this.target); this.registrationBuilder.dataKey(provider.key()).provider(provider); return this; } + + @SuppressWarnings({"unchecked", "UnstableApiUsage"}) + protected , R extends MutableRegistration> MutableRegistrator register(final MutableContextualRegistration registration) { + this.register((MutableRegistration) registration); + this.registrationBuilder.perspectiveResolver(registration.buildPerspectiveResolver()); + return this; + } } public static final class ImmutableRegistrator extends DataProviderRegistrator { @@ -323,7 +334,7 @@ public R supports(final Function supports) { return (R) this; } - public DataProvider build(Class target) { + public DataProvider buildProvider(Class target) { final MutableRegistrationBase registration = this; return new GenericMutableDataProvider(registration.key, target) { final boolean isBooleanKey = registration.key.elementType() == Boolean.class; @@ -407,7 +418,7 @@ protected boolean supports(final H dataHolder) { } - public static final class MutableRegistration extends MutableRegistrationBase> { + public static class MutableRegistration> extends MutableRegistrationBase { private final MutableRegistrator registrator; @@ -416,12 +427,18 @@ private MutableRegistration(final MutableRegistrator registrator, final Key MutableRegistration create(final DefaultedRegistryReference>> suppliedKey) { + public > MutableRegistration create(final DefaultedRegistryReference>> suppliedKey) { return this.create(suppliedKey.get()); } - public MutableRegistration create(final Key> key) { - final MutableRegistration registration = new MutableRegistration<>(this.registrator, key); + public > MutableRegistration create(final Key> key) { + final MutableRegistration registration = new MutableRegistration<>(this.registrator, key); + this.registrator.register(registration); + return registration; + } + + public MutableContextualRegistration createContextual(final Key> key) { + final MutableContextualRegistration registration = new MutableContextualRegistration<>(this.registrator, key); this.registrator.register(registration); return registration; } @@ -443,7 +460,46 @@ public ImmutableRegistrator asImmutable(final Class target) { } } - @SuppressWarnings("unchecked") + public static final class MutableContextualRegistration extends MutableRegistration> { + + private @Nullable Function, E> dataPerspectiveMerge; + private @Nullable TriConsumer dataPerspectiveApply; + + private MutableContextualRegistration(MutableRegistrator registrator, Key> key) { + super(registrator, key); + } + + public MutableContextualRegistration dataPerspectiveMerge(final Function, E> merge) { + this.dataPerspectiveMerge = merge; + return this; + } + + public MutableContextualRegistration dataPerspectiveApply(final TriConsumer apply) { + this.dataPerspectiveApply = apply; + return this; + } + + public DataPerspectiveResolver buildPerspectiveResolver() { + final MutableContextualRegistration registration = this; + return new GenericDataPerspectiveResolver(registration.key) { + + @Override + public E merge(Iterable values) { + return registration.dataPerspectiveMerge.apply(values); + } + + @Override + public void apply(H dataHolder, DataPerspective perspective, E value) { + if (registration.dataPerspectiveApply != null) { + registration.dataPerspectiveApply.accept(dataHolder, perspective, value); + } + } + }; + } + } + + + @SuppressWarnings("unchecked") private static class ImmutableRegistrationBase> { private final Key> key; private @Nullable BiFunction> constructValue; @@ -700,7 +756,7 @@ public MutableDataProviderBuilder reset() { @Override public DataProvider build() { - return this.registration.build((Class) GenericTypeReflector.erase(this.holder)); + return this.registration.buildProvider((Class) GenericTypeReflector.erase(this.holder)); } } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java b/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java index 4fb03550573..697fb87e6a0 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java @@ -27,6 +27,7 @@ import net.minecraft.world.entity.Entity; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.data.Keys; +import org.spongepowered.api.effect.VanishState; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -46,7 +47,7 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.IS_INVISIBLE) .get(VanishableBridge::bridge$isInvisible) .set(VanishableBridge::bridge$setInvisible) - .create(Keys.VANISH_STATE) + .createContextual(Keys.VANISH_STATE) .get(VanishableBridge::bridge$vanishState) .setAnd((h, v) -> { if (h instanceof Entity && ((Entity) h).level().isClientSide) { @@ -54,6 +55,46 @@ public static void register(final DataProviderRegistrator registrator) { } h.bridge$vanishState(v); return true; + }) + .dataPerspectiveMerge(values -> { + VanishState current = VanishState.unvanished(); + for (final VanishState value : values) { + if (!value.invisible()) { + continue; + } + else if (!current.invisible()) { + current = value; + continue; + } + + if (!value.affectsMonsterSpawning()) { + current = current.affectMonsterSpawning(false); + } + + if (value.untargetable()) { + current = current.untargetable(true); + } + + if (!value.createsSounds()) { + current = current.createSounds(false); + } + + if (!value.createsParticles()) { + current = current.createParticles(false); + } + + if (!value.triggerVibrations()) { + current = current.triggerVibrations(false); + } + } + + return current; + }) + .dataPerspectiveApply((h, p, v) -> { + if (h instanceof Entity && ((Entity) h).level().isClientSide) { + return; + } + h.bridge$vanishState(v, p); }); final ResourceKey dataStoreKey = ResourceKey.sponge("invisibility"); registrator.spongeDataStore(dataStoreKey, VanishableBridge.class, Keys.IS_INVISIBLE, Keys.VANISH_STATE); diff --git a/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java b/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java index 55a3815c87a..2303099ee16 100644 --- a/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java +++ b/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java @@ -42,6 +42,7 @@ import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.DataSerializable; @@ -543,6 +544,16 @@ public Inventory enderChestInventory() { } + @Override + public void bridge$vanishState(final VanishState state, final DataPerspective perspective) { + final Optional playerOpt = this.player(); + if (playerOpt.isPresent()) { + ((VanishableBridge) playerOpt.get()).bridge$vanishState(state, perspective); + return; + } + //TODO: Make this work + } + @Override public boolean bridge$isInvisible() { return this.player().map(player -> ((VanishableBridge) player).bridge$isInvisible()).orElseGet(() -> this.isInvisible); diff --git a/src/main/java/org/spongepowered/common/function/TriConsumer.java b/src/main/java/org/spongepowered/common/function/TriConsumer.java new file mode 100644 index 00000000000..c5ce5ae55ef --- /dev/null +++ b/src/main/java/org/spongepowered/common/function/TriConsumer.java @@ -0,0 +1,41 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.function; + +import java.util.Objects; + +@FunctionalInterface +public interface TriConsumer { + void accept(T t, U u, V v); + + default TriConsumer andThen(TriConsumer after) { + Objects.requireNonNull(after); + + return (t, u, v) -> { + accept(t, u, v); + after.accept(t, u, v); + }; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java index 9e5b2fe1c8f..31b6e9da153 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java @@ -38,10 +38,13 @@ import net.minecraft.world.entity.RelativeMovement; import net.minecraft.world.phys.Vec3; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.Queries; import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.api.entity.EntityArchetype; import org.spongepowered.api.entity.EntitySnapshot; import org.spongepowered.api.entity.EntityType; @@ -60,13 +63,16 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.entity.EntityBridge; +import org.spongepowered.common.data.ContextualDataHolder; import org.spongepowered.common.data.persistence.NBTTranslator; import org.spongepowered.common.entity.SpongeEntityArchetypeBuilder; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.plugin.PluginContainer; +import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.Objects; @@ -103,6 +109,8 @@ public abstract class EntityMixin_API implements org.spongepowered.api.entity.En @Shadow public abstract CompoundTag shadow$saveWithoutId(CompoundTag $$0); // @formatter:on + private ContextualDataHolder impl$contextualData = new ContextualDataHolder(this); + @Override public Source random() { return (Source) this.random; @@ -391,4 +399,23 @@ public HoverEvent asHoverEvent(final UnaryOperator perceives() { + return Collections.singleton(this); + } + + @Override + public ValueContainer getDataPerception(final DataPerspective perspective) { + final @Nullable ValueContainer container = this.impl$contextualData.get(perspective); + if (container != null) { + return container; + } + return this; + } + + @Override + public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { + return this.impl$contextualData.createDataPerception(plugin, perspective); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java index e5cd023b341..4deb80f682b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java @@ -30,6 +30,10 @@ import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.Scoreboard; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.api.scoreboard.Team; import org.spongepowered.api.scoreboard.Visibility; import org.spongepowered.asm.mixin.Final; @@ -41,6 +45,7 @@ import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge; +import org.spongepowered.plugin.PluginContainer; import java.util.Collection; import java.util.Optional; @@ -221,4 +226,20 @@ public boolean unregister() { return true; } + @Override + public Iterable perceives() { + return Sponge.server().onlinePlayers().stream().filter(p -> this.members().contains(p.teamRepresentation())).collect(Collectors.toList()); + } + + @Override + public ValueContainer getDataPerception(DataPerspective perspective) { + return null; + } + + @Override + public DataHolder.Mutable createDataPerception(PluginContainer plugin, DataPerspective perspective) { + + return null; + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java index 2164fa49bf1..69aa44f3fd7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java @@ -29,14 +29,14 @@ import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.world.entity.Entity; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.Keys; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.entity.living.human.HumanEntity; import java.util.stream.Stream; @@ -44,9 +44,14 @@ @Mixin(targets = "net/minecraft/server/level/ChunkMap$TrackedEntity") public abstract class ChunkMap_TrackedEntityMixin { - @Shadow @Final private Entity entity; + @Shadow @Final Entity entity; /** + * @author gabizou + * @reason Because of the public availability of some methods, a packet + * being sent for a "vanished" entity is not permissible since the vanished + * entity is being "removed" from clients by way of literally being mimiced being + * "untracked". This safeguards the players being updated erroneously. * @author gabizou * @reason Instead of attempting to fetch the player set within * {@link org.spongepowered.common.mixin.core.server.level.ServerEntityMixin#impl$sendHumanMetadata(CallbackInfo)}, @@ -58,6 +63,10 @@ public abstract class ChunkMap_TrackedEntityMixin { @Redirect(method = "broadcast(Lnet/minecraft/network/protocol/Packet;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerConnection;send(Lnet/minecraft/network/protocol/Packet;)V")) private void impl$sendQueuedHumanPackets(final ServerPlayerConnection serverPlayNetHandler, final Packet packetIn) { + if (((DataPerspective) this.entity).getDataPerception((DataPerspective) serverPlayNetHandler.getPlayer()).require(Keys.VANISH_STATE).invisible()) { + return; + } + serverPlayNetHandler.send(packetIn); if (this.entity instanceof HumanEntity && serverPlayNetHandler instanceof ServerGamePacketListenerImpl) { @@ -67,32 +76,15 @@ public abstract class ChunkMap_TrackedEntityMixin { } } - /** - * @author gabizou - * @reason Because of the public availability of some methods, a packet - * being sent for a "vanished" entity is not permissible since the vanished - * entity is being "removed" from clients by way of literally being mimiced being - * "untracked". This safeguards the players being updated erroneously. - */ - @Inject(method = "broadcast(Lnet/minecraft/network/protocol/Packet;)V", at = @At("HEAD"), cancellable = true) - private void impl$ignoreVanished(final Packet p_219391_1_, final CallbackInfo ci) { - if (this.entity instanceof VanishableBridge) { - if (((VanishableBridge) this.entity).bridge$vanishState().invisible()) { - ci.cancel(); - } - } - } - @Redirect(method = "updatePlayer(Lnet/minecraft/server/level/ServerPlayer;)V", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;broadcastToPlayer(Lnet/minecraft/server/level/ServerPlayer;)Z")) private boolean impl$isSpectatedOrVanished(final Entity entity, final ServerPlayer player) { - if (entity instanceof VanishableBridge) { - if (((VanishableBridge) entity).bridge$vanishState().invisible()) { - return false; - } + if (((DataPerspective) entity).getDataPerception((DataPerspective) player).require(Keys.VANISH_STATE).invisible()) { + return false; } + return entity.broadcastToPlayer(player); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java index de09ff02afb..1d6764feaaf 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java @@ -70,6 +70,7 @@ import org.objectweb.asm.Opcodes; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.DataTransactionResult; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -421,12 +422,16 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, if (this.bridge$vanishState().invisible()) { for (final ServerPlayerConnection playerConnection : trackerAccessor.accessor$seenBy()) { - trackerAccessor.accessor$removePlayer(playerConnection.getPlayer()); + final ServerPlayer player = playerConnection.getPlayer(); + if (((DataPerspective) this).getDataPerception((DataPerspective) player).require(Keys.VANISH_STATE).invisible()) { + trackerAccessor.accessor$removePlayer(player); + } } if ((Entity) (Object) this instanceof ServerPlayer) { for (final ServerPlayer entityPlayerMP : SpongeCommon.server().getPlayerList().getPlayers()) { - if ((Object) this == entityPlayerMP) { + if ((Object) this == entityPlayerMP + || !((DataPerspective) this).getDataPerception((DataPerspective) entityPlayerMP).require(Keys.VANISH_STATE).invisible()) { continue; } entityPlayerMP.connection.send(new ClientboundPlayerInfoRemovePacket(List.of(this.uuid))); @@ -434,7 +439,8 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, } } else { for (final ServerPlayer entityPlayerMP : SpongeCommon.server().getPlayerList().getPlayers()) { - if ((Object) this == entityPlayerMP) { + if ((Object) this == entityPlayerMP + || ((DataPerspective) this).getDataPerception((DataPerspective) entityPlayerMP).require(Keys.VANISH_STATE).invisible()) { continue; } if ((Entity) (Object) this instanceof ServerPlayer player) { @@ -445,6 +451,32 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, } } + @Override + public void bridge$vanishState(final VanishState state, final DataPerspective perspective) { + final ChunkMap_TrackedEntityAccessor trackerAccessor = ((ChunkMapAccessor) ((ServerWorld) this.shadow$level()).chunkManager()).accessor$entityMap().get(this.shadow$getId()); + if (trackerAccessor == null) { + return; + } + + for (final DataPerspective perceive : perspective.perceives()) { + if (!(perceive instanceof final ServerPlayer entityPlayerMP) || this == perceive) { + continue; + } + + if (state.invisible()) { + trackerAccessor.accessor$removePlayer(entityPlayerMP); + if ((Entity) (Object) this instanceof ServerPlayer) { + entityPlayerMP.connection.send(new ClientboundPlayerInfoRemovePacket(List.of(this.uuid))); + } + } else { + if ((Entity) (Object) this instanceof final ServerPlayer player) { + entityPlayerMP.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); + } + trackerAccessor.accessor$updatePlayer(entityPlayerMP); + } + } + } + @Override public boolean bridge$isTransient() { return this.shadow$getEncodeId() == null; From 4f7c07c4068fd8b40389997df3308685e4c69941 Mon Sep 17 00:00:00 2001 From: aromaa Date: Sun, 21 Apr 2024 18:32:31 +0300 Subject: [PATCH 2/7] Support Team <-> Team contextual data --- .../world/entity/EntityBridge_Contextual.java | 32 +++ .../scores/PlayerTeamBridge_Contextual.java | 32 +++ .../common/data/ContextualDataHolder.java | 246 ------------------ .../common/data/SpongeDataManager.java | 1 + .../data/contextual/ContextualData.java | 32 +++ .../contextual/ContextualDataDelegate.java | 111 ++++++++ .../data/contextual/ContextualDataHolder.java | 146 +++++++++++ .../ContextualDataProvider.java} | 10 +- .../DataPerspectiveResolverRegistry.java | 2 +- .../data/contextual/PerspectiveContainer.java | 114 ++++++++ .../data/contextual/PerspectiveType.java | 31 +++ .../world/entity/EntityMixin_API.java | 8 +- .../world/scores/PlayerTeamMixin_API.java | 8 +- .../mixin/core/world/entity/EntityMixin.java | 17 +- .../core/world/scores/PlayerTeamMixin.java | 19 +- 15 files changed, 546 insertions(+), 263 deletions(-) create mode 100644 src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge_Contextual.java create mode 100644 src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge_Contextual.java delete mode 100644 src/main/java/org/spongepowered/common/data/ContextualDataHolder.java create mode 100644 src/main/java/org/spongepowered/common/data/contextual/ContextualData.java create mode 100644 src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java create mode 100644 src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java rename src/main/java/org/spongepowered/common/data/{ContextualDataHolderProvider.java => contextual/ContextualDataProvider.java} (94%) rename src/main/java/org/spongepowered/common/data/{ => contextual}/DataPerspectiveResolverRegistry.java (97%) create mode 100644 src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java create mode 100644 src/main/java/org/spongepowered/common/data/contextual/PerspectiveType.java diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge_Contextual.java b/src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge_Contextual.java new file mode 100644 index 00000000000..5fbe2164186 --- /dev/null +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge_Contextual.java @@ -0,0 +1,32 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.bridge.world.entity; + +import org.spongepowered.common.data.contextual.ContextualDataHolder; + +public interface EntityBridge_Contextual { + + ContextualDataHolder bridge$contextualData(); +} diff --git a/src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge_Contextual.java b/src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge_Contextual.java new file mode 100644 index 00000000000..839a1e9adfe --- /dev/null +++ b/src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge_Contextual.java @@ -0,0 +1,32 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.bridge.world.scores; + +import org.spongepowered.common.data.contextual.ContextualDataDelegate; + +public interface PlayerTeamBridge_Contextual { + + ContextualDataDelegate bridge$contextualData(); +} diff --git a/src/main/java/org/spongepowered/common/data/ContextualDataHolder.java b/src/main/java/org/spongepowered/common/data/ContextualDataHolder.java deleted file mode 100644 index e660f8588ee..00000000000 --- a/src/main/java/org/spongepowered/common/data/ContextualDataHolder.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.data; - -import com.google.common.collect.Maps; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.data.DataHolder; -import org.spongepowered.api.data.DataPerspective; -import org.spongepowered.api.data.DataPerspectiveResolver; -import org.spongepowered.api.data.DataTransactionResult; -import org.spongepowered.api.data.Key; -import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.data.value.ValueContainer; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.scoreboard.Team; -import org.spongepowered.api.world.World; -import org.spongepowered.common.util.CopyHelper; -import org.spongepowered.plugin.PluginContainer; - -import java.util.EnumMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -@SuppressWarnings("unchecked") -public final class ContextualDataHolder { - - private final DataHolder dataHolder; - private final Map> perspectives; - - public ContextualDataHolder(final DataHolder dataHolder) { - this.dataHolder = dataHolder; - - this.perspectives = Maps.newHashMap(); - } - - private PerspectiveContainer createDataPerception(final DataPerspective perspective) { - return this.perspectives.computeIfAbsent(perspective, p -> { - if (p instanceof final Entity entity) { - return new EntityPerspectiveContainer(entity); - } else if (p instanceof final Team team) { - return new TeamPerspectiveContainer(team); - } else if (p instanceof final World world) { - return new WorldPerspectiveContainer(world); - } - throw new UnsupportedOperationException(); - }); - } - - public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { - return new ContextualDataHolderProvider(plugin, this.createDataPerception(perspective)); - } - - public @Nullable ValueContainer get(final DataPerspective perspective) { - return this.perspectives.get(perspective); - } - - abstract class PerspectiveContainer implements ValueContainer { - - protected final T perspective; - - private final Map, Map> pluginValues; - protected final Map, Object> activeValues; - - protected PerspectiveContainer(final T perspective) { - this.perspective = perspective; - - this.pluginValues = Maps.newHashMap(); - this.activeValues = Maps.newHashMap(); - } - - DataTransactionResult offer(final PluginContainer pluginContainer, final Key> key, final E value) { - final @Nullable DataPerspectiveResolver, E> resolver = SpongeDataManager.getDataPerspectiveResolverRegistry().get(key); - if (resolver == null) { - return DataTransactionResult.failResult(Value.immutableOf(key, value)); - } - - final Map map = (Map) this.pluginValues.computeIfAbsent(key, k -> Maps.newLinkedHashMap()); - if (Objects.equals(value, map.put(pluginContainer, value))) { - return DataTransactionResult.successResult(Value.immutableOf(key, value)); - } - - final E mergedValue = resolver.merge(map.values()); - this.offer(key, resolver, mergedValue); - return DataTransactionResult.successResult(Value.immutableOf(key, mergedValue)); - } - - protected abstract void offer(final Key> key, final DataPerspectiveResolver, E> resolver, final E value); - - @Override - public Optional get(final Key> key) { - Objects.requireNonNull(key, "key"); - final @Nullable E value = (E) this.activeValues.get(key); - if (value == null) { - return ContextualDataHolder.this.dataHolder.get(key); - } - return Optional.of(CopyHelper.copy(value)); - } - - @Override - public > Optional getValue(final Key key) { - Objects.requireNonNull(key, "key"); - final @Nullable E value = (E) this.activeValues.get(key); - if (value == null) { - return ContextualDataHolder.this.dataHolder.getValue(key); - } - return Optional.of(Value.genericMutableOf(key, CopyHelper.copy(value))); - } - - @Override - public boolean supports(Key key) { - return ContextualDataHolder.this.dataHolder.supports(key); - } - - @Override - public Set> getKeys() { - return ContextualDataHolder.this.dataHolder.getKeys(); - } - - @Override - public Set> getValues() { - return ContextualDataHolder.this.dataHolder.getValues(); - } - } - - private abstract class NonEntityContainer extends PerspectiveContainer { - - protected NonEntityContainer(T perspective) { - super(perspective); - } - - protected abstract PerspectiveType type(); - - protected void offer(final Key> key, final DataPerspectiveResolver, E> resolver, final E value) { - if (Objects.equals(this.activeValues.put(key, value), value)) { - return; - } - - for (final DataPerspective perspective : this.perspective.perceives()) { - if (!(perspective instanceof Entity entity)) { - continue; - } - - final EntityPerspectiveContainer entityContainer = (EntityPerspectiveContainer) ContextualDataHolder.this.createDataPerception(entity); - entityContainer.offer(this.type(), key, resolver, value); - } - } - } - - private final class TeamPerspectiveContainer extends NonEntityContainer { - - public TeamPerspectiveContainer(final Team team) { - super(team); - } - - @Override - protected PerspectiveType type() { - return PerspectiveType.TEAM; - } - } - - private final class WorldPerspectiveContainer extends NonEntityContainer> { - - public WorldPerspectiveContainer(final World world) { - super(world); - } - - @Override - protected PerspectiveType type() { - return PerspectiveType.WORLD; - } - } - - private final class EntityPerspectiveContainer extends PerspectiveContainer { - - private final Map, EnumMap> perspectiveValues; - - public EntityPerspectiveContainer(final Entity entity) { - super(entity); - - this.perspectiveValues = Maps.newHashMap(); - } - - private EnumMap getValues(final Key> key) { - return (EnumMap) this.perspectiveValues.computeIfAbsent(key, k -> Maps.newEnumMap(PerspectiveType.class)); - } - - @Override - protected void offer(final Key> key, final DataPerspectiveResolver, E> resolver, final E value) { - this.getValues(key).put(PerspectiveType.ENTITY, value); - if (!Objects.equals(value, this.activeValues.put(key, value))) { - resolver.apply(ContextualDataHolder.this.dataHolder, this.perspective, value); - } - } - - void offer(final PerspectiveType type, final Key> key, final DataPerspectiveResolver, E> resolver, final E value) { - final EnumMap values = this.getValues(key); - values.put(type, value); - this.updatePerspective(key, values, resolver); - } - - private void updatePerspective(final Key> key, final EnumMap values, final DataPerspectiveResolver, E> resolver) { - for (final PerspectiveType type : PerspectiveType.values()) { - final @Nullable E value = values.get(type); - if (value == null) { - continue; - } - - if (!Objects.equals(this.activeValues.put(key, value), value)) { - resolver.apply(ContextualDataHolder.this.dataHolder, this.perspective, value); - } - - return; - } - } - } - - private enum PerspectiveType { - ENTITY, - TEAM, - WORLD - } -} diff --git a/src/main/java/org/spongepowered/common/data/SpongeDataManager.java b/src/main/java/org/spongepowered/common/data/SpongeDataManager.java index 6b66790994a..8a0a50714bd 100644 --- a/src/main/java/org/spongepowered/common/data/SpongeDataManager.java +++ b/src/main/java/org/spongepowered/common/data/SpongeDataManager.java @@ -92,6 +92,7 @@ import org.spongepowered.common.data.builder.util.weighted.BaseAndVarianceBuilder; import org.spongepowered.common.data.builder.util.weighted.FixedBuilder; import org.spongepowered.common.data.builder.util.weighted.OptionalVarianceBuilder; +import org.spongepowered.common.data.contextual.DataPerspectiveResolverRegistry; import org.spongepowered.common.data.datasync.entity.EntityAirSupplyConverter; import org.spongepowered.common.data.datasync.entity.EntityBabyConverter; import org.spongepowered.common.data.datasync.entity.EntityCustomNameConverter; diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java new file mode 100644 index 00000000000..a785e47816b --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java @@ -0,0 +1,32 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.contextual; + +import org.spongepowered.api.data.DataPerspective; + +public interface ContextualData { + + PerspectiveContainer createDataPerception(DataPerspective perspective); +} diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java new file mode 100644 index 00000000000..979d6f65adf --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java @@ -0,0 +1,111 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.contextual; + +import com.google.common.collect.Maps; +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.DataPerspectiveResolver; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.scoreboard.Team; +import org.spongepowered.api.world.World; +import org.spongepowered.plugin.PluginContainer; + +import java.util.Map; +import java.util.Objects; + +public final class ContextualDataDelegate implements ContextualData { + + private final DataPerspective perspective; + private final Map> perspectives; + + public ContextualDataDelegate(final DataPerspective perspective) { + this.perspective = perspective; + + this.perspectives = Maps.newHashMap(); + } + + public PerspectiveContainer createDataPerception(final DataPerspective perspective) { + return this.perspectives.computeIfAbsent(perspective, p -> { + if (p instanceof final Entity entity) { + return new EntityPerspectiveContainer(this, entity); + } else if (p instanceof final Team team) { + return new TeamPerspectiveContainer(this, team); + } else if (p instanceof final World world) { + return new WorldPerspectiveContainer(this, world); + } + throw new UnsupportedOperationException(); + }); + } + + public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { + return new ContextualDataProvider(this.createDataPerception(perspective), plugin); + } + + public ValueContainer getDataPerception(final DataPerspective perspective) { + return this.perspectives.get(perspective); + } + + private static abstract class AbstractPerspectiveContainer

extends PerspectiveContainer { + + protected AbstractPerspectiveContainer(final PerspectiveType perspectiveType, final ContextualDataDelegate holder, final P perspective) { + super(perspectiveType, holder, perspective); + } + + @Override + protected void offer(final PerspectiveType perspectiveType, final DataPerspectiveResolver, E> resolver, final E value) { + if (Objects.equals(this.activeValues.put(resolver.key(), value), value)) { + return; + } + + for (final DataPerspective perspective : this.holder.perspective.perceives()) { + ((ContextualData) perspective).createDataPerception(this.perspective).offer(PerspectiveType.TEAM, this.holder.perspective, resolver, value); + } + } + } + + private static final class EntityPerspectiveContainer extends AbstractPerspectiveContainer { + + private EntityPerspectiveContainer(final ContextualDataDelegate holder, final Entity entity) { + super(PerspectiveType.ENTITY, holder, entity); + } + } + + private static final class TeamPerspectiveContainer extends AbstractPerspectiveContainer { + + private TeamPerspectiveContainer(final ContextualDataDelegate holder, final Team team) { + super(PerspectiveType.TEAM, holder, team); + } + } + + private static final class WorldPerspectiveContainer extends AbstractPerspectiveContainer> { + + private WorldPerspectiveContainer(final ContextualDataDelegate holder, final World world) { + super(PerspectiveType.WORLD, holder, world); + } + } +} diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java new file mode 100644 index 00000000000..3d29e206614 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java @@ -0,0 +1,146 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.contextual; + +import com.google.common.collect.Maps; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.DataPerspectiveResolver; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.scoreboard.Team; +import org.spongepowered.api.world.World; +import org.spongepowered.plugin.PluginContainer; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Objects; + +@SuppressWarnings("unchecked") +public final class ContextualDataHolder implements ContextualData { + + private final DataHolder dataHolder; + + private final Map> perspectives; + + public ContextualDataHolder(final DataHolder dataHolder) { + this.dataHolder = dataHolder; + + this.perspectives = Maps.newHashMap(); + } + + public PerspectiveContainer createDataPerception(final DataPerspective perspective) { + return this.perspectives.computeIfAbsent(perspective, p -> { + if (p instanceof final Entity entity) { + return new EntityPerspectiveContainer(this, entity); + } else if (p instanceof final Team team) { + return new TeamPerspectiveContainer(this, team); + } else if (p instanceof final World world) { + return new WorldPerspectiveContainer(this, world); + } + throw new UnsupportedOperationException(); + }); + } + + public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { + return new ContextualDataProvider(this.createDataPerception(perspective), plugin); + } + + public @Nullable ValueContainer get(final DataPerspective perspective) { + return this.perspectives.get(perspective); + } + + private static final class EntityPerspectiveContainer extends PerspectiveContainer { + + private final Map, EnumMap> perspectiveValues; + + private EntityPerspectiveContainer(final ContextualDataHolder holder, final Entity entity) { + super(PerspectiveType.ENTITY, holder, entity); + + this.perspectiveValues = Maps.newHashMap(); + } + + private EnumMap getValues(final Key> key) { + return (EnumMap) this.perspectiveValues.computeIfAbsent(key, k -> Maps.newEnumMap(PerspectiveType.class)); + } + + @Override + protected void offer(final PerspectiveType perspectiveType, final DataPerspectiveResolver, E> resolver, final E value) { + final EnumMap values = this.getValues(resolver.key()); + values.put(perspectiveType, value); + this.updatePerspective(values, resolver); + } + + private void updatePerspective(final EnumMap values, final DataPerspectiveResolver, E> resolver) { + for (final PerspectiveType type : PerspectiveType.values()) { + final @Nullable E value = values.get(type); + if (value == null) { + continue; + } + + if (!Objects.equals(this.activeValues.put(resolver.key(), value), value)) { + resolver.apply(this.holder.dataHolder, this.perspective, value); + } + + return; + } + } + } + + private static abstract class NonEntityContainer

extends PerspectiveContainer { + + protected NonEntityContainer(final PerspectiveType perspectiveType, final ContextualDataHolder holder, final P perspective) { + super(perspectiveType, holder, perspective); + } + + @Override + protected void offer(final PerspectiveType perspectiveType, final DataPerspectiveResolver, E> resolver, final E value) { + if (Objects.equals(this.activeValues.put(resolver.key(), value), value)) { + return; + } + + for (final DataPerspective perspective : this.perspective.perceives()) { + this.holder.createDataPerception(perspective).offer(perspectiveType, this.perspective, resolver, value); + } + } + } + + private static final class TeamPerspectiveContainer extends NonEntityContainer { + + private TeamPerspectiveContainer(final ContextualDataHolder holder, final Team team) { + super(PerspectiveType.TEAM, holder, team); + } + } + + private static final class WorldPerspectiveContainer extends NonEntityContainer> { + + private WorldPerspectiveContainer(final ContextualDataHolder holder, final World world) { + super(PerspectiveType.WORLD, holder, world); + } + } +} diff --git a/src/main/java/org/spongepowered/common/data/ContextualDataHolderProvider.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataProvider.java similarity index 94% rename from src/main/java/org/spongepowered/common/data/ContextualDataHolderProvider.java rename to src/main/java/org/spongepowered/common/data/contextual/ContextualDataProvider.java index 9e0ca25765c..685dedec43b 100644 --- a/src/main/java/org/spongepowered/common/data/ContextualDataHolderProvider.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataProvider.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.data; +package org.spongepowered.common.data.contextual; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataTransactionResult; @@ -41,14 +41,14 @@ import java.util.Set; @SuppressWarnings("unchecked") -final class ContextualDataHolderProvider implements DataHolder.Mutable { +final class ContextualDataProvider implements DataHolder.Mutable { + private final PerspectiveContainer container; private final PluginContainer plugin; - private final ContextualDataHolder.PerspectiveContainer container; - ContextualDataHolderProvider(final PluginContainer plugin, final ContextualDataHolder.PerspectiveContainer container) { - this.plugin = plugin; + ContextualDataProvider(final PerspectiveContainer container, final PluginContainer plugin) { this.container = container; + this.plugin = plugin; } @Override diff --git a/src/main/java/org/spongepowered/common/data/DataPerspectiveResolverRegistry.java b/src/main/java/org/spongepowered/common/data/contextual/DataPerspectiveResolverRegistry.java similarity index 97% rename from src/main/java/org/spongepowered/common/data/DataPerspectiveResolverRegistry.java rename to src/main/java/org/spongepowered/common/data/contextual/DataPerspectiveResolverRegistry.java index 1a2a26b2036..06e33687325 100644 --- a/src/main/java/org/spongepowered/common/data/DataPerspectiveResolverRegistry.java +++ b/src/main/java/org/spongepowered/common/data/contextual/DataPerspectiveResolverRegistry.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.data; +package org.spongepowered.common.data.contextual; import com.google.common.collect.Maps; import org.checkerframework.checker.nullness.qual.Nullable; diff --git a/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java new file mode 100644 index 00000000000..8677d88591b --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java @@ -0,0 +1,114 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.contextual; + +import com.google.common.collect.Maps; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.DataPerspectiveResolver; +import org.spongepowered.api.data.DataTransactionResult; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.common.data.SpongeDataManager; +import org.spongepowered.common.util.CopyHelper; +import org.spongepowered.plugin.PluginContainer; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +@SuppressWarnings("unchecked") +public abstract class PerspectiveContainer implements ValueContainer { + + private final PerspectiveType perspectiveType; + protected final H holder; + protected final P perspective; + + private final Map, Map> valuesByOwner; + protected final Map, Object> activeValues; + + protected PerspectiveContainer(final PerspectiveType perspectiveType, final H holder, final P perspective) { + this.perspectiveType = perspectiveType; + this.holder = holder; + this.perspective = perspective; + + this.valuesByOwner = Maps.newHashMap(); + this.activeValues = Maps.newHashMap(); + } + + final DataTransactionResult offer(final PluginContainer pluginContainer, final Key> key, final E value) { + final @Nullable DataPerspectiveResolver, E> resolver = SpongeDataManager.getDataPerspectiveResolverRegistry().get(key); + if (resolver == null) { + return DataTransactionResult.failResult(Value.immutableOf(key, value)); + } + + return this.offer(this.perspectiveType, pluginContainer, resolver, value); + } + + final DataTransactionResult offer(final PerspectiveType perspectiveType, final Object owner, final DataPerspectiveResolver, E> resolver, final E value) { + final Map valueMap = (Map) this.valuesByOwner.computeIfAbsent(resolver.key(), k -> Maps.newLinkedHashMap()); + if (Objects.equals(value, valueMap.put(owner, value))) { + return DataTransactionResult.successResult(Value.immutableOf(resolver.key(), value)); + } + + final E mergedValue = resolver.merge(valueMap.values()); + this.offer(perspectiveType, resolver, mergedValue); + return DataTransactionResult.successResult(Value.immutableOf(resolver.key(), mergedValue)); + } + + protected abstract void offer(final PerspectiveType perspectiveType, final DataPerspectiveResolver, E> resolver, final E value); + + @Override + public Optional get(final Key> key) { + Objects.requireNonNull(key, "key"); + final @Nullable E value = (E) this.activeValues.get(key); + return Optional.ofNullable(CopyHelper.copy(value)); + } + + @Override + public > Optional getValue(final Key key) { + Objects.requireNonNull(key, "key"); + final @Nullable E value = (E) this.activeValues.get(key); + return Optional.of(Value.genericMutableOf(key, CopyHelper.copy(value))); + } + + @Override + public boolean supports(Key key) { + return false; + } + + @Override + public Set> getKeys() { + return Collections.emptySet(); + } + + @Override + public Set> getValues() { + return Collections.emptySet(); + } +} diff --git a/src/main/java/org/spongepowered/common/data/contextual/PerspectiveType.java b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveType.java new file mode 100644 index 00000000000..e88bb62c05c --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveType.java @@ -0,0 +1,31 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.contextual; + +public enum PerspectiveType { + ENTITY, + TEAM, + WORLD +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java index 31b6e9da153..ab9a3d970c7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java @@ -63,7 +63,7 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.entity.EntityBridge; -import org.spongepowered.common.data.ContextualDataHolder; +import org.spongepowered.common.bridge.world.entity.EntityBridge_Contextual; import org.spongepowered.common.data.persistence.NBTTranslator; import org.spongepowered.common.entity.SpongeEntityArchetypeBuilder; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -109,8 +109,6 @@ public abstract class EntityMixin_API implements org.spongepowered.api.entity.En @Shadow public abstract CompoundTag shadow$saveWithoutId(CompoundTag $$0); // @formatter:on - private ContextualDataHolder impl$contextualData = new ContextualDataHolder(this); - @Override public Source random() { return (Source) this.random; @@ -407,7 +405,7 @@ public Iterable perceives() { @Override public ValueContainer getDataPerception(final DataPerspective perspective) { - final @Nullable ValueContainer container = this.impl$contextualData.get(perspective); + final @Nullable ValueContainer container = ((EntityBridge_Contextual) this).bridge$contextualData().get(perspective); if (container != null) { return container; } @@ -416,6 +414,6 @@ public ValueContainer getDataPerception(final DataPerspective perspective) { @Override public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { - return this.impl$contextualData.createDataPerception(plugin, perspective); + return ((EntityBridge_Contextual) this).bridge$contextualData().createDataPerception(plugin, perspective); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java index 4deb80f682b..7e9f2e502e5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java @@ -45,6 +45,7 @@ import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge; +import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge_Contextual; import org.spongepowered.plugin.PluginContainer; import java.util.Collection; @@ -232,14 +233,13 @@ public Iterable perceives() { } @Override - public ValueContainer getDataPerception(DataPerspective perspective) { + public ValueContainer getDataPerception(final DataPerspective perspective) { return null; } @Override - public DataHolder.Mutable createDataPerception(PluginContainer plugin, DataPerspective perspective) { - - return null; + public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { + return ((PlayerTeamBridge_Contextual) this).bridge$contextualData().createDataPerception(plugin, perspective); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java index 1d6764feaaf..ca6407284b5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java @@ -111,10 +111,14 @@ import org.spongepowered.common.bridge.data.TransientBridge; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.entity.EntityBridge; +import org.spongepowered.common.bridge.world.entity.EntityBridge_Contextual; import org.spongepowered.common.bridge.world.entity.PlatformEntityBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.bridge.world.level.PlatformServerLevelBridge; import org.spongepowered.common.data.DataUtil; +import org.spongepowered.common.data.contextual.ContextualData; +import org.spongepowered.common.data.contextual.ContextualDataHolder; +import org.spongepowered.common.data.contextual.PerspectiveContainer; import org.spongepowered.common.data.provider.nbt.NBTDataType; import org.spongepowered.common.data.provider.nbt.NBTDataTypes; import org.spongepowered.common.data.value.ImmutableSpongeValue; @@ -142,7 +146,7 @@ import java.util.UUID; @Mixin(Entity.class) -public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, VanishableBridge, CommandSourceProviderBridge, DataCompoundHolder, TransientBridge { +public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, VanishableBridge, CommandSourceProviderBridge, DataCompoundHolder, TransientBridge, EntityBridge_Contextual, ContextualData { // @formatter:off @@ -257,6 +261,8 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, // Structure: tileNbt - ForgeData - SpongeData - customdata private CompoundTag impl$customDataCompound; + private final ContextualDataHolder impl$contextualData = new ContextualDataHolder((DataHolder) this); + @Override public boolean bridge$isConstructing() { return this.impl$isConstructing; @@ -1330,4 +1336,13 @@ public void stopRiding() { }*/ + @Override + public ContextualDataHolder bridge$contextualData() { + return this.impl$contextualData; + } + + @Override + public PerspectiveContainer createDataPerception(final DataPerspective perspective) { + return this.impl$contextualData.createDataPerception(perspective); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java index 6e46f440fa7..f312ae2df02 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java @@ -36,6 +36,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.DataPerspective; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; @@ -46,13 +47,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge; +import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge_Contextual; +import org.spongepowered.common.data.contextual.ContextualData; +import org.spongepowered.common.data.contextual.ContextualDataDelegate; +import org.spongepowered.common.data.contextual.PerspectiveContainer; import java.util.Collection; import java.util.Optional; import java.util.stream.Collectors; @Mixin(PlayerTeam.class) -public abstract class PlayerTeamMixin implements PlayerTeamBridge { +public abstract class PlayerTeamMixin implements PlayerTeamBridge, PlayerTeamBridge_Contextual, ContextualData { // @formatter:off @Shadow @Final @Mutable @Nullable private Scoreboard scoreboard; @@ -68,6 +73,8 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge { private @MonotonicNonNull Component bridge$suffix; private @MonotonicNonNull NamedTextColor bridge$color; + private final ContextualDataDelegate impl$contextualData = new ContextualDataDelegate((DataPerspective) this); + private void impl$teamChanged() { if (this.scoreboard != null) { this.scoreboard.onTeamChanged((PlayerTeam) (Object) this); @@ -204,4 +211,14 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge { .filter(player -> ((ServerPlayer) player).getTeam() != (Object) this) .collect(Collectors.toSet())); } + + @Override + public ContextualDataDelegate bridge$contextualData() { + return this.impl$contextualData; + } + + @Override + public PerspectiveContainer createDataPerception(final DataPerspective perspective) { + return this.impl$contextualData.createDataPerception(perspective); + } } From a51ed468f9d2f16e5b348769a48cbceb685c3433 Mon Sep 17 00:00:00 2001 From: aromaa Date: Sun, 21 Apr 2024 21:37:54 +0300 Subject: [PATCH 3/7] Hacky contextual custom name --- .../data/contextual/ContextualData.java | 3 ++ .../contextual/ContextualDataDelegate.java | 7 +++ .../data/contextual/ContextualDataHolder.java | 43 ++++++++++++++++++- .../data/provider/entity/EntityData.java | 10 ++++- .../level/ChunkMap_TrackedEntityMixin.java | 25 ++++++++++- .../core/server/level/ServerEntityMixin.java | 26 +++++++++++ .../mixin/core/world/entity/EntityMixin.java | 8 ++++ 7 files changed, 117 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java index a785e47816b..dc3d4c96b62 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java @@ -24,9 +24,12 @@ */ package org.spongepowered.common.data.contextual; +import net.minecraft.network.protocol.Packet; import org.spongepowered.api.data.DataPerspective; public interface ContextualData { PerspectiveContainer createDataPerception(DataPerspective perspective); + + void broadcastToPerceives(Packet packet); } diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java index 979d6f65adf..7a4014b53ac 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java @@ -25,6 +25,7 @@ package org.spongepowered.common.data.contextual; import com.google.common.collect.Maps; +import net.minecraft.network.protocol.Packet; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.DataPerspectiveResolver; @@ -49,6 +50,7 @@ public ContextualDataDelegate(final DataPerspective perspective) { this.perspectives = Maps.newHashMap(); } + @Override public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.perspectives.computeIfAbsent(perspective, p -> { if (p instanceof final Entity entity) { @@ -62,6 +64,11 @@ public ContextualDataDelegate(final DataPerspective perspective) { }); } + @Override + public void broadcastToPerceives(Packet packet) { + ((ContextualData) this.perspective).broadcastToPerceives(packet); + } + public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { return new ContextualDataProvider(this.createDataPerception(perspective), plugin); } diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java index 3d29e206614..e2b83c707b6 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java @@ -25,6 +25,8 @@ package org.spongepowered.common.data.contextual; import com.google.common.collect.Maps; +import net.minecraft.network.protocol.Packet; +import net.minecraft.server.level.ServerPlayer; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataPerspective; @@ -35,11 +37,13 @@ import org.spongepowered.api.entity.Entity; import org.spongepowered.api.scoreboard.Team; import org.spongepowered.api.world.World; +import org.spongepowered.common.util.CopyHelper; import org.spongepowered.plugin.PluginContainer; import java.util.EnumMap; import java.util.Map; import java.util.Objects; +import java.util.Optional; @SuppressWarnings("unchecked") public final class ContextualDataHolder implements ContextualData { @@ -54,6 +58,7 @@ public ContextualDataHolder(final DataHolder dataHolder) { this.perspectives = Maps.newHashMap(); } + @Override public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.perspectives.computeIfAbsent(perspective, p -> { if (p instanceof final Entity entity) { @@ -67,6 +72,13 @@ public ContextualDataHolder(final DataHolder dataHolder) { }); } + @Override + public void broadcastToPerceives(final Packet packet) { + if (this.dataHolder instanceof final ServerPlayer player) { + player.connection.send(packet); + } + } + public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { return new ContextualDataProvider(this.createDataPerception(perspective), plugin); } @@ -75,7 +87,34 @@ public DataHolder.Mutable createDataPerception(final PluginContainer plugin, fin return this.perspectives.get(perspective); } - private static final class EntityPerspectiveContainer extends PerspectiveContainer { + private static abstract class AbstractPerspectiveContainer

extends PerspectiveContainer { + + protected AbstractPerspectiveContainer(PerspectiveType perspectiveType, ContextualDataHolder holder, P perspective) { + super(perspectiveType, holder, perspective); + } + + @Override + public Optional get(final Key> key) { + Objects.requireNonNull(key, "key"); + final @Nullable E value = (E) this.activeValues.get(key); + if (value == null) { + return this.holder.dataHolder.get(key); + } + return Optional.ofNullable(CopyHelper.copy(value)); + } + + @Override + public > Optional getValue(final Key key) { + Objects.requireNonNull(key, "key"); + final @Nullable E value = (E) this.activeValues.get(key); + if (value == null) { + return this.holder.dataHolder.getValue(key); + } + return Optional.of(Value.genericMutableOf(key, CopyHelper.copy(value))); + } + } + + private static final class EntityPerspectiveContainer extends AbstractPerspectiveContainer { private final Map, EnumMap> perspectiveValues; @@ -112,7 +151,7 @@ private void updatePerspective(final EnumMap values, fin } } - private static abstract class NonEntityContainer

extends PerspectiveContainer { + private static abstract class NonEntityContainer

extends AbstractPerspectiveContainer

{ protected NonEntityContainer(final PerspectiveType perspectiveType, final ContextualDataHolder holder, final P perspective) { super(perspectiveType, holder, perspective); diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java b/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java index 385a55b994a..baa709f7702 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java @@ -25,6 +25,8 @@ package org.spongepowered.common.data.provider.entity; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; @@ -37,6 +39,7 @@ import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.world.entity.EntityBridge; import org.spongepowered.common.bridge.world.entity.EntityMaxAirBridge; +import org.spongepowered.common.data.contextual.ContextualData; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.entity.SpongeEntityArchetype; import org.spongepowered.common.entity.SpongeEntitySnapshot; @@ -44,6 +47,7 @@ import org.spongepowered.common.util.SpongeTicks; import org.spongepowered.common.util.VecHelper; +import java.util.Collections; import java.util.stream.Collectors; public final class EntityData { @@ -74,13 +78,17 @@ public static void register(final DataProviderRegistrator registrator) { } return (org.spongepowered.api.entity.Entity) rootVehicle; }) - .create(Keys.CUSTOM_NAME) + .createContextual(Keys.CUSTOM_NAME) .get(h -> h.hasCustomName() ? SpongeAdventure.asAdventure(h.getCustomName()) : null) .set((h, v) -> h.setCustomName(SpongeAdventure.asVanilla(v))) .delete(h -> { h.setCustomName(null); h.setCustomNameVisible(false); }) + .dataPerspectiveMerge(values -> values.iterator().next()) + .dataPerspectiveApply((h, p, v) -> ((ContextualData) p).broadcastToPerceives(new ClientboundSetEntityDataPacket(h.getId(), + Collections.singletonList(SynchedEntityData.DataValue.create(EntityAccessor.accessor$DATA_CUSTOM_NAME(), SpongeAdventure.asVanillaOpt(v))))) + ) .create(Keys.DISPLAY_NAME) .get(h -> SpongeAdventure.asAdventure(h.getDisplayName())) .create(Keys.EYE_HEIGHT) diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java index 69aa44f3fd7..524daab8e4c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java @@ -25,20 +25,27 @@ package org.spongepowered.common.mixin.core.server.level; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.world.entity.Entity; import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.value.ValueContainer; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.accessor.world.entity.EntityAccessor; +import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.entity.living.human.HumanEntity; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Stream; @Mixin(targets = "net/minecraft/server/level/ChunkMap$TrackedEntity") @@ -63,11 +70,25 @@ public abstract class ChunkMap_TrackedEntityMixin { @Redirect(method = "broadcast(Lnet/minecraft/network/protocol/Packet;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerConnection;send(Lnet/minecraft/network/protocol/Packet;)V")) private void impl$sendQueuedHumanPackets(final ServerPlayerConnection serverPlayNetHandler, final Packet packetIn) { - if (((DataPerspective) this.entity).getDataPerception((DataPerspective) serverPlayNetHandler.getPlayer()).require(Keys.VANISH_STATE).invisible()) { + final ValueContainer contextualData = ((DataPerspective) this.entity).getDataPerception((DataPerspective) serverPlayNetHandler.getPlayer()); + if (contextualData.require(Keys.VANISH_STATE).invisible()) { return; } - serverPlayNetHandler.send(packetIn); + if (packetIn instanceof final ClientboundSetEntityDataPacket entityDataPacket) { + List> values = new ArrayList<>(); + for (final SynchedEntityData.DataValue dataValue : entityDataPacket.packedItems()) { + if (dataValue.id() == EntityAccessor.accessor$DATA_CUSTOM_NAME().getId()) { + values.add(SynchedEntityData.DataValue.create( + EntityAccessor.accessor$DATA_CUSTOM_NAME(), SpongeAdventure.asVanillaOpt(contextualData.require(Keys.CUSTOM_NAME)))); + } else { + values.add(dataValue); + } + } + serverPlayNetHandler.send(new ClientboundSetEntityDataPacket(this.entity.getId(), values)); + } else { + serverPlayNetHandler.send(packetIn); + } if (this.entity instanceof HumanEntity && serverPlayNetHandler instanceof ServerGamePacketListenerImpl) { final ServerPlayer player = ((ServerGamePacketListenerImpl) serverPlayNetHandler).player; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java index eb423b64f86..853e5a6619a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java @@ -25,14 +25,19 @@ package org.spongepowered.common.mixin.core.server.level; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; @@ -42,11 +47,14 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.accessor.world.entity.EntityAccessor; import org.spongepowered.common.accessor.world.entity.LivingEntityAccessor; +import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.server.level.ServerPlayerBridge; import org.spongepowered.common.entity.living.human.HumanEntity; +import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.List; @@ -166,4 +174,22 @@ public abstract class ServerEntityMixin { return packed; } + + @Redirect(method = "sendPairingData", at = @At(value = "INVOKE", remap = false, target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V", ordinal = 1)) + private void impl$modifyContextualData(final Consumer> instance, final Object packet, final ServerPlayer player) { + final ValueContainer contextualData = ((DataPerspective) this.entity).getDataPerception((DataPerspective) player); + + List> values = new ArrayList<>(); + for (final SynchedEntityData.DataValue dataValue : ((ClientboundSetEntityDataPacket) packet).packedItems()) { + if (dataValue.id() == EntityAccessor.accessor$DATA_CUSTOM_NAME().getId()) { + values.add(SynchedEntityData.DataValue.create( + EntityAccessor.accessor$DATA_CUSTOM_NAME(), SpongeAdventure.asVanillaOpt(contextualData.require(Keys.CUSTOM_NAME)))); + } else { + values.add(dataValue); + } + } + + instance.accept(new ClientboundSetEntityDataPacket(this.entity.getId(), values)); + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java index ca6407284b5..3df4d677a79 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java @@ -32,6 +32,7 @@ import net.minecraft.core.particles.ParticleOptions; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; import net.minecraft.network.syncher.SynchedEntityData; @@ -1345,4 +1346,11 @@ public void stopRiding() { public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.impl$contextualData.createDataPerception(perspective); } + + @Override + public void broadcastToPerceives(final Packet packet) { + if ((Object) this instanceof ServerPlayer player) { + player.connection.send(packet); + } + } } From cb9caeb75e6712ebf412522a36861847d58959c1 Mon Sep 17 00:00:00 2001 From: aromaa Date: Tue, 23 Apr 2024 15:54:17 +0300 Subject: [PATCH 4/7] Optimize contextual entity data --- .../data/contextual/ContextualData.java | 3 + .../contextual/ContextualDataDelegate.java | 11 +-- .../data/contextual/ContextualDataHolder.java | 10 +-- .../data/contextual/PerspectiveContainer.java | 12 +++ .../contextual/util/ContextualPacketUtil.java | 71 ++++++++++++++++++ .../syncher/SpongeSynchedEntityDataList.java | 75 +++++++++++++++++++ .../world/entity/EntityMixin_API.java | 2 +- .../syncher/SynchedEntityDataMixin.java | 21 ++++++ .../level/ChunkMap_TrackedEntityMixin.java | 27 +++---- .../core/server/level/ServerEntityMixin.java | 23 +----- .../mixin/core/world/entity/EntityMixin.java | 5 ++ .../core/world/scores/PlayerTeamMixin.java | 5 ++ 12 files changed, 217 insertions(+), 48 deletions(-) create mode 100644 src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java create mode 100644 src/main/java/org/spongepowered/common/network/syncher/SpongeSynchedEntityDataList.java diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java index dc3d4c96b62..cc787d6315e 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java @@ -25,10 +25,13 @@ package org.spongepowered.common.data.contextual; import net.minecraft.network.protocol.Packet; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.DataPerspective; public interface ContextualData { + @Nullable PerspectiveContainer getDataPerception(DataPerspective perspective); + PerspectiveContainer createDataPerception(DataPerspective perspective); void broadcastToPerceives(Packet packet); diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java index 7a4014b53ac..59a506c170b 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java @@ -26,11 +26,11 @@ import com.google.common.collect.Maps; import net.minecraft.network.protocol.Packet; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.scoreboard.Team; import org.spongepowered.api.world.World; @@ -50,6 +50,11 @@ public ContextualDataDelegate(final DataPerspective perspective) { this.perspectives = Maps.newHashMap(); } + @Override + public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { + return this.perspectives.get(perspective); + } + @Override public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.perspectives.computeIfAbsent(perspective, p -> { @@ -73,10 +78,6 @@ public DataHolder.Mutable createDataPerception(final PluginContainer plugin, fin return new ContextualDataProvider(this.createDataPerception(perspective), plugin); } - public ValueContainer getDataPerception(final DataPerspective perspective) { - return this.perspectives.get(perspective); - } - private static abstract class AbstractPerspectiveContainer

extends PerspectiveContainer { protected AbstractPerspectiveContainer(final PerspectiveType perspectiveType, final ContextualDataDelegate holder, final P perspective) { diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java index e2b83c707b6..22200302657 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java @@ -33,7 +33,6 @@ import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.Key; import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.scoreboard.Team; import org.spongepowered.api.world.World; @@ -58,6 +57,11 @@ public ContextualDataHolder(final DataHolder dataHolder) { this.perspectives = Maps.newHashMap(); } + @Override + public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { + return this.perspectives.get(perspective); + } + @Override public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.perspectives.computeIfAbsent(perspective, p -> { @@ -83,10 +87,6 @@ public DataHolder.Mutable createDataPerception(final PluginContainer plugin, fin return new ContextualDataProvider(this.createDataPerception(perspective), plugin); } - public @Nullable ValueContainer get(final DataPerspective perspective) { - return this.perspectives.get(perspective); - } - private static abstract class AbstractPerspectiveContainer

extends PerspectiveContainer { protected AbstractPerspectiveContainer(PerspectiveType perspectiveType, ContextualDataHolder holder, P perspective) { diff --git a/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java index 8677d88591b..c8142588236 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java +++ b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java @@ -30,8 +30,10 @@ import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.DataTransactionResult; import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.common.accessor.world.entity.EntityAccessor; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.util.CopyHelper; import org.spongepowered.plugin.PluginContainer; @@ -52,6 +54,8 @@ public abstract class PerspectiveContainer, Map> valuesByOwner; protected final Map, Object> activeValues; + private long entityDataFlags; + protected PerspectiveContainer(final PerspectiveType perspectiveType, final H holder, final P perspective) { this.perspectiveType = perspectiveType; this.holder = holder; @@ -61,6 +65,10 @@ protected PerspectiveContainer(final PerspectiveType perspectiveType, final H ho this.activeValues = Maps.newHashMap(); } + public long entityDataFlags() { + return this.entityDataFlags; + } + final DataTransactionResult offer(final PluginContainer pluginContainer, final Key> key, final E value) { final @Nullable DataPerspectiveResolver, E> resolver = SpongeDataManager.getDataPerspectiveResolverRegistry().get(key); if (resolver == null) { @@ -76,6 +84,10 @@ final DataTransactionResult offer(final PerspectiveType perspectiveType, fin return DataTransactionResult.successResult(Value.immutableOf(resolver.key(), value)); } + if (resolver.key() == (Key)Keys.CUSTOM_NAME) { + entityDataFlags |= 1L << EntityAccessor.accessor$DATA_CUSTOM_NAME().getId(); + } + final E mergedValue = resolver.merge(valueMap.values()); this.offer(perspectiveType, resolver, mergedValue); return DataTransactionResult.successResult(Value.immutableOf(resolver.key(), mergedValue)); diff --git a/src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java b/src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java new file mode 100644 index 00000000000..bb77704c7ec --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java @@ -0,0 +1,71 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.contextual.util; + +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.syncher.SynchedEntityData; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.api.data.Keys; +import org.spongepowered.common.accessor.world.entity.EntityAccessor; +import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.data.contextual.ContextualData; +import org.spongepowered.common.data.contextual.PerspectiveContainer; +import org.spongepowered.common.network.syncher.SpongeSynchedEntityDataList; + +import java.util.ArrayList; +import java.util.List; + +public final class ContextualPacketUtil { + + public static ClientboundSetEntityDataPacket createContextualPacket( + final ClientboundSetEntityDataPacket original, final ContextualData context, final DataPerspective perspective) { + final @Nullable PerspectiveContainer contextualData = context.getDataPerception(perspective); + if (contextualData == null) { + return original; + } + return ContextualPacketUtil.createContextualPacket(original, contextualData); + } + + public static ClientboundSetEntityDataPacket createContextualPacket( + final ClientboundSetEntityDataPacket original, final PerspectiveContainer contextualData) { + if (original.packedItems() instanceof final SpongeSynchedEntityDataList entityDataList + && (entityDataList.flags() & contextualData.entityDataFlags()) == 0L) { + return original; + } + + List> values = new ArrayList<>(original.packedItems().size()); + for (final SynchedEntityData.DataValue dataValue : original.packedItems()) { + if (dataValue.id() == EntityAccessor.accessor$DATA_CUSTOM_NAME().getId()) { + values.add(SynchedEntityData.DataValue.create( + EntityAccessor.accessor$DATA_CUSTOM_NAME(), SpongeAdventure.asVanillaOpt(contextualData.require(Keys.CUSTOM_NAME)))); + } else { + values.add(dataValue); + } + } + + return new ClientboundSetEntityDataPacket(original.id(), values); + } +} diff --git a/src/main/java/org/spongepowered/common/network/syncher/SpongeSynchedEntityDataList.java b/src/main/java/org/spongepowered/common/network/syncher/SpongeSynchedEntityDataList.java new file mode 100644 index 00000000000..ad0b17b2fe4 --- /dev/null +++ b/src/main/java/org/spongepowered/common/network/syncher/SpongeSynchedEntityDataList.java @@ -0,0 +1,75 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.network.syncher; + +import net.minecraft.network.syncher.SynchedEntityData; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; + +public final class SpongeSynchedEntityDataList extends AbstractList> { + + private final List> list; + + private long flags; + + public SpongeSynchedEntityDataList() { + this.list = new ArrayList<>(); + } + + public long flags() { + return this.flags; + } + + @Override + public int size() { + return this.list.size(); + } + + @Override + public SynchedEntityData.DataValue get(final int index) { + return this.list.get(index); + } + + @Override + public SynchedEntityData.DataValue set(final int index, final SynchedEntityData.DataValue element) { + this.flags |= 1L << element.id(); + return this.list.set(index, element); + } + + @Override + public void add(final int index, final SynchedEntityData.DataValue element) { + this.flags |= 1L << element.id(); + this.list.add(index, element); + } + + @Override + public SynchedEntityData.DataValue remove(final int index) { + final SynchedEntityData.DataValue element = this.list.remove(index); + this.flags &= ~(1L << element.id()); + return element; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java index ab9a3d970c7..ea163cba164 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java @@ -405,7 +405,7 @@ public Iterable perceives() { @Override public ValueContainer getDataPerception(final DataPerspective perspective) { - final @Nullable ValueContainer container = ((EntityBridge_Contextual) this).bridge$contextualData().get(perspective); + final @Nullable ValueContainer container = ((EntityBridge_Contextual) this).bridge$contextualData().getDataPerception(perspective); if (container != null) { return container; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java index bb6aca6c5ac..eadc1e24fa5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java @@ -37,11 +37,16 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.common.bridge.network.syncher.EntityDataAccessorBridge; import org.spongepowered.common.bridge.world.entity.EntityBridge; import org.spongepowered.common.data.datasync.DataParameterConverter; import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.network.syncher.SpongeSynchedEntityDataList; +import java.util.List; import java.util.Optional; @Mixin(SynchedEntityData.class) @@ -108,4 +113,20 @@ public void set(final EntityDataAccessor key, T value) { this.isDirty = true; } } + + @ModifyVariable(method = "packDirty", at = @At("STORE"), slice = @Slice( + from = @At(value = "INVOKE", target = "java/util/ArrayList.()V", remap = false), + to = @At(value = "INVOKE", target = "java/util/List.add (Ljava/lang/Object;)Z", remap = false) + )) + private List> impl$useTrackingList(final List> original) { + return new SpongeSynchedEntityDataList(); + } + + @ModifyVariable(method = "getNonDefaultValues", at = @At("STORE"), slice = @Slice( + from = @At(value = "INVOKE", target = "java/util/ArrayList.()V", remap = false), + to = @At(value = "INVOKE", target = "java/util/List.add (Ljava/lang/Object;)Z", remap = false) + )) + private List> impl$useTrackingList2(final List> original) { + return new SpongeSynchedEntityDataList(); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java index 524daab8e4c..196ad8013be 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java @@ -26,11 +26,11 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; -import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.world.entity.Entity; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.ValueContainer; @@ -40,12 +40,11 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.accessor.world.entity.EntityAccessor; -import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.data.contextual.ContextualData; +import org.spongepowered.common.data.contextual.PerspectiveContainer; +import org.spongepowered.common.data.contextual.util.ContextualPacketUtil; import org.spongepowered.common.entity.living.human.HumanEntity; -import java.util.ArrayList; -import java.util.List; import java.util.stream.Stream; @Mixin(targets = "net/minecraft/server/level/ChunkMap$TrackedEntity") @@ -70,22 +69,14 @@ public abstract class ChunkMap_TrackedEntityMixin { @Redirect(method = "broadcast(Lnet/minecraft/network/protocol/Packet;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerConnection;send(Lnet/minecraft/network/protocol/Packet;)V")) private void impl$sendQueuedHumanPackets(final ServerPlayerConnection serverPlayNetHandler, final Packet packetIn) { - final ValueContainer contextualData = ((DataPerspective) this.entity).getDataPerception((DataPerspective) serverPlayNetHandler.getPlayer()); - if (contextualData.require(Keys.VANISH_STATE).invisible()) { + final @Nullable PerspectiveContainer contextualData = ((ContextualData) this.entity).getDataPerception((DataPerspective) serverPlayNetHandler.getPlayer()); + final ValueContainer dataPerception = contextualData != null ? contextualData : (ValueContainer) this.entity; + if (dataPerception.require(Keys.VANISH_STATE).invisible()) { return; } - if (packetIn instanceof final ClientboundSetEntityDataPacket entityDataPacket) { - List> values = new ArrayList<>(); - for (final SynchedEntityData.DataValue dataValue : entityDataPacket.packedItems()) { - if (dataValue.id() == EntityAccessor.accessor$DATA_CUSTOM_NAME().getId()) { - values.add(SynchedEntityData.DataValue.create( - EntityAccessor.accessor$DATA_CUSTOM_NAME(), SpongeAdventure.asVanillaOpt(contextualData.require(Keys.CUSTOM_NAME)))); - } else { - values.add(dataValue); - } - } - serverPlayNetHandler.send(new ClientboundSetEntityDataPacket(this.entity.getId(), values)); + if (contextualData != null && packetIn instanceof final ClientboundSetEntityDataPacket entityDataPacket) { + serverPlayNetHandler.send(ContextualPacketUtil.createContextualPacket(entityDataPacket, contextualData)); } else { serverPlayNetHandler.send(packetIn); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java index 853e5a6619a..2640a0e8942 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java @@ -36,8 +36,6 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import org.spongepowered.api.data.DataPerspective; -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; @@ -47,14 +45,13 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.accessor.world.entity.EntityAccessor; import org.spongepowered.common.accessor.world.entity.LivingEntityAccessor; -import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.server.level.ServerPlayerBridge; +import org.spongepowered.common.data.contextual.ContextualData; +import org.spongepowered.common.data.contextual.util.ContextualPacketUtil; import org.spongepowered.common.entity.living.human.HumanEntity; -import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.List; @@ -176,20 +173,8 @@ public abstract class ServerEntityMixin { @Redirect(method = "sendPairingData", at = @At(value = "INVOKE", remap = false, target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V", ordinal = 1)) - private void impl$modifyContextualData(final Consumer> instance, final Object packet, final ServerPlayer player) { - final ValueContainer contextualData = ((DataPerspective) this.entity).getDataPerception((DataPerspective) player); - - List> values = new ArrayList<>(); - for (final SynchedEntityData.DataValue dataValue : ((ClientboundSetEntityDataPacket) packet).packedItems()) { - if (dataValue.id() == EntityAccessor.accessor$DATA_CUSTOM_NAME().getId()) { - values.add(SynchedEntityData.DataValue.create( - EntityAccessor.accessor$DATA_CUSTOM_NAME(), SpongeAdventure.asVanillaOpt(contextualData.require(Keys.CUSTOM_NAME)))); - } else { - values.add(dataValue); - } - } - - instance.accept(new ClientboundSetEntityDataPacket(this.entity.getId(), values)); + private void impl$modifyContextualEntityData(final Consumer> instance, final Object packet, final ServerPlayer player) { + instance.accept(ContextualPacketUtil.createContextualPacket((ClientboundSetEntityDataPacket) packet, (ContextualData) this.entity, (DataPerspective) player)); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java index 3df4d677a79..af3a260ffaa 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java @@ -1342,6 +1342,11 @@ public void stopRiding() { return this.impl$contextualData; } + @Override + public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { + return this.impl$contextualData.getDataPerception(perspective); + } + @Override public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.impl$contextualData.createDataPerception(perspective); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java index f312ae2df02..c1f24c816bd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java @@ -217,6 +217,11 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge, PlayerTeamBri return this.impl$contextualData; } + @Override + public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { + return this.impl$contextualData.getDataPerception(perspective); + } + @Override public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.impl$contextualData.createDataPerception(perspective); From cf938134d39450db64a7d00d9f12ca5fb42af2ed Mon Sep 17 00:00:00 2001 From: aromaa Date: Tue, 23 Apr 2024 16:28:31 +0300 Subject: [PATCH 5/7] Update api ref --- SpongeAPI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpongeAPI b/SpongeAPI index 123754f46c3..22676ca8118 160000 --- a/SpongeAPI +++ b/SpongeAPI @@ -1 +1 @@ -Subproject commit 123754f46c374d4c111d66c6ecf7f2c6613fe058 +Subproject commit 22676ca81183bbb78c814e694416457954d1cedc From 8deccdfdb68db38ee489ae6893b131931710f830 Mon Sep 17 00:00:00 2001 From: aromaa Date: Thu, 25 Apr 2024 18:43:24 +0300 Subject: [PATCH 6/7] Support when team players change --- .../bridge/world/scores/PlayerTeamBridge.java | 8 ++ .../data/contextual/ContextualData.java | 5 +- .../contextual/ContextualDataDelegate.java | 72 +++++++++------ .../data/contextual/ContextualDataHolder.java | 83 +++++++++-------- .../data/contextual/ContextualDataOwner.java | 89 +++++++++++++++++++ .../contextual/ContextualDataProvider.java | 2 +- .../GenericDataPerspectiveResolver.java | 2 +- .../data/contextual/PerspectiveContainer.java | 59 ++++++++++-- .../contextual/util/ContextualPacketUtil.java | 2 +- .../provider/DataProviderRegistrator.java | 2 +- .../world/entity/EntityMixin_API.java | 2 +- .../world/scores/PlayerTeamMixin_API.java | 3 +- .../core/server/ServerScoreboardMixin.java | 52 +++++++++-- .../level/ChunkMap_TrackedEntityMixin.java | 2 +- .../mixin/core/world/entity/EntityMixin.java | 23 ++++- .../core/world/scores/PlayerTeamMixin.java | 55 ++++++++++-- 16 files changed, 364 insertions(+), 97 deletions(-) create mode 100644 src/main/java/org/spongepowered/common/data/contextual/ContextualDataOwner.java rename src/main/java/org/spongepowered/common/data/{ => contextual}/GenericDataPerspectiveResolver.java (97%) diff --git a/src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge.java b/src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge.java index d6cfa6c9b9a..625a5b0d802 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/scores/PlayerTeamBridge.java @@ -29,6 +29,8 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.minecraft.server.level.ServerPlayer; +import java.util.List; + public interface PlayerTeamBridge { Component bridge$getDisplayName(); @@ -50,4 +52,10 @@ public interface PlayerTeamBridge { Audience bridge$getTeamChannel(ServerPlayer player); Audience bridge$getNonTeamChannel(); + + void bridge$addPlayer(ServerPlayer player); + + void bridge$removePlayer(ServerPlayer player, boolean sendPackets); + + List bridge$getPlayers(); } diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java index cc787d6315e..7569271c18e 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualData.java @@ -30,9 +30,12 @@ public interface ContextualData { - @Nullable PerspectiveContainer getDataPerception(DataPerspective perspective); + @Nullable PerspectiveContainer dataPerception(DataPerspective perspective); PerspectiveContainer createDataPerception(DataPerspective perspective); + void linkContextualOwner(ContextualDataOwner owner); + void unlinkContextualOwner(ContextualDataOwner owner); + void broadcastToPerceives(Packet packet); } diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java index 59a506c170b..cc06609f46a 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataDelegate.java @@ -24,35 +24,22 @@ */ package org.spongepowered.common.data.contextual; -import com.google.common.collect.Maps; -import net.minecraft.network.protocol.Packet; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.DataPerspectiveResolver; +import org.spongepowered.api.data.Key; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.scoreboard.Team; import org.spongepowered.api.world.World; -import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.common.data.SpongeDataManager; -import java.util.Map; import java.util.Objects; -public final class ContextualDataDelegate implements ContextualData { - - private final DataPerspective perspective; - private final Map> perspectives; +public final class ContextualDataDelegate extends ContextualDataOwner { public ContextualDataDelegate(final DataPerspective perspective) { - this.perspective = perspective; - - this.perspectives = Maps.newHashMap(); - } - - @Override - public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { - return this.perspectives.get(perspective); + super(perspective); } @Override @@ -69,15 +56,6 @@ public ContextualDataDelegate(final DataPerspective perspective) { }); } - @Override - public void broadcastToPerceives(Packet packet) { - ((ContextualData) this.perspective).broadcastToPerceives(packet); - } - - public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { - return new ContextualDataProvider(this.createDataPerception(perspective), plugin); - } - private static abstract class AbstractPerspectiveContainer

extends PerspectiveContainer { protected AbstractPerspectiveContainer(final PerspectiveType perspectiveType, final ContextualDataDelegate holder, final P perspective) { @@ -85,13 +63,49 @@ protected AbstractPerspectiveContainer(final PerspectiveType perspectiveType, fi } @Override - protected void offer(final PerspectiveType perspectiveType, final DataPerspectiveResolver, E> resolver, final E value) { + protected final void offer(final PerspectiveType perspectiveType, final DataPerspectiveResolver, E> resolver, final E value) { if (Objects.equals(this.activeValues.put(resolver.key(), value), value)) { return; } - for (final DataPerspective perspective : this.holder.perspective.perceives()) { - ((ContextualData) perspective).createDataPerception(this.perspective).offer(PerspectiveType.TEAM, this.holder.perspective, resolver, value); + for (final DataPerspective perspective : this.holder.owner.perceives()) { + ((ContextualData) perspective).createDataPerception(this.perspective).offer(PerspectiveType.TEAM, this.holder.owner, resolver, value); + } + } + + @Override + protected void remove(PerspectiveType perspectiveType, DataPerspectiveResolver, ?> resolver) { + this.activeValues.remove(resolver.key()); + + for (final DataPerspective perspective : this.holder.owner.perceives()) { + final @Nullable PerspectiveContainer container = ((ContextualData) perspective).dataPerception(this.perspective); + if (container != null) { + container.remove(PerspectiveType.TEAM, this.holder.owner, resolver); + } + } + } + + @Override + protected void addPerspective(final DataPerspective perspective) { + final PerspectiveContainer container = ((ContextualData) perspective).createDataPerception(this.perspective); + this.activeValues.forEach((k, v) -> { + final DataPerspectiveResolver, Object> resolver = + Objects.requireNonNull(SpongeDataManager.getDataPerspectiveResolverRegistry().get((Key) k)); + + container.offer(PerspectiveType.TEAM, this.holder.owner, resolver, v); + }); + } + + @Override + protected void removePerspective(final DataPerspective perspective) { + final @Nullable PerspectiveContainer container = ((ContextualData) perspective).dataPerception(this.perspective); + if (container != null) { + this.activeValues.forEach((k, v) -> { + final DataPerspectiveResolver, ?> resolver = + Objects.requireNonNull(SpongeDataManager.getDataPerspectiveResolverRegistry().get((Key) k)); + + container.remove(PerspectiveType.TEAM, this.holder.owner, resolver); + }); } } } diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java index 22200302657..ab2301f80af 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataHolder.java @@ -25,10 +25,7 @@ package org.spongepowered.common.data.contextual; import com.google.common.collect.Maps; -import net.minecraft.network.protocol.Packet; -import net.minecraft.server.level.ServerPlayer; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.DataPerspectiveResolver; import org.spongepowered.api.data.Key; @@ -37,7 +34,6 @@ import org.spongepowered.api.scoreboard.Team; import org.spongepowered.api.world.World; import org.spongepowered.common.util.CopyHelper; -import org.spongepowered.plugin.PluginContainer; import java.util.EnumMap; import java.util.Map; @@ -45,21 +41,10 @@ import java.util.Optional; @SuppressWarnings("unchecked") -public final class ContextualDataHolder implements ContextualData { +public final class ContextualDataHolder extends ContextualDataOwner { - private final DataHolder dataHolder; - - private final Map> perspectives; - - public ContextualDataHolder(final DataHolder dataHolder) { - this.dataHolder = dataHolder; - - this.perspectives = Maps.newHashMap(); - } - - @Override - public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { - return this.perspectives.get(perspective); + public ContextualDataHolder(final Entity entity) { + super(entity); } @Override @@ -76,17 +61,6 @@ public ContextualDataHolder(final DataHolder dataHolder) { }); } - @Override - public void broadcastToPerceives(final Packet packet) { - if (this.dataHolder instanceof final ServerPlayer player) { - player.connection.send(packet); - } - } - - public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { - return new ContextualDataProvider(this.createDataPerception(perspective), plugin); - } - private static abstract class AbstractPerspectiveContainer

extends PerspectiveContainer { protected AbstractPerspectiveContainer(PerspectiveType perspectiveType, ContextualDataHolder holder, P perspective) { @@ -94,29 +68,39 @@ protected AbstractPerspectiveContainer(PerspectiveType perspectiveType, Contextu } @Override - public Optional get(final Key> key) { + public final Optional get(final Key> key) { Objects.requireNonNull(key, "key"); final @Nullable E value = (E) this.activeValues.get(key); if (value == null) { - return this.holder.dataHolder.get(key); + return this.holder.owner.get(key); } return Optional.ofNullable(CopyHelper.copy(value)); } @Override - public > Optional getValue(final Key key) { + public final > Optional getValue(final Key key) { Objects.requireNonNull(key, "key"); final @Nullable E value = (E) this.activeValues.get(key); if (value == null) { - return this.holder.dataHolder.getValue(key); + return this.holder.owner.getValue(key); } return Optional.of(Value.genericMutableOf(key, CopyHelper.copy(value))); } + + @Override + protected void addPerspective(final DataPerspective perspective) { + throw new UnsupportedOperationException(); + } + + @Override + protected void removePerspective(final DataPerspective perspective) { + throw new UnsupportedOperationException(); + } } private static final class EntityPerspectiveContainer extends AbstractPerspectiveContainer { - private final Map, EnumMap> perspectiveValues; + private final Map, EnumMap> perspectiveValues; private EntityPerspectiveContainer(final ContextualDataHolder holder, final Entity entity) { super(PerspectiveType.ENTITY, holder, entity); @@ -135,6 +119,23 @@ protected void offer(final PerspectiveType perspectiveType, final DataPerspe this.updatePerspective(values, resolver); } + @Override + protected void remove(final PerspectiveType perspectiveType, final DataPerspectiveResolver, ?> resolver) { + final @Nullable EnumMap values = this.perspectiveValues.get(resolver.key()); + if (values == null) { + return; + } + + values.remove(perspectiveType); + if (values.isEmpty()) { + this.perspectiveValues.remove(resolver.key()); + ((DataPerspectiveResolver) resolver).apply(this.holder.owner, this.perspective, this.holder.owner.require((Key) resolver.key())); + return; + } + + this.updatePerspective(values, (DataPerspectiveResolver) resolver); + } + private void updatePerspective(final EnumMap values, final DataPerspectiveResolver, E> resolver) { for (final PerspectiveType type : PerspectiveType.values()) { final @Nullable E value = values.get(type); @@ -143,7 +144,7 @@ private void updatePerspective(final EnumMap values, fin } if (!Objects.equals(this.activeValues.put(resolver.key(), value), value)) { - resolver.apply(this.holder.dataHolder, this.perspective, value); + resolver.apply(this.holder.owner, this.perspective, value); } return; @@ -167,6 +168,18 @@ protected void offer(final PerspectiveType perspectiveType, final DataPerspe this.holder.createDataPerception(perspective).offer(perspectiveType, this.perspective, resolver, value); } } + + @Override + protected void remove(final PerspectiveType perspectiveType, final DataPerspectiveResolver, ?> resolver) { + this.activeValues.remove(resolver.key()); + + for (final DataPerspective perspective : this.perspective.perceives()) { + final @Nullable PerspectiveContainer container = this.holder.dataPerception(perspective); + if (container != null) { + container.remove(perspectiveType, this.perspective, resolver); + } + } + } } private static final class TeamPerspectiveContainer extends NonEntityContainer { diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataOwner.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataOwner.java new file mode 100644 index 00000000000..0e31981a64f --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataOwner.java @@ -0,0 +1,89 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.contextual; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataHolder; +import org.spongepowered.api.data.DataPerspective; +import org.spongepowered.plugin.PluginContainer; + +import java.io.Closeable; +import java.util.Map; +import java.util.Set; + +public abstract class ContextualDataOwner implements Closeable { + + protected final T owner; + + protected final Map> perspectives; + private final Set> links; + + ContextualDataOwner(final T owner) { + this.owner = owner; + + this.perspectives = Maps.newHashMap(); + this.links = Sets.newHashSet(); + } + + public final @Nullable PerspectiveContainer dataPerception(final DataPerspective perspective) { + return this.perspectives.get(perspective); + } + + //TODO: Abstract method on ContextualData? + public abstract PerspectiveContainer createDataPerception(final DataPerspective perspective); + + public final DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) { + return new ContextualDataProvider(this.createDataPerception(perspective), plugin); + } + + public final void linkContextualOwner(final ContextualDataOwner owner) { + this.links.add(owner); + } + + public final void unlinkContextualOwner(final ContextualDataOwner owner) { + this.links.remove(owner); + } + + public void perceiveAdded(final DataPerspective perspective) { + for (final PerspectiveContainer container : this.perspectives.values()) { + container.addPerspective(perspective); + } + } + + public void perceiveRemoved(final DataPerspective perspective) { + for (final PerspectiveContainer container : this.perspectives.values()) { + container.removePerspective(perspective); + } + } + + @Override + public final void close() { + this.perspectives.values().forEach(PerspectiveContainer::close); + + this.links.forEach(o -> o.perspectives.remove(this.owner)); + } +} diff --git a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataProvider.java b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataProvider.java index 685dedec43b..ca38c414e2c 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/ContextualDataProvider.java +++ b/src/main/java/org/spongepowered/common/data/contextual/ContextualDataProvider.java @@ -129,7 +129,7 @@ public DataTransactionResult tryOffer(Key> key, E value) @Override public DataTransactionResult remove(Key key) { - return null; + return this.container.remove(this.plugin, key); } @Override diff --git a/src/main/java/org/spongepowered/common/data/GenericDataPerspectiveResolver.java b/src/main/java/org/spongepowered/common/data/contextual/GenericDataPerspectiveResolver.java similarity index 97% rename from src/main/java/org/spongepowered/common/data/GenericDataPerspectiveResolver.java rename to src/main/java/org/spongepowered/common/data/contextual/GenericDataPerspectiveResolver.java index 561be71aa30..d7e4cbf8e75 100644 --- a/src/main/java/org/spongepowered/common/data/GenericDataPerspectiveResolver.java +++ b/src/main/java/org/spongepowered/common/data/contextual/GenericDataPerspectiveResolver.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.data; +package org.spongepowered.common.data.contextual; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataPerspective; diff --git a/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java index c8142588236..abb58029c6e 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java +++ b/src/main/java/org/spongepowered/common/data/contextual/PerspectiveContainer.java @@ -38,6 +38,7 @@ import org.spongepowered.common.util.CopyHelper; import org.spongepowered.plugin.PluginContainer; +import java.io.Closeable; import java.util.Collections; import java.util.Map; import java.util.Objects; @@ -45,13 +46,13 @@ import java.util.Set; @SuppressWarnings("unchecked") -public abstract class PerspectiveContainer implements ValueContainer { +public abstract class PerspectiveContainer, P extends DataPerspective> implements ValueContainer, Closeable { private final PerspectiveType perspectiveType; protected final H holder; protected final P perspective; - private final Map, Map> valuesByOwner; + private final Map, Map> valuesByOwner; protected final Map, Object> activeValues; private long entityDataFlags; @@ -63,9 +64,11 @@ protected PerspectiveContainer(final PerspectiveType perspectiveType, final H ho this.valuesByOwner = Maps.newHashMap(); this.activeValues = Maps.newHashMap(); + + ((ContextualData) perspective).linkContextualOwner(this.holder); } - public long entityDataFlags() { + public final long entityDataFlags() { return this.entityDataFlags; } @@ -85,16 +88,57 @@ final DataTransactionResult offer(final PerspectiveType perspectiveType, fin } if (resolver.key() == (Key)Keys.CUSTOM_NAME) { - entityDataFlags |= 1L << EntityAccessor.accessor$DATA_CUSTOM_NAME().getId(); + this.entityDataFlags |= 1L << EntityAccessor.accessor$DATA_CUSTOM_NAME().getId(); } - final E mergedValue = resolver.merge(valueMap.values()); + final E mergedValue = valueMap.size() == 1 ? value : resolver.merge(valueMap.values()); this.offer(perspectiveType, resolver, mergedValue); return DataTransactionResult.successResult(Value.immutableOf(resolver.key(), mergedValue)); } protected abstract void offer(final PerspectiveType perspectiveType, final DataPerspectiveResolver, E> resolver, final E value); + final DataTransactionResult remove(final PluginContainer pluginContainer, final Key key) { + final @Nullable DataPerspectiveResolver, ?> resolver = SpongeDataManager.getDataPerspectiveResolverRegistry().get((Key) key); + if (resolver == null) { + return DataTransactionResult.failNoData(); + } + + return this.remove(this.perspectiveType, pluginContainer, resolver); + } + + final DataTransactionResult remove(final PerspectiveType perspectiveType, final Object owner, final DataPerspectiveResolver, ?> resolver) { + final @Nullable Map valueMap = (Map) this.valuesByOwner.get(resolver.key()); + if (valueMap == null) { + return DataTransactionResult.failNoData(); + } + + final @Nullable Object value = valueMap.remove(owner); + if (value == null) { + return DataTransactionResult.failNoData(); + } + + if (resolver.key() == (Key)Keys.CUSTOM_NAME) { + this.entityDataFlags &= ~(1L << EntityAccessor.accessor$DATA_CUSTOM_NAME().getId()); + } + + if (valueMap.isEmpty()) { + this.valuesByOwner.remove(resolver.key()); + this.activeValues.remove(resolver.key()); + this.remove(perspectiveType, resolver); + return DataTransactionResult.successNoData(); + } + + final Object mergedValue = valueMap.size() == 1 ? valueMap.values().iterator().next() : ((DataPerspectiveResolver) resolver).merge(valueMap.values()); + this.offer(perspectiveType, owner, (DataPerspectiveResolver) resolver, mergedValue); + return DataTransactionResult.successNoData(); + } + + protected abstract void remove(final PerspectiveType perspectiveType, final DataPerspectiveResolver, ?> resolver); + + protected abstract void addPerspective(final DataPerspective perspective); + protected abstract void removePerspective(final DataPerspective perspective); + @Override public Optional get(final Key> key) { Objects.requireNonNull(key, "key"); @@ -123,4 +167,9 @@ public Set> getKeys() { public Set> getValues() { return Collections.emptySet(); } + + @Override + public final void close() { + ((ContextualData) this.perspective).unlinkContextualOwner(this.holder); + } } diff --git a/src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java b/src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java index bb77704c7ec..3b8855a3620 100644 --- a/src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java +++ b/src/main/java/org/spongepowered/common/data/contextual/util/ContextualPacketUtil.java @@ -42,7 +42,7 @@ public final class ContextualPacketUtil { public static ClientboundSetEntityDataPacket createContextualPacket( final ClientboundSetEntityDataPacket original, final ContextualData context, final DataPerspective perspective) { - final @Nullable PerspectiveContainer contextualData = context.getDataPerception(perspective); + final @Nullable PerspectiveContainer contextualData = context.dataPerception(perspective); if (contextualData == null) { return original; } diff --git a/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java b/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java index fe4de14fe34..340dd77e9f4 100644 --- a/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java +++ b/src/main/java/org/spongepowered/common/data/provider/DataProviderRegistrator.java @@ -47,10 +47,10 @@ import org.spongepowered.api.registry.DefaultedRegistryReference; import org.spongepowered.api.util.OptBool; import org.spongepowered.common.bridge.data.DataContainerHolder; -import org.spongepowered.common.data.GenericDataPerspectiveResolver; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.data.SpongeDataRegistration; import org.spongepowered.common.data.SpongeDataRegistrationBuilder; +import org.spongepowered.common.data.contextual.GenericDataPerspectiveResolver; import org.spongepowered.common.data.persistence.datastore.SpongeDataStoreBuilder; import org.spongepowered.common.function.TriConsumer; import org.spongepowered.common.util.CopyHelper; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java index ea163cba164..ca0bae960a8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java @@ -405,7 +405,7 @@ public Iterable perceives() { @Override public ValueContainer getDataPerception(final DataPerspective perspective) { - final @Nullable ValueContainer container = ((EntityBridge_Contextual) this).bridge$contextualData().getDataPerception(perspective); + final @Nullable ValueContainer container = ((EntityBridge_Contextual) this).bridge$contextualData().dataPerception(perspective); if (container != null) { return container; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java index 7e9f2e502e5..4e8c404b68a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/scores/PlayerTeamMixin_API.java @@ -30,7 +30,6 @@ import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.Scoreboard; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.Sponge; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataPerspective; import org.spongepowered.api.data.value.ValueContainer; @@ -229,7 +228,7 @@ public boolean unregister() { @Override public Iterable perceives() { - return Sponge.server().onlinePlayers().stream().filter(p -> this.members().contains(p.teamRepresentation())).collect(Collectors.toList()); + return (Iterable) (Object) ((PlayerTeamBridge) this).bridge$getPlayers(); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerScoreboardMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerScoreboardMixin.java index b9dc29ee5d9..49a9052834a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerScoreboardMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerScoreboardMixin.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.core.server; +import com.google.common.collect.Maps; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; @@ -36,6 +37,7 @@ import net.minecraft.world.scores.ScoreHolder; import net.minecraft.world.scores.Scoreboard; import net.minecraft.world.scores.criteria.ObjectiveCriteria; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.scoreboard.Score; import org.spongepowered.api.scoreboard.Team; import org.spongepowered.api.scoreboard.criteria.Criterion; @@ -46,19 +48,22 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.accessor.world.scores.PlayerTeamAccessor; import org.spongepowered.common.accessor.world.scores.ScoreboardAccessor; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.server.ServerScoreboardBridge; import org.spongepowered.common.bridge.world.scores.ObjectiveBridge; +import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge; +import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge_Contextual; import org.spongepowered.common.scoreboard.SpongeObjective; import org.spongepowered.common.scoreboard.SpongeScore; import org.spongepowered.common.util.Constants; -import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -68,7 +73,7 @@ public abstract class ServerScoreboardMixin extends Scoreboard implements ServerScoreboardBridge { - private final List impl$players = new ArrayList<>(); + private final Map impl$players = Maps.newHashMap(); private boolean impl$apiCall; @@ -178,14 +183,18 @@ public abstract class ServerScoreboardMixin extends Scoreboard implements Server @Override public void bridge$sendToPlayers(final Packet packet) { - for (final ServerPlayer player: this.impl$players) { + for (final ServerPlayer player: this.impl$players.values()) { player.connection.send(packet); } } @Override public void bridge$addPlayer(final ServerPlayer player, final boolean sendPackets) { - this.impl$players.add(player); + this.impl$players.put(player.getScoreboardName(), player); + final @Nullable PlayerTeam playerTeam = this.getPlayersTeam(player.getScoreboardName()); + if (playerTeam != null) { + ((PlayerTeamBridge) playerTeam).bridge$addPlayer(player); + } if (sendPackets) { for (final PlayerTeam team : this.getPlayerTeams()) { player.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true)); @@ -213,7 +222,11 @@ public abstract class ServerScoreboardMixin extends Scoreboard implements Server @Override public void bridge$removePlayer(final ServerPlayer player, final boolean sendPackets) { - this.impl$players.remove(player); + this.impl$players.remove(player.getScoreboardName()); + final @Nullable PlayerTeam playerTeam = this.getPlayersTeam(player.getScoreboardName()); + if (playerTeam != null) { + ((PlayerTeamBridge) playerTeam).bridge$removePlayer(player, sendPackets); + } if (sendPackets) { this.impl$removeScoreboard(player); } @@ -309,13 +322,13 @@ private boolean onUpdateScoreValue(final Set set, final Object object) { @Redirect(method = "startTrackingObjective", at = @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;", ordinal = 0, remap = false)) private Iterator impl$useOurScoreboardForPlayers(final List list) { - return this.impl$players.iterator(); + return this.impl$players.values().iterator(); } @Redirect(method = "stopTrackingObjective", at = @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;", ordinal = 0, remap = false)) private Iterator impl$useOurScoreboardForPlayersOnRemoval(final List list) { - return this.impl$players.iterator(); + return this.impl$players.values().iterator(); } private void impl$removeScoreboard(final ServerPlayer player) { @@ -344,4 +357,29 @@ private boolean onUpdateScoreValue(final Set set, final Object object) { ((SpongeScore) spongeScore).unregister(mcObjective); this.impl$apiCall = false; } + + @Inject(method = "addPlayerToTeam", at = @At("RETURN")) + private void impl$onAddPlayerToTeam(final String scoreboardName, final PlayerTeam team, final CallbackInfoReturnable cir) { + if (!cir.getReturnValue()) { + return; + } + + final @Nullable ServerPlayer player = this.impl$players.get(scoreboardName); + if (player != null) { + ((PlayerTeamBridge) team).bridge$addPlayer(player); + } + } + + @Inject(method = "removePlayerFromTeam", at = @At("TAIL")) + private void impl$onPlayerRemovedFromTeam(final String scoreboardName, final PlayerTeam team, final CallbackInfo ci) { + final @Nullable ServerPlayer player = this.impl$players.get(scoreboardName); + if (player != null) { + ((PlayerTeamBridge) team).bridge$removePlayer(player, true); + } + } + + @Inject(method = "onTeamRemoved", at = @At("TAIL")) + private void impl$onTeamRemoved(final PlayerTeam team, final CallbackInfo ci) { + ((PlayerTeamBridge_Contextual) team).bridge$contextualData().close(); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java index 196ad8013be..b8001c474c9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java @@ -69,7 +69,7 @@ public abstract class ChunkMap_TrackedEntityMixin { @Redirect(method = "broadcast(Lnet/minecraft/network/protocol/Packet;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerConnection;send(Lnet/minecraft/network/protocol/Packet;)V")) private void impl$sendQueuedHumanPackets(final ServerPlayerConnection serverPlayNetHandler, final Packet packetIn) { - final @Nullable PerspectiveContainer contextualData = ((ContextualData) this.entity).getDataPerception((DataPerspective) serverPlayNetHandler.getPlayer()); + final @Nullable PerspectiveContainer contextualData = ((ContextualData) this.entity).dataPerception((DataPerspective) serverPlayNetHandler.getPlayer()); final ValueContainer dataPerception = contextualData != null ? contextualData : (ValueContainer) this.entity; if (dataPerception.require(Keys.VANISH_STATE).invisible()) { return; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java index af3a260ffaa..a0310495242 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java @@ -119,6 +119,7 @@ import org.spongepowered.common.data.DataUtil; import org.spongepowered.common.data.contextual.ContextualData; import org.spongepowered.common.data.contextual.ContextualDataHolder; +import org.spongepowered.common.data.contextual.ContextualDataOwner; import org.spongepowered.common.data.contextual.PerspectiveContainer; import org.spongepowered.common.data.provider.nbt.NBTDataType; import org.spongepowered.common.data.provider.nbt.NBTDataTypes; @@ -262,7 +263,7 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, // Structure: tileNbt - ForgeData - SpongeData - customdata private CompoundTag impl$customDataCompound; - private final ContextualDataHolder impl$contextualData = new ContextualDataHolder((DataHolder) this); + private final ContextualDataHolder impl$contextualData = new ContextualDataHolder((org.spongepowered.api.entity.Entity) this); @Override public boolean bridge$isConstructing() { @@ -1343,8 +1344,8 @@ public void stopRiding() { } @Override - public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { - return this.impl$contextualData.getDataPerception(perspective); + public @Nullable PerspectiveContainer dataPerception(final DataPerspective perspective) { + return this.impl$contextualData.dataPerception(perspective); } @Override @@ -1352,10 +1353,26 @@ public void stopRiding() { return this.impl$contextualData.createDataPerception(perspective); } + @Override + public void linkContextualOwner(final ContextualDataOwner owner) { + this.impl$contextualData.linkContextualOwner(owner); + } + + @Override + public void unlinkContextualOwner(final ContextualDataOwner owner) { + this.impl$contextualData.unlinkContextualOwner(owner); + } + @Override public void broadcastToPerceives(final Packet packet) { if ((Object) this instanceof ServerPlayer player) { player.connection.send(packet); } } + + //TODO: We lose the contextual data when the entity changes dimensions + @Inject(method = "setRemoved", at = @At("TAIL")) + private void impl$clearContextualData(final Entity.RemovalReason $$0, final CallbackInfo ci) { + this.impl$contextualData.close(); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java index c1f24c816bd..d4f48035174 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java @@ -29,6 +29,7 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.minecraft.ChatFormatting; +import net.minecraft.network.protocol.Packet; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.Scoreboard; @@ -50,10 +51,13 @@ import org.spongepowered.common.bridge.world.scores.PlayerTeamBridge_Contextual; import org.spongepowered.common.data.contextual.ContextualData; import org.spongepowered.common.data.contextual.ContextualDataDelegate; +import org.spongepowered.common.data.contextual.ContextualDataOwner; import org.spongepowered.common.data.contextual.PerspectiveContainer; +import java.util.ArrayList; import java.util.Collection; -import java.util.Optional; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @Mixin(PlayerTeam.class) @@ -68,10 +72,12 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge, PlayerTeamBri @Shadow public abstract Collection getPlayers(); // @formatter:on + @Shadow @Final private Set players; private @MonotonicNonNull Component bridge$displayName; private @MonotonicNonNull Component bridge$prefix; private @MonotonicNonNull Component bridge$suffix; private @MonotonicNonNull NamedTextColor bridge$color; + private final List impl$players = new ArrayList<>(); private final ContextualDataDelegate impl$contextualData = new ContextualDataDelegate((DataPerspective) this); @@ -197,12 +203,7 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge, PlayerTeamBri @SuppressWarnings("EqualsBetweenInconvertibleTypes") @Override public Audience bridge$getTeamChannel(final ServerPlayer player) { - return Audience.audience(this.getPlayers().stream() - .map(name -> Sponge.game().server().player(name)) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(member -> member != player) - .collect(Collectors.toSet())); + return Audience.audience((Iterable) (Object) this.impl$players); } @Override @@ -218,12 +219,48 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge, PlayerTeamBri } @Override - public @Nullable PerspectiveContainer getDataPerception(final DataPerspective perspective) { - return this.impl$contextualData.getDataPerception(perspective); + public @Nullable PerspectiveContainer dataPerception(final DataPerspective perspective) { + return this.impl$contextualData.dataPerception(perspective); } @Override public PerspectiveContainer createDataPerception(final DataPerspective perspective) { return this.impl$contextualData.createDataPerception(perspective); } + + @Override + public void linkContextualOwner(final ContextualDataOwner owner) { + this.impl$contextualData.linkContextualOwner(owner); + } + + @Override + public void unlinkContextualOwner(final ContextualDataOwner owner) { + this.impl$contextualData.unlinkContextualOwner(owner); + } + + @Override + public void broadcastToPerceives(final Packet packet) { + for (final ServerPlayer player : this.impl$players) { + player.connection.send(packet); + } + } + + @Override + public void bridge$addPlayer(final ServerPlayer player) { + this.impl$players.add(player); + this.impl$contextualData.perceiveAdded((DataPerspective) player); + } + + @Override + public void bridge$removePlayer(final ServerPlayer player, final boolean sendPackets) { + this.impl$players.remove(player); + if (sendPackets) { + this.impl$contextualData.perceiveRemoved((DataPerspective) player); + } + } + + @Override + public List bridge$getPlayers() { + return this.impl$players; + } } From 1f11dfc5e2e7aa4d1c0a3c2ab6985718c755860f Mon Sep 17 00:00:00 2001 From: aromaa Date: Thu, 25 Apr 2024 18:47:25 +0300 Subject: [PATCH 7/7] Remove accidentally added shadow --- .../common/mixin/core/world/scores/PlayerTeamMixin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java index d4f48035174..c49327d5c2b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java @@ -72,7 +72,6 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge, PlayerTeamBri @Shadow public abstract Collection getPlayers(); // @formatter:on - @Shadow @Final private Set players; private @MonotonicNonNull Component bridge$displayName; private @MonotonicNonNull Component bridge$prefix; private @MonotonicNonNull Component bridge$suffix;