/* * This file is part of LanternServer, licensed under the MIT License (MIT). * * Copyright (c) LanternPowered <https://www.lanternpowered.org> * Copyright (c) SpongePowered <https://www.spongepowered.org> * 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.lanternpowered.server.block; import static com.google.common.base.Preconditions.checkNotNull; import com.flowpowered.math.vector.Vector3i; import org.lanternpowered.server.data.property.AbstractPropertyHolder; import org.lanternpowered.server.data.util.DataQueries; import org.lanternpowered.server.world.WeakWorldReference; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.tileentity.TileEntity; import org.spongepowered.api.block.tileentity.TileEntityArchetype; import org.spongepowered.api.data.DataContainer; import org.spongepowered.api.data.DataView; import org.spongepowered.api.data.MemoryDataContainer; import org.spongepowered.api.data.Queries; import org.spongepowered.api.data.key.Key; import org.spongepowered.api.data.manipulator.ImmutableDataManipulator; import org.spongepowered.api.data.merge.MergeFunction; import org.spongepowered.api.data.value.BaseValue; import org.spongepowered.api.data.value.immutable.ImmutableValue; import org.spongepowered.api.event.cause.Cause; import org.spongepowered.api.event.cause.NamedCause; import org.spongepowered.api.world.BlockChangeFlag; import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.function.Function; import javax.annotation.Nullable; public class LanternBlockSnapshot implements BlockSnapshot, AbstractPropertyHolder { private static Cause RESTORE_CAUSE = Cause.of(NamedCause.of("EMPTY", "EMPTY")); /** * Represents the {@link Location} of a block. */ static final class BlockLocation { final WeakWorldReference world; final Vector3i position; public BlockLocation(Location<World> location) { this(new WeakWorldReference(location.getExtent()), location.getBlockPosition()); } public BlockLocation(World world, Vector3i position) { this(new WeakWorldReference(world), position); } public BlockLocation(UUID worldUUID, Vector3i position) { this(new WeakWorldReference(worldUUID), position); } private BlockLocation(WeakWorldReference world, Vector3i position) { this.position = checkNotNull(position, "position"); this.world = world; } } @Nullable final BlockLocation location; private final BlockState state; @Nullable private final BlockState extendedState; private final Optional<UUID> notifier; private final Optional<UUID> creator; @Nullable final Map<Key<?>, Object> tileEntityData; public LanternBlockSnapshot(Location<World> location, BlockState blockState, @Nullable BlockState extendedState, Optional<UUID> creator, Optional<UUID> notifier, @Nullable Map<Key<?>, Object> tileEntityData) { this(new BlockLocation(checkNotNull(location, "location")), blockState, extendedState, creator, notifier, tileEntityData); } public LanternBlockSnapshot(UUID worldUUID, Vector3i position, BlockState blockState, @Nullable BlockState extendedState, Optional<UUID> creator, Optional<UUID> notifier, @Nullable Map<Key<?>, Object> tileEntityData) { this(new BlockLocation(worldUUID, position), blockState, extendedState, creator, notifier, tileEntityData); } public LanternBlockSnapshot(BlockState blockState, @Nullable BlockState extendedState, Optional<UUID> notifier, Optional<UUID> creator, @Nullable Map<Key<?>, Object> tileEntityData) { this((BlockLocation) null, blockState, extendedState, creator, notifier, tileEntityData); } LanternBlockSnapshot(@Nullable BlockLocation location, BlockState blockState, @Nullable BlockState extendedState, Optional<UUID> creator, Optional<UUID> notifier, @Nullable Map<Key<?>, Object> tileEntityData) { this.extendedState = extendedState; this.notifier = checkNotNull(notifier, "notifier"); this.creator = checkNotNull(creator, "creator"); this.state = checkNotNull(blockState, "blockState"); this.tileEntityData = tileEntityData; this.location = location; } public boolean isPositionless() { return this.location == null; } @Override public int getContentVersion() { return 1; } @Override public DataContainer toContainer() { final DataContainer container = new MemoryDataContainer() .set(DataQueries.BLOCK_STATE, this.state); if (this.location != null) { container.set(Queries.WORLD_ID, this.location.world.getUniqueId()); final DataView positionView = container.createView(DataQueries.SNAPSHOT_WORLD_POSITION); positionView.set(Queries.POSITION_X, this.location.position.getX()); positionView.set(Queries.POSITION_Y, this.location.position.getY()); positionView.set(Queries.POSITION_Z, this.location.position.getZ()); } this.notifier.ifPresent(notifier -> container.set(Queries.NOTIFIER_ID, notifier)); this.creator.ifPresent(creator -> container.set(Queries.CREATOR_ID, creator)); return container; } @Override public BlockState getExtendedState() { return this.extendedState == null ? this.state : this.extendedState; } @Override public BlockState getState() { return this.state; } @Override public LanternBlockSnapshot copy() { return new LanternBlockSnapshot(this.location, this.state, extendedState, this.creator, this.notifier, tileEntityData); } @Override public Optional<Location<World>> getLocation() { if (this.location == null) { return Optional.empty(); } Optional<World> world = this.location.world.getWorld(); if (!world.isPresent()) { return Optional.empty(); } return Optional.of(new Location<>(world.get(), this.location.position)); } @Override public List<ImmutableDataManipulator<?, ?>> getManipulators() { // TODO Auto-generated method stub return null; } @Override public <T extends ImmutableDataManipulator<?, ?>> Optional<T> get(Class<T> containerClass) { // TODO Auto-generated method stub return null; } @Override public <T extends ImmutableDataManipulator<?, ?>> Optional<T> getOrCreate(Class<T> containerClass) { // TODO Auto-generated method stub return null; } @Override public boolean supports(Class<? extends ImmutableDataManipulator<?, ?>> containerClass) { // TODO Auto-generated method stub return false; } @Override public <E> Optional<BlockSnapshot> transform(Key<? extends BaseValue<E>> key, Function<E, E> function) { // TODO Auto-generated method stub return null; } @Override public <E> Optional<BlockSnapshot> with(Key<? extends BaseValue<E>> key, E value) { // TODO Auto-generated method stub return null; } @Override public Optional<BlockSnapshot> with(BaseValue<?> value) { // TODO Auto-generated method stub return null; } @Override public Optional<BlockSnapshot> with(ImmutableDataManipulator<?, ?> valueContainer) { // TODO Auto-generated method stub return null; } @Override public Optional<BlockSnapshot> with(Iterable<ImmutableDataManipulator<?, ?>> valueContainers) { // TODO Auto-generated method stub return null; } @Override public Optional<BlockSnapshot> without(Class<? extends ImmutableDataManipulator<?, ?>> containerClass) { // TODO Auto-generated method stub return null; } @Override public BlockSnapshot merge(BlockSnapshot that) { // TODO Auto-generated method stub return null; } @Override public BlockSnapshot merge(BlockSnapshot that, MergeFunction function) { // TODO Auto-generated method stub return null; } @Override public <E> Optional<E> get(Key<? extends BaseValue<E>> key) { // TODO Auto-generated method stub return null; } @Override public <E, V extends BaseValue<E>> Optional<V> getValue(Key<V> key) { // TODO Auto-generated method stub return null; } @Override public boolean supports(Key<?> key) { // TODO Auto-generated method stub return false; } @Override public Set<Key<?>> getKeys() { // TODO Auto-generated method stub return null; } @Override public Set<ImmutableValue<?>> getValues() { // TODO Auto-generated method stub return null; } @Override public BlockSnapshot withState(BlockState blockState) { // TODO Auto-generated method stub return null; } @Override public List<ImmutableDataManipulator<?, ?>> getContainers() { // TODO Auto-generated method stub return null; } @Override public UUID getWorldUniqueId() { if (this.location == null) { throw new IllegalStateException("This BlockSnapshot doesn't have a location."); } return this.location.world.getUniqueId(); } @Override public Vector3i getPosition() { if (this.location == null) { throw new IllegalStateException("This BlockSnapshot doesn't have a location."); } return this.location.position; } @Override public BlockSnapshot withLocation(Location<World> location) { checkNotNull(location, "location"); return new LanternBlockSnapshot(location, this.state, extendedState, this.creator, this.notifier, tileEntityData); } @Override public BlockSnapshot withContainer(DataContainer container) { return new LanternBlockSnapshotBuilder().build(container).get(); } @Override public boolean restore(boolean force, BlockChangeFlag flag) { if (this.location == null) { throw new IllegalStateException("This BlockSnapshot doesn't have a location."); } Location<World> loc = this.getLocation().orElse(null); if (loc == null || (!force && loc.getBlockType() != this.state.getType())) { return false; } loc.setBlock(this.state, flag, RESTORE_CAUSE); final World world = loc.getExtent(); world.setCreator(this.location.position, this.creator.orElse(null)); world.setNotifier(this.location.position, this.notifier.orElse(null)); if (this.tileEntityData != null) { final TileEntity tileEntity = loc.getTileEntity().orElse(null); if (tileEntity != null) { //noinspection unchecked this.tileEntityData.forEach((key, value) -> tileEntity.offer((Key) key, value)); } } return true; } @Override public Optional<UUID> getCreator() { return this.creator; } @Override public Optional<UUID> getNotifier() { return this.notifier; } @Override public Optional<TileEntityArchetype> createArchetype() { return null; } }