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); + } }