/* * 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 static com.google.common.base.Preconditions.checkState; import com.flowpowered.math.vector.Vector3i; import com.google.common.collect.ImmutableMap; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.data.DataView; import org.spongepowered.api.data.key.Key; import org.spongepowered.api.data.manipulator.DataManipulator; import org.spongepowered.api.data.manipulator.ImmutableDataManipulator; import org.spongepowered.api.data.persistence.AbstractDataBuilder; import org.spongepowered.api.data.persistence.InvalidDataException; import org.spongepowered.api.data.value.BaseValue; import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import org.spongepowered.api.world.storage.WorldProperties; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; import javax.annotation.Nullable; // Using this class directly makes intellij complain, // so let's just use a subclass until it's fixed. public abstract class BlockSnapshotBuilder extends AbstractDataBuilder<BlockSnapshot> implements BlockSnapshot.Builder { public static BlockSnapshotBuilder create() { return new LanternBlockSnapshotBuilder(); } public static BlockSnapshotBuilder createPositionless() { return new Positionless(); } private static final class Positionless extends BlockSnapshotBuilder { } UUID worldUUID; private BlockState blockState; @Nullable private BlockState extendedBlockState; Vector3i position; @Nullable private UUID creator; @Nullable private UUID notifier; private final Map<Key<?>, Object> tileEntityData = new HashMap<>(); BlockSnapshotBuilder() { super(BlockSnapshot.class, 1); } /** * Sets the coordinates and world of this {@link BlockSnapshot} from a {@link Location}. * * @param location The location * @return This builder, for chaining */ public BlockSnapshotBuilder location(Location<World> location) { this.worldUUID = location == null ? null : location.getExtent().getProperties().getUniqueId(); this.position = location == null ? null : location.getBlockPosition(); return this; } @Override public BlockSnapshotBuilder world(WorldProperties worldProperties) { this.worldUUID = worldProperties == null ? null : worldProperties.getUniqueId(); return this; } @Override public BlockSnapshotBuilder blockState(BlockState blockState) { this.blockState = checkNotNull(blockState, "blockState"); this.extendedBlockState = null; return this; } @Override public BlockSnapshotBuilder position(Vector3i position) { this.position = position; return this; } @Override public BlockSnapshotBuilder from(Location<World> location) { checkNotNull(location, "location"); this.worldUUID = location.getExtent().getProperties().getUniqueId(); this.position = location.getBlockPosition(); this.blockState = location.getBlock(); final World world = location.getExtent(); this.creator = world.getCreator(location.getBlockPosition()).orElse(null); this.notifier = world.getNotifier(location.getBlockPosition()).orElse(null); final BlockState extendedState = ((LanternBlockType) this.blockState.getType()) .getExtendedBlockStateProvider().get(this.blockState, location, null); this.extendedBlockState = extendedState == this.blockState ? null : extendedState; return this; } @Override public BlockSnapshotBuilder creator(UUID uuid) { this.creator = checkNotNull(uuid, "uuid"); return this; } @Override public BlockSnapshotBuilder notifier(UUID uuid) { this.notifier = checkNotNull(uuid, "uuid"); return this; } @Override public BlockSnapshotBuilder add(DataManipulator<?, ?> manipulator) { checkState(this.blockState != null, "The block state must be set before you can add data manipulators."); final Optional<BlockState> blockState = this.blockState.with(manipulator.asImmutable()); if (blockState.isPresent()) { this.blockState = blockState.get(); } return this; } @Override public BlockSnapshotBuilder add(ImmutableDataManipulator<?, ?> manipulator) { checkState(this.blockState != null, "The block state must be set before you can add data manipulators."); final Optional<BlockState> blockState = this.blockState.with(manipulator); if (blockState.isPresent()) { this.blockState = blockState.get(); } return this; } @Override public <V> BlockSnapshotBuilder add(Key<? extends BaseValue<V>> key, V value) { checkState(this.blockState != null, "The block state must be set before you can add key values."); final Optional<BlockState> blockState = this.blockState.with(key, value); if (blockState.isPresent()) { this.blockState = blockState.get(); } else { // TODO: Check if the key is supported??? this.tileEntityData.put(key, value); } return this; } @Override public BlockSnapshotBuilder from(BlockSnapshot holder) { final LanternBlockSnapshot snapshot = (LanternBlockSnapshot) checkNotNull(holder, "holder"); this.creator = snapshot.getCreator().orElse(null); this.notifier = snapshot.getNotifier().orElse(null); this.blockState = snapshot.getState(); final BlockState extendedState = holder.getExtendedState(); this.extendedBlockState = extendedState == this.blockState ? null : extendedState; final LanternBlockSnapshot.BlockLocation blockLocation = snapshot.location; //noinspection ConstantConditions this.worldUUID = blockLocation == null ? null : blockLocation.world.getUniqueId(); //noinspection ConstantConditions this.position = blockLocation == null ? null : blockLocation.position; this.tileEntityData.clear(); if (snapshot.tileEntityData != null) { this.tileEntityData.putAll(snapshot.tileEntityData); } return this; } @Override public BlockSnapshot build() { checkState(this.blockState != null, "The block state must be set."); final LanternBlockSnapshot.BlockLocation blockLocation = this.worldUUID == null || this.position == null ? null : new LanternBlockSnapshot.BlockLocation(this.worldUUID, this.position); return new LanternBlockSnapshot(blockLocation, this.blockState, this.extendedBlockState, Optional.ofNullable(this.creator), Optional.ofNullable(this.notifier), ImmutableMap.copyOf(this.tileEntityData)); } @Override public Optional<BlockSnapshot> buildContent(DataView container) throws InvalidDataException { return null; } @Override public BlockSnapshotBuilder reset() { this.position = null; this.blockState = null; this.extendedBlockState = null; this.worldUUID = null; this.notifier = null; this.creator = null; this.tileEntityData.clear(); return this; } }