-
-
Save gabizou/4219d260b00fe8624fa2 to your computer and use it in GitHub Desktop.
package org.spongepowered.cookbook.plugin; | |
import static com.google.common.base.Preconditions.checkNotNull; | |
import com.google.common.base.Objects; | |
import org.spongepowered.api.Sponge; | |
import org.spongepowered.api.data.DataContainer; | |
import org.spongepowered.api.data.DataHolder; | |
import org.spongepowered.api.data.manipulator.mutable.common.AbstractData; | |
import org.spongepowered.api.data.merge.MergeFunction; | |
import org.spongepowered.api.data.value.mutable.MutableBoundedValue; | |
import org.spongepowered.api.data.value.mutable.Value; | |
import java.util.Optional; | |
public class FakeData extends AbstractData<FakeData, ImmutableFakeData> { | |
private int level; | |
private String name; | |
private boolean enabled; | |
public FakeData() { | |
this(0, "", false); | |
} | |
public FakeData(int level, String name, boolean enabled) { | |
this.level = level; | |
this.name = name; | |
this.enabled = enabled; | |
} | |
public Value<Boolean> fakeBool() { | |
return Sponge.getRegistry().getValueFactory().createValue(FakeKeys.FAKE_BOOL, false, this.enabled); | |
} | |
public Value<String> fakeString() { | |
return Sponge.getRegistry().getValueFactory().createValue(FakeKeys.FAKE_STRING, "", this.name); | |
} | |
public MutableBoundedValue<Integer> fakeInt() { | |
return Sponge.getRegistry().getValueFactory().createBoundedValueBuilder(FakeKeys.FAKE_BOUNDED) | |
.defaultValue(0) | |
.minimum(0) | |
.maximum(20) | |
.actualValue(this.level) | |
.build(); | |
} | |
@Override | |
protected void registerGettersAndSetters() { | |
registerFieldGetter(FakeKeys.FAKE_BOOL, () -> this.enabled); | |
registerFieldSetter(FakeKeys.FAKE_BOOL, value -> this.enabled = value); | |
registerKeyValue(FakeKeys.FAKE_BOOL, this::fakeBool); | |
registerFieldGetter(FakeKeys.FAKE_BOUNDED, () -> this.level); | |
registerFieldSetter(FakeKeys.FAKE_BOUNDED, value -> this.level = value); | |
registerKeyValue(FakeKeys.FAKE_BOUNDED, this::fakeInt); | |
registerFieldGetter(FakeKeys.FAKE_STRING, () -> this.name); | |
registerFieldSetter(FakeKeys.FAKE_STRING, value -> this.name = checkNotNull(value)); | |
registerKeyValue(FakeKeys.FAKE_STRING, this::fakeString); | |
} | |
@Override | |
public Optional<FakeData> fill(DataHolder dataHolder, MergeFunction overlap) { | |
return Optional.empty(); // Yes, this should be implemented properly, but it isn't necessary currently. | |
} | |
@Override | |
public Optional<FakeData> from(DataContainer container) { | |
if (!container.contains(FakeKeys.FAKE_STRING.getQuery(), FakeKeys.FAKE_BOOL.getQuery(), FakeKeys.FAKE_BOUNDED.getQuery())) { | |
return Optional.empty(); | |
} | |
final String string = container.getString(FakeKeys.FAKE_STRING.getQuery()).get(); | |
final boolean bool = container.getBoolean(FakeKeys.FAKE_BOOL.getQuery()).get(); | |
final int level = container.getInt(FakeKeys.FAKE_BOUNDED.getQuery()).get(); | |
this.name = string; | |
this.enabled = bool; | |
this.level = level; | |
return Optional.of(this); | |
} | |
@Override | |
public FakeData copy() { | |
return new FakeData(this.level, this.name, this.enabled); | |
} | |
@Override | |
public ImmutableFakeData asImmutable() { | |
return new ImmutableFakeData(this.level, this.name, this.enabled); | |
} | |
@Override | |
public int compareTo(FakeData o) { | |
return 0; | |
} | |
@Override | |
public int getContentVersion() { | |
return 1; | |
} | |
@Override | |
public DataContainer toContainer() { | |
return super.toContainer() | |
.set(FakeKeys.FAKE_STRING, this.name) | |
.set(FakeKeys.FAKE_BOOL, this.enabled) | |
.set(FakeKeys.FAKE_BOUNDED, this.level); | |
} | |
@Override | |
public String toString() { | |
return Objects.toStringHelper(this) | |
.add("level", this.level) | |
.add("name", this.name) | |
.add("enabled", this.enabled) | |
.toString(); | |
} | |
} |
package org.spongepowered.cookbook.plugin; | |
import org.spongepowered.api.data.DataHolder; | |
import org.spongepowered.api.data.DataView; | |
import org.spongepowered.api.data.manipulator.DataManipulatorBuilder; | |
import org.spongepowered.api.util.persistence.InvalidDataException; | |
import java.util.Optional; | |
public class FakeDataManipulatorBuilder implements DataManipulatorBuilder<FakeData, ImmutableFakeData> { | |
@Override | |
public FakeData create() { | |
return new FakeData(); | |
} | |
@Override | |
public Optional<FakeData> createFrom(DataHolder dataHolder) { | |
return Optional.of(dataHolder.get(FakeData.class).orElse(new FakeData())); | |
} | |
@Override | |
public Optional<FakeData> build(DataView container) throws InvalidDataException { | |
// Note that this should check the Queries.CONTENT_VERSION, but for the sake of demonstration | |
// it's not necessary | |
if (container.contains(FakeKeys.FAKE_BOOL, FakeKeys.FAKE_BOUNDED, FakeKeys.FAKE_STRING)) { | |
final boolean fakeBool = container.getBoolean(FakeKeys.FAKE_BOOL.getQuery()).get(); | |
final int level = container.getInt(FakeKeys.FAKE_BOUNDED.getQuery()).get(); | |
final String fakeString = container.getString(FakeKeys.FAKE_STRING.getQuery()).get(); | |
return Optional.of(new FakeData(level, fakeString, fakeBool)); | |
} | |
return Optional.empty(); | |
} | |
} |
package org.spongepowered.cookbook.plugin; | |
import org.spongepowered.api.Sponge; | |
import org.spongepowered.api.command.CommandException; | |
import org.spongepowered.api.command.CommandResult; | |
import org.spongepowered.api.command.CommandSource; | |
import org.spongepowered.api.command.args.CommandContext; | |
import org.spongepowered.api.command.args.GenericArguments; | |
import org.spongepowered.api.command.spec.CommandExecutor; | |
import org.spongepowered.api.command.spec.CommandSpec; | |
import org.spongepowered.api.entity.living.player.Player; | |
import org.spongepowered.api.event.Listener; | |
import org.spongepowered.api.event.game.state.GameInitializationEvent; | |
import org.spongepowered.api.event.game.state.GamePreInitializationEvent; | |
import org.spongepowered.api.plugin.Plugin; | |
import org.spongepowered.api.text.Text; | |
import java.util.Optional; | |
import javax.annotation.Resource; | |
@Plugin(id = "spongetest", name = "SpongeTest", version = "0.1") | |
@Resource | |
public class WorldsTest { | |
@Listener | |
public void onPreInit(GamePreInitializationEvent event) { | |
Sponge.getDataManager().register(FakeData.class, ImmutableFakeData.class, new FakeDataManipulatorBuilder()); | |
} | |
@Listener | |
public void onGameInit(GameInitializationEvent event) { | |
CommandSpec skillDataSpec = CommandSpec.builder() | |
.description(Text.of("Applies skill data")) | |
.arguments(GenericArguments.optional(GenericArguments.onlyOne(GenericArguments.player(Text.of("player"))))) | |
.executor(new SkillDataExecturo()) | |
.build(); | |
CommandSpec skillValidate = CommandSpec.builder() | |
.description(Text.of("Validates skill data")) | |
.arguments(GenericArguments.optional(GenericArguments.onlyOne(GenericArguments.player(Text.of("player"))))) | |
.executor(new SkillValidator()) | |
.build(); | |
Sponge.getGame().getCommandManager().register(this, skillValidate, "validateData"); | |
Sponge.getGame().getCommandManager().register(this, skillDataSpec, "fakeData"); | |
} | |
public static class SkillDataExecturo implements CommandExecutor { | |
@Override | |
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { | |
Optional<Player> target = args.getOne("player"); | |
if (target.isPresent()) { | |
Player player = target.get(); | |
player.offer(new FakeData(1000, "Three Hundred", false)); | |
} else { | |
if (src instanceof Player) { | |
Player player = (Player) src; | |
player.offer(new FakeData(1000, "Three Hunderd", false)); | |
} | |
} | |
return CommandResult.success(); | |
} | |
} | |
public static class SkillValidator implements CommandExecutor { | |
@Override | |
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { | |
Optional<Player> target = args.getOne("player"); | |
if (target.isPresent()) { | |
Player player = target.get(); | |
Optional<FakeData> optional = player.get(FakeData.class); | |
if (optional.isPresent()) { | |
src.sendMessage(Text.of("Data available!")); | |
System.out.println(optional.get().toString()); | |
} | |
} else { | |
if (src instanceof Player) { | |
Player player = (Player) src; | |
Optional<FakeData> optional = player.get(FakeData.class); | |
if (optional.isPresent()) { | |
src.sendMessage(Text.of("Data available!")); | |
System.out.println(optional.get().toString()); | |
} | |
} | |
} | |
return CommandResult.success(); | |
} | |
} | |
} |
package org.spongepowered.cookbook.plugin; | |
import static org.spongepowered.api.data.DataQuery.of; | |
import static org.spongepowered.api.data.key.KeyFactory.makeSingleKey; | |
import org.spongepowered.api.data.key.Key; | |
import org.spongepowered.api.data.value.mutable.MutableBoundedValue; | |
import org.spongepowered.api.data.value.mutable.Value; | |
public class FakeKeys { | |
public static final Key<Value<Boolean>> FAKE_BOOL = makeSingleKey(Boolean.class, Value.class, of("FakeBool")); | |
public static final Key<Value<String>> FAKE_STRING = makeSingleKey(String.class, Value.class, of("FakeString")); | |
public static final Key<MutableBoundedValue<Integer>> FAKE_BOUNDED = makeSingleKey(Integer.class, MutableBoundedValue.class, of("FakeInteger")); | |
} |
package org.spongepowered.cookbook.plugin; | |
import com.google.common.collect.ComparisonChain; | |
import org.spongepowered.api.Sponge; | |
import org.spongepowered.api.data.DataContainer; | |
import org.spongepowered.api.data.MemoryDataContainer; | |
import org.spongepowered.api.data.key.Key; | |
import org.spongepowered.api.data.manipulator.immutable.common.AbstractImmutableData; | |
import org.spongepowered.api.data.value.BaseValue; | |
import org.spongepowered.api.data.value.immutable.ImmutableBoundedValue; | |
import org.spongepowered.api.data.value.immutable.ImmutableValue; | |
import java.util.Optional; | |
public class ImmutableFakeData extends AbstractImmutableData<ImmutableFakeData, FakeData> { | |
private final int level; | |
private final String name; | |
private final boolean enabled; | |
public ImmutableFakeData() { | |
this(0, "", false); | |
} | |
public ImmutableFakeData(int level, String name, boolean enabled) { | |
this.level = level; | |
this.name = name; | |
this.enabled = enabled; | |
} | |
public ImmutableValue<Boolean> fakeBool() { | |
return Sponge.getRegistry().getValueFactory().createValue(FakeKeys.FAKE_BOOL, false, this.enabled).asImmutable(); | |
} | |
public ImmutableValue<String> fakeString() { | |
return Sponge.getRegistry().getValueFactory().createValue(FakeKeys.FAKE_STRING, "", this.name).asImmutable(); | |
} | |
public ImmutableBoundedValue<Integer> fakeInt() { | |
return Sponge.getRegistry().getValueFactory().createBoundedValueBuilder(FakeKeys.FAKE_BOUNDED) | |
.defaultValue(0) | |
.minimum(0) | |
.maximum(20) | |
.actualValue(this.level) | |
.build() | |
.asImmutable(); | |
} | |
@Override | |
protected void registerGetters() { | |
registerFieldGetter(FakeKeys.FAKE_BOOL, this::isEnabled); | |
registerKeyValue(FakeKeys.FAKE_BOOL, this::fakeBool); | |
registerFieldGetter(FakeKeys.FAKE_BOUNDED, this::getLevel); | |
registerKeyValue(FakeKeys.FAKE_BOUNDED, this::fakeInt); | |
registerFieldGetter(FakeKeys.FAKE_STRING, this::getName); | |
registerKeyValue(FakeKeys.FAKE_STRING, this::fakeString); | |
} | |
@Override | |
public <E> Optional<ImmutableFakeData> with(Key<? extends BaseValue<E>> key, E value) { | |
return Optional.empty(); | |
} | |
@Override | |
public FakeData asMutable() { | |
return new FakeData(this.level, this.name, this.enabled); | |
} | |
@Override | |
public int compareTo(ImmutableFakeData o) { | |
return ComparisonChain.start() | |
.compare(o.enabled, this.enabled) | |
.compare(o.level, this.level) | |
.compare(o.name, this.name) | |
.result(); | |
} | |
@Override | |
public int getContentVersion() { | |
return 1; | |
} | |
@Override | |
public DataContainer toContainer() { | |
return new MemoryDataContainer() | |
.set(FakeKeys.FAKE_STRING, this.name) | |
.set(FakeKeys.FAKE_BOOL, this.enabled) | |
.set(FakeKeys.FAKE_BOUNDED, this.level); | |
} | |
private int getLevel() { | |
return this.level; | |
} | |
private String getName() { | |
return this.name; | |
} | |
private boolean isEnabled() { | |
return this.enabled; | |
} | |
} |
createValue element and default is backwards?
Saladoc also pointed out that the comparison chain is likely backwards.
o might need to be swapped with this.
Line 6 FakeDataManipulatorBuilder should
import org.spongepowered.api.util.persistence.InvalidDataException;
be
import org.spongepowered.api.data.persistence.InvalidDataException;
To spare others the same degree of hairloss I have suffered:
This example works if you access the whole data, but it will not work for actually getting the Values by using keys. Data can be offered, and successfully added, and visibly there... but not retrievable via the Value based retrieval methods.
The method registerGettersAndSetters() in the data class is not used, and must be called in the constructor.
Likewise, the method registerGetters() in the immutable data class is not used, and must be called in its constructor.
With those additions, you then will actually be able to get the Values with key access, or .getValues() methods on the data.
Is there any way to add custom data to BlockState's ?
@Daniel12321 Not really no.
TileEntities you can, because there is an arbitrary NBT structure attached to them.
BlockStates get optimized down to a map of BlockStates to integers so they can be stored in memory efficiently.
So the block traits that Mojang register end up getting enumerated and turned into singletons representing the type of each block.
The closest thing you can do to attach data to locations is to create your own store assuming it's sparse and accessed infrequently, you could get away with a database, otherwise you are basically going to be stuck reimplementing the chunk structure and storing your data in that.
One option might be to allow storing data on chunks, referenced by location, in a similar vein to tile entities / the current block tracking solution. But it's hairy and probably best handled by the Sponge team at a later date once everything else that is currently in the API is working and implemented.
I forked this for others trying to follow along in the comments.
https://gist.github.com/ryantheleach/6d3c7d8ea5cddde47d426183e120e6d7
@resource
public class WorldsTest {