Skip to content

Commit e349fe8

Browse files
committed
Initial contextual data work
1 parent 5515ac3 commit e349fe8

File tree

17 files changed

+810
-40
lines changed

17 files changed

+810
-40
lines changed

src/main/java/org/spongepowered/common/bridge/data/VanishableBridge.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package org.spongepowered.common.bridge.data;
2626

27+
import org.spongepowered.api.data.DataPerspective;
2728
import org.spongepowered.api.effect.VanishState;
2829

2930
public interface VanishableBridge {
@@ -32,6 +33,8 @@ public interface VanishableBridge {
3233

3334
void bridge$vanishState(VanishState state);
3435

36+
void bridge$vanishState(VanishState state, DataPerspective perspective);
37+
3538
boolean bridge$isInvisible();
3639

3740
void bridge$setInvisible(boolean invisible);
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/*
2+
* This file is part of Sponge, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.common.data;
26+
27+
import com.google.common.collect.Maps;
28+
import org.checkerframework.checker.nullness.qual.Nullable;
29+
import org.spongepowered.api.data.DataHolder;
30+
import org.spongepowered.api.data.DataPerspective;
31+
import org.spongepowered.api.data.DataPerspectiveResolver;
32+
import org.spongepowered.api.data.DataTransactionResult;
33+
import org.spongepowered.api.data.Key;
34+
import org.spongepowered.api.data.value.Value;
35+
import org.spongepowered.api.data.value.ValueContainer;
36+
import org.spongepowered.api.entity.Entity;
37+
import org.spongepowered.api.scoreboard.Team;
38+
import org.spongepowered.api.world.World;
39+
import org.spongepowered.common.util.CopyHelper;
40+
import org.spongepowered.plugin.PluginContainer;
41+
42+
import java.util.EnumMap;
43+
import java.util.Map;
44+
import java.util.Objects;
45+
import java.util.Optional;
46+
import java.util.Set;
47+
48+
@SuppressWarnings("unchecked")
49+
public final class ContextualDataHolder {
50+
51+
private final DataHolder dataHolder;
52+
private final Map<DataPerspective, PerspectiveContainer<?>> perspectives;
53+
54+
public ContextualDataHolder(final DataHolder dataHolder) {
55+
this.dataHolder = dataHolder;
56+
57+
this.perspectives = Maps.newHashMap();
58+
}
59+
60+
private PerspectiveContainer<?> createDataPerception(final DataPerspective perspective) {
61+
return this.perspectives.computeIfAbsent(perspective, p -> {
62+
if (p instanceof final Entity entity) {
63+
return new EntityPerspectiveContainer(entity);
64+
} else if (p instanceof final Team team) {
65+
return new TeamPerspectiveContainer(team);
66+
} else if (p instanceof final World<?, ?> world) {
67+
return new WorldPerspectiveContainer(world);
68+
}
69+
throw new UnsupportedOperationException();
70+
});
71+
}
72+
73+
public DataHolder.Mutable createDataPerception(final PluginContainer plugin, final DataPerspective perspective) {
74+
return new ContextualDataHolderProvider(plugin, this.createDataPerception(perspective));
75+
}
76+
77+
public @Nullable ValueContainer get(final DataPerspective perspective) {
78+
return this.perspectives.get(perspective);
79+
}
80+
81+
abstract class PerspectiveContainer<T extends DataPerspective> implements ValueContainer {
82+
83+
protected final T perspective;
84+
85+
private final Map<Key<?>, Map<PluginContainer, Object>> pluginValues;
86+
protected final Map<Key<?>, Object> activeValues;
87+
88+
protected PerspectiveContainer(final T perspective) {
89+
this.perspective = perspective;
90+
91+
this.pluginValues = Maps.newHashMap();
92+
this.activeValues = Maps.newHashMap();
93+
}
94+
95+
<E> DataTransactionResult offer(final PluginContainer pluginContainer, final Key<? extends Value<E>> key, final E value) {
96+
final @Nullable DataPerspectiveResolver<Value<E>, E> resolver = SpongeDataManager.getDataPerspectiveResolverRegistry().get(key);
97+
if (resolver == null) {
98+
return DataTransactionResult.failResult(Value.immutableOf(key, value));
99+
}
100+
101+
final Map<PluginContainer, E> map = (Map<PluginContainer, E>) this.pluginValues.computeIfAbsent(key, k -> Maps.newLinkedHashMap());
102+
if (Objects.equals(value, map.put(pluginContainer, value))) {
103+
return DataTransactionResult.successResult(Value.immutableOf(key, value));
104+
}
105+
106+
final E mergedValue = resolver.merge(map.values());
107+
this.offer(key, resolver, mergedValue);
108+
return DataTransactionResult.successResult(Value.immutableOf(key, mergedValue));
109+
}
110+
111+
protected abstract <E> void offer(final Key<? extends Value<E>> key, final DataPerspectiveResolver<Value<E>, E> resolver, final E value);
112+
113+
@Override
114+
public <E> Optional<E> get(final Key<? extends Value<E>> key) {
115+
Objects.requireNonNull(key, "key");
116+
final @Nullable E value = (E) this.activeValues.get(key);
117+
if (value == null) {
118+
return ContextualDataHolder.this.dataHolder.get(key);
119+
}
120+
return Optional.of(CopyHelper.copy(value));
121+
}
122+
123+
@Override
124+
public <E, V extends Value<E>> Optional<V> getValue(final Key<V> key) {
125+
Objects.requireNonNull(key, "key");
126+
final @Nullable E value = (E) this.activeValues.get(key);
127+
if (value == null) {
128+
return ContextualDataHolder.this.dataHolder.getValue(key);
129+
}
130+
return Optional.of(Value.genericMutableOf(key, CopyHelper.copy(value)));
131+
}
132+
133+
@Override
134+
public boolean supports(Key<?> key) {
135+
return ContextualDataHolder.this.dataHolder.supports(key);
136+
}
137+
138+
@Override
139+
public Set<Key<?>> getKeys() {
140+
return ContextualDataHolder.this.dataHolder.getKeys();
141+
}
142+
143+
@Override
144+
public Set<Value.Immutable<?>> getValues() {
145+
return ContextualDataHolder.this.dataHolder.getValues();
146+
}
147+
}
148+
149+
private abstract class NonEntityContainer<T extends DataPerspective> extends PerspectiveContainer<T> {
150+
151+
protected NonEntityContainer(T perspective) {
152+
super(perspective);
153+
}
154+
155+
protected abstract PerspectiveType type();
156+
157+
protected <E> void offer(final Key<? extends Value<E>> key, final DataPerspectiveResolver<Value<E>, E> resolver, final E value) {
158+
if (Objects.equals(this.activeValues.put(key, value), value)) {
159+
return;
160+
}
161+
162+
for (final DataPerspective perspective : this.perspective.perceives()) {
163+
if (!(perspective instanceof Entity entity)) {
164+
continue;
165+
}
166+
167+
final EntityPerspectiveContainer entityContainer = (EntityPerspectiveContainer) ContextualDataHolder.this.createDataPerception(entity);
168+
entityContainer.offer(this.type(), key, resolver, value);
169+
}
170+
}
171+
}
172+
173+
private final class TeamPerspectiveContainer extends NonEntityContainer<Team> {
174+
175+
public TeamPerspectiveContainer(final Team team) {
176+
super(team);
177+
}
178+
179+
@Override
180+
protected PerspectiveType type() {
181+
return PerspectiveType.TEAM;
182+
}
183+
}
184+
185+
private final class WorldPerspectiveContainer extends NonEntityContainer<World<?, ?>> {
186+
187+
public WorldPerspectiveContainer(final World<?, ?> world) {
188+
super(world);
189+
}
190+
191+
@Override
192+
protected PerspectiveType type() {
193+
return PerspectiveType.WORLD;
194+
}
195+
}
196+
197+
private final class EntityPerspectiveContainer extends PerspectiveContainer<Entity> {
198+
199+
private final Map<Key<?>, EnumMap<PerspectiveType, Object>> perspectiveValues;
200+
201+
public EntityPerspectiveContainer(final Entity entity) {
202+
super(entity);
203+
204+
this.perspectiveValues = Maps.newHashMap();
205+
}
206+
207+
private <E> EnumMap<PerspectiveType, E> getValues(final Key<? extends Value<E>> key) {
208+
return (EnumMap<PerspectiveType, E>) this.perspectiveValues.computeIfAbsent(key, k -> Maps.newEnumMap(PerspectiveType.class));
209+
}
210+
211+
@Override
212+
protected <E> void offer(final Key<? extends Value<E>> key, final DataPerspectiveResolver<Value<E>, E> resolver, final E value) {
213+
this.getValues(key).put(PerspectiveType.ENTITY, value);
214+
if (!Objects.equals(value, this.activeValues.put(key, value))) {
215+
resolver.apply(ContextualDataHolder.this.dataHolder, this.perspective, value);
216+
}
217+
}
218+
219+
<E> void offer(final PerspectiveType type, final Key<? extends Value<E>> key, final DataPerspectiveResolver<Value<E>, E> resolver, final E value) {
220+
final EnumMap<PerspectiveType, E> values = this.getValues(key);
221+
values.put(type, value);
222+
this.updatePerspective(key, values, resolver);
223+
}
224+
225+
private <E> void updatePerspective(final Key<? extends Value<E>> key, final EnumMap<PerspectiveType, E> values, final DataPerspectiveResolver<Value<E>, E> resolver) {
226+
for (final PerspectiveType type : PerspectiveType.values()) {
227+
final @Nullable E value = values.get(type);
228+
if (value == null) {
229+
continue;
230+
}
231+
232+
if (!Objects.equals(this.activeValues.put(key, value), value)) {
233+
resolver.apply(ContextualDataHolder.this.dataHolder, this.perspective, value);
234+
}
235+
236+
return;
237+
}
238+
}
239+
}
240+
241+
private enum PerspectiveType {
242+
ENTITY,
243+
TEAM,
244+
WORLD
245+
}
246+
}

0 commit comments

Comments
 (0)