/*
* 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.world;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.lanternpowered.server.world.chunk.LanternChunk.fixEntityYSection;
import static org.lanternpowered.server.world.chunk.LanternChunkLayout.SPACE_MAX;
import static org.lanternpowered.server.world.chunk.LanternChunkLayout.SPACE_MIN;
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3d;
import com.flowpowered.math.vector.Vector3i;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.lanternpowered.api.world.weather.WeatherUniverse;
import org.lanternpowered.server.behavior.Behavior;
import org.lanternpowered.server.behavior.BehaviorContext;
import org.lanternpowered.server.behavior.BehaviorContextImpl;
import org.lanternpowered.server.behavior.Parameters;
import org.lanternpowered.server.behavior.pipeline.BehaviorPipeline;
import org.lanternpowered.server.block.LanternBlockType;
import org.lanternpowered.server.block.action.BlockAction;
import org.lanternpowered.server.block.behavior.types.InteractWithBlockBehavior;
import org.lanternpowered.server.block.behavior.types.PlaceBlockBehavior;
import org.lanternpowered.server.component.BaseComponentHolder;
import org.lanternpowered.server.config.world.WorldConfig;
import org.lanternpowered.server.data.io.ChunkIOService;
import org.lanternpowered.server.data.io.ScoreboardIO;
import org.lanternpowered.server.data.io.anvil.AnvilChunkIOService;
import org.lanternpowered.server.data.io.store.item.WrittenBookItemTypeObjectSerializer;
import org.lanternpowered.server.effect.AbstractViewer;
import org.lanternpowered.server.effect.sound.LanternSoundType;
import org.lanternpowered.server.entity.LanternEntity;
import org.lanternpowered.server.entity.LanternEntityType;
import org.lanternpowered.server.entity.living.player.LanternPlayer;
import org.lanternpowered.server.entity.living.player.ObservedChunkManager;
import org.lanternpowered.server.game.Lantern;
import org.lanternpowered.server.game.LanternGame;
import org.lanternpowered.server.network.entity.EntityProtocolManager;
import org.lanternpowered.server.network.entity.EntityProtocolType;
import org.lanternpowered.server.network.message.Message;
import org.lanternpowered.server.network.objects.RawItemStack;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutOpenBook;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutParticleEffect;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutSetWindowSlot;
import org.lanternpowered.server.text.chat.LanternChatType;
import org.lanternpowered.server.text.title.LanternTitles;
import org.lanternpowered.server.util.VecHelper;
import org.lanternpowered.server.world.chunk.ChunkLoadingTicket;
import org.lanternpowered.server.world.chunk.LanternChunk;
import org.lanternpowered.server.world.chunk.LanternChunkManager;
import org.lanternpowered.server.world.chunk.LanternChunkTicketManager;
import org.lanternpowered.server.world.dimension.LanternDimensionType;
import org.lanternpowered.server.world.extent.AbstractExtent;
import org.lanternpowered.server.world.extent.ExtentViewDownsize;
import org.lanternpowered.server.world.extent.worker.LanternMutableBiomeVolumeWorker;
import org.lanternpowered.server.world.extent.worker.LanternMutableBlockVolumeWorker;
import org.lanternpowered.server.world.rules.Rule;
import org.lanternpowered.server.world.rules.RuleHolder;
import org.lanternpowered.server.world.rules.RuleType;
import org.lanternpowered.server.world.weather.LanternWeather;
import org.spongepowered.api.block.BlockSnapshot;
import org.spongepowered.api.block.BlockState;
import org.spongepowered.api.block.BlockType;
import org.spongepowered.api.block.ScheduledBlockUpdate;
import org.spongepowered.api.block.tileentity.TileEntity;
import org.spongepowered.api.data.DataContainer;
import org.spongepowered.api.data.DataHolder;
import org.spongepowered.api.data.DataTransactionResult;
import org.spongepowered.api.data.DataView;
import org.spongepowered.api.data.MemoryDataContainer;
import org.spongepowered.api.data.Property;
import org.spongepowered.api.data.key.Key;
import org.spongepowered.api.data.manipulator.DataManipulator;
import org.spongepowered.api.data.merge.MergeFunction;
import org.spongepowered.api.data.persistence.InvalidDataException;
import org.spongepowered.api.data.type.HandTypes;
import org.spongepowered.api.data.value.BaseValue;
import org.spongepowered.api.data.value.immutable.ImmutableValue;
import org.spongepowered.api.effect.particle.ParticleEffect;
import org.spongepowered.api.effect.sound.SoundCategory;
import org.spongepowered.api.effect.sound.SoundType;
import org.spongepowered.api.entity.Entity;
import org.spongepowered.api.entity.EntitySnapshot;
import org.spongepowered.api.entity.EntityType;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.item.inventory.ItemStack;
import org.spongepowered.api.scoreboard.Scoreboard;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.text.BookView;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.channel.MessageChannel;
import org.spongepowered.api.text.chat.ChatType;
import org.spongepowered.api.text.chat.ChatTypes;
import org.spongepowered.api.text.title.Title;
import org.spongepowered.api.util.AABB;
import org.spongepowered.api.util.Direction;
import org.spongepowered.api.util.GuavaCollectors;
import org.spongepowered.api.world.BlockChangeFlag;
import org.spongepowered.api.world.Chunk;
import org.spongepowered.api.world.Dimension;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.PortalAgent;
import org.spongepowered.api.world.World;
import org.spongepowered.api.world.WorldBorder.ChunkPreGenerate;
import org.spongepowered.api.world.biome.BiomeType;
import org.spongepowered.api.world.difficulty.Difficulty;
import org.spongepowered.api.world.explosion.Explosion;
import org.spongepowered.api.world.extent.ArchetypeVolume;
import org.spongepowered.api.world.extent.Extent;
import org.spongepowered.api.world.extent.worker.MutableBiomeVolumeWorker;
import org.spongepowered.api.world.extent.worker.MutableBlockVolumeWorker;
import org.spongepowered.api.world.gen.WorldGenerator;
import org.spongepowered.api.world.storage.WorldStorage;
import org.spongepowered.api.world.weather.Weather;
import org.spongepowered.api.world.weather.Weathers;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
public class LanternWorld extends BaseComponentHolder implements AbstractExtent, org.lanternpowered.api.world.World, AbstractViewer, RuleHolder {
public static final Vector3i BLOCK_MIN = new Vector3i(-30000000, 0, -30000000);
public static final Vector3i BLOCK_MAX = new Vector3i(30000000, 256, 30000000).sub(1, 1, 1);
public static final Vector3i BLOCK_SIZE = BLOCK_MAX.sub(BLOCK_MIN).add(1, 1, 1);
public static final Vector3i BIOME_MIN = BLOCK_MIN.mul(1, 0, 1);
public static final Vector3i BIOME_MAX = BLOCK_MAX.mul(1, 0, 1);
public static final Vector3i BIOME_SIZE = BIOME_MAX.sub(BLOCK_MIN).add(1, 1, 1);
// The spawn size starting from the spawn point and expanded
// by this size in the directions +x, +z, -x, -z
private final static int SPAWN_SIZE = 12;
// The loading ticket to keep the spawn chunks loaded
@Nullable private volatile ChunkLoadingTicket spawnLoadingTicket;
// The game instance
final LanternGame game;
// The world border
final LanternWorldBorder worldBorder;
// The weather universe
@Nullable final LanternWeatherUniverse weatherUniverse;
private final TimeUniverse timeUniverse;
// All the players in this world
private final Set<LanternPlayer> players = Sets.newConcurrentHashSet();
// All the players in this world
private final Collection<Player> unmodifiablePlayers = Collections.unmodifiableCollection(this.players);
// The chunk manager of this world
private final LanternChunkManager chunkManager;
/**
* The entities mapped by their unique id.
*/
private final Map<UUID, LanternEntity> entitiesByUniqueId = new ConcurrentHashMap<>();
/**
* The chunk manager that will allows observers to track
* changes in chunks.
*/
private final ObservedChunkManager observedChunkManager = new ObservedChunkManager(this);
/**
* The {@link Scoreboard} that is attached to this {@link World}.
*/
private final Scoreboard scoreboard;
// The dimension instance attached to this world
private final Dimension dimension;
// The world configuration
private final WorldConfig worldConfig;
// The properties of this world
final LanternWorldProperties properties;
private final PortalAgent portalAgent;
// The context of this world
private final Context worldContext;
private final MultiWorldEventListener worldEventListener = new MultiWorldEventListener();
/**
* The directory where all the data of the
* world is stored.
*/
private final Path directory;
/**
* The message channel of the world.
*/
private MessageChannel messageChannel = MessageChannel.world(this);
/**
* The entity protocol manager.
*/
private EntityProtocolManager entityProtocolManager = new EntityProtocolManager();
public LanternWorld(LanternGame game, WorldConfig worldConfig, Path directory,
Scoreboard scoreboard, LanternWorldProperties properties) {
this.directory = directory;
this.worldConfig = worldConfig;
this.scoreboard = scoreboard;
this.properties = properties;
this.game = game;
// Create the chunk io service
final ChunkIOService chunkIOService = new AnvilChunkIOService(directory, this);
// Get the chunk load service
final LanternChunkTicketManager chunkLoadService = game.getChunkTicketManager();
// Get the dimension type
final LanternDimensionType<?> dimensionType = (LanternDimensionType<?>) properties.getDimensionType();
// Create the weather universe if needed
if (dimensionType.hasSky()) {
this.weatherUniverse = this.addComponent(LanternWeatherUniverse.class);
} else {
this.weatherUniverse = null;
}
this.timeUniverse = this.addComponent(TimeUniverse.class);
// Create the world border
this.worldBorder = this.addComponent(LanternWorldBorder.class);
// Create the dimension
this.dimension = dimensionType.newDimension(this);
// Create the portal agent
this.portalAgent = properties.getPortalAgentType().newPortalAgent(this);
// Create a new world generator
final WorldGenerator worldGenerator = properties.getGeneratorType().createGenerator(this);
// Finally, create the chunk manager
this.chunkManager = new LanternChunkManager(this.game, this, this.worldConfig, chunkLoadService,
chunkIOService, worldGenerator, directory);
this.worldContext = new Context(Context.WORLD_KEY, this.getName());
this.worldEventListener.add(this.observedChunkManager);
}
@Override
public Optional<WeatherUniverse> getWeatherUniverse() {
return Optional.ofNullable(this.weatherUniverse);
}
public ObservedChunkManager getObservedChunkManager() {
return this.observedChunkManager;
}
public void initialize() {
// Initialize the world if needed
if (this.properties.isInitialized()) {
return;
}
this.properties.setInitialized(true);
}
/**
* Gets the {@link Scoreboard} of this world.
*
* @return The scoreboard
*/
public Scoreboard getScoreboard() {
return this.scoreboard;
}
/**
* Shuts the world down and saves all the
* data in the process.
*/
void shutdown() {
// Release the spawn ticket to avoid it
// getting saved
if (this.spawnLoadingTicket != null) {
this.spawnLoadingTicket.release();
this.spawnLoadingTicket = null;
}
// Shut the chunk manager down
this.chunkManager.shutdown();
}
/**
* Enables whether the spawn volume should be generated and keeping it loaded.
*
* @param keepSpawnLoaded keep spawn loaded
*/
void enableSpawnArea(boolean keepSpawnLoaded) {
if (keepSpawnLoaded) {
final Vector3i spawnPoint = this.properties.getSpawnPosition();
if (this.spawnLoadingTicket == null) {
this.spawnLoadingTicket = (ChunkLoadingTicket) this.chunkManager.createTicket(
this.game.getMinecraftPlugin()).get();
} else {
this.spawnLoadingTicket.unforceChunks();
}
final int chunkX = spawnPoint.getX() >> 4;
final int chunkZ = spawnPoint.getZ() >> 4;
this.game.getLogger().info("Generating spawn volume...");
for (int x = chunkX - SPAWN_SIZE; x < chunkX + SPAWN_SIZE; x++) {
for (int z = chunkZ - SPAWN_SIZE; z < chunkZ + SPAWN_SIZE; z++) {
this.chunkManager.getOrCreateChunk(x, z, () -> Cause.source(this.game.getMinecraftPlugin())
.owner(this).build(), true);
this.spawnLoadingTicket.forceChunk(new Vector2i(x, z));
}
}
this.game.getLogger().info("Finished generating spawn volume.");
} else if (this.spawnLoadingTicket != null) {
this.spawnLoadingTicket.unforceChunks();
}
}
/**
* Gets the players that are currently in this world.
*
* @return The players
*/
@Override
public Collection<Player> getPlayers() {
return this.unmodifiablePlayers;
}
/**
* Gets a raw list with all the players that are currently in this world.
*
* @return The players
*/
public Set<LanternPlayer> getRawPlayers() {
return this.players;
}
public void addPlayer(LanternPlayer player) {
this.players.add(player);
checkArgument(addEntity(player) == null);
}
public void removePlayer(LanternPlayer player) {
this.players.remove(player);
this.entitiesByUniqueId.remove(player.getUniqueId());
final Vector3i lastChunkSection = player.getLastChunkSectionCoords();
LanternChunk chunk;
if (lastChunkSection != null && (chunk = this.chunkManager.getChunkIfLoaded(
lastChunkSection.getX(), lastChunkSection.getZ())) != null) {
chunk.removeEntity(player, lastChunkSection.getY());
}
this.entityProtocolManager.remove(player);
}
/**
* Gets the chunk manager of this world.
*
* @return the chunk manager.
*/
public LanternChunkManager getChunkManager() {
return this.chunkManager;
}
@Override
public Location<World> getLocation(Vector3i position) {
return getLocation(position.getX(), position.getY(), position.getZ());
}
@Override
public Location<World> getLocation(Vector3d position) {
return getLocation(position.getX(), position.getY(), position.getZ());
}
public int getHighestBlockAt(int x, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getHighestBlockAt(x, z);
}
@Override
public Collection<ScheduledBlockUpdate> getScheduledUpdates(int x, int y, int z) {
LanternChunk chunk = this.chunkManager.getChunk(x >> 4, z >> 4);
if (chunk != null) {
return chunk.getScheduledUpdates(x, y, z);
}
return ImmutableSet.of();
}
@Override
public ScheduledBlockUpdate addScheduledUpdate(int x, int y, int z, int priority, int ticks) {
// TODO Auto-generated method stub
return null;
}
@Override
public void removeScheduledUpdate(int x, int y, int z, ScheduledBlockUpdate update) {
// TODO Auto-generated method stub
}
@Override
public boolean isLoaded() {
// TODO Auto-generated method stub
return false;
}
@Override
public Extent getExtentView(Vector3i newMin, Vector3i newMax) {
checkVolumeBounds(newMin);
checkVolumeBounds(newMax);
return new ExtentViewDownsize(this, newMin, newMax);
}
@Override
public MutableBiomeVolumeWorker<World> getBiomeWorker() {
return new LanternMutableBiomeVolumeWorker<>(this);
}
@Override
public MutableBlockVolumeWorker<World> getBlockWorker(Cause cause) {
return new LanternMutableBlockVolumeWorker<>(this, cause);
}
@Override
public boolean save() throws IOException {
this.chunkManager.save();
// Save the scoreboard
ScoreboardIO.write(this.directory, this.scoreboard);
// Save the world properties
Lantern.getServer().getWorldManager().saveWorldProperties(this.properties);
return true; // TODO
}
@Override
public Optional<AABB> getBlockSelectionBox(int x, int y, int z) {
final LanternChunk chunk = getChunkManager().getChunkIfLoaded(x >> 4, z >> 4);
return chunk == null ? Optional.empty() : chunk.getBlockSelectionBox(x, y, z);
}
@Override
public Set<AABB> getIntersectingCollisionBoxes(Entity owner, AABB box) {
checkNotNull(owner, "owner");
return getIntersectingBlockCollisionBoxes(box, entity -> entity != owner);
}
@Override
public Set<AABB> getIntersectingBlockCollisionBoxes(AABB box) {
return getIntersectingBlockCollisionBoxes(box, null);
}
public Set<AABB> getIntersectingBlockCollisionBoxes(AABB box, @Nullable Predicate<Entity> filter) {
checkNotNull(box, "box");
final ImmutableSet.Builder<AABB> boxes = ImmutableSet.builder();
int minY = box.getMin().getFloorY();
final int maxY = box.getMax().getFloorY();
if (minY >= LanternWorld.BLOCK_MAX.getY() || maxY < 0) {
return boxes.build();
}
minY = Math.max(0, minY);
final int maxX = box.getMax().getFloorX();
final int minX = box.getMin().getFloorX();
final int maxZ = box.getMax().getFloorZ();
final int minZ = box.getMin().getFloorZ();
final int maxChunkX = maxX >> 4;
final int minChunkX = minX >> 4;
final int maxChunkZ = maxZ >> 4;
final int minChunkZ = minZ >> 4;
final int maxChunkSection = maxY >> 4;
final int minChunkSection = minY >> 4;
for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) {
for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) {
final LanternChunk chunk = getChunkManager().getChunkIfLoaded(chunkX, chunkZ);
if (chunk == null) {
continue;
}
final int startX = Math.max(minX, chunkX << 4);
final int endX = Math.min(maxX, (chunkX << 4) | 0xf);
final int startZ = Math.max(minZ, chunkZ << 4);
final int endZ = Math.min(maxZ, (chunkZ << 4) | 0xf);
for (int x = startX; x <= endX; x++) {
for (int z = startZ; z <= endZ; z++) {
for (int y = minY; y <= maxY; y++) {
final Optional<AABB> boundingBox = chunk.getBlockSelectionBox(x, y, z);
if (boundingBox.isPresent() && boundingBox.get().intersects(box)) {
boxes.add(boundingBox.get());
}
}
}
}
if (filter != null) {
chunk.addIntersectingEntitiesBoxes(boxes, maxChunkSection, minChunkSection, box, filter);
}
}
}
return boxes.build();
}
@Override
public Set<Entity> getIntersectingEntities(AABB box, Predicate<Entity> filter) {
checkNotNull(box, "box");
checkNotNull(filter, "filter");
final ImmutableSet.Builder<Entity> entities = ImmutableSet.builder();
final int maxX = ((int) Math.ceil(box.getMax().getX() + 2.0)) >> 4;
final int minX = ((int) Math.floor(box.getMin().getX() - 2.0)) >> 4;
final int maxYSection = fixEntityYSection(((int) Math.round(box.getMax().getY() + 2.0)) >> 4);
final int minYSection = fixEntityYSection(((int) Math.round(box.getMin().getY() - 2.0)) >> 4);
final int maxZ = ((int) Math.ceil(box.getMax().getZ() + 2.0)) >> 4;
final int minZ = ((int) Math.floor(box.getMin().getZ() - 2.0)) >> 4;
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
final LanternChunk chunk = getChunkManager().getChunkIfLoaded(x, z);
if (chunk != null) {
chunk.addIntersectingEntities(entities, maxYSection, minYSection, box, filter);
}
}
}
return entities.build();
}
@Override
public Set<EntityHit> getIntersectingEntities(Vector3d start, Vector3d end, Predicate<EntityHit> filter) {
return Collections.emptySet();
}
@Override
public Set<EntityHit> getIntersectingEntities(Vector3d start, Vector3d direction, double distance, Predicate<EntityHit> filter) {
return Collections.emptySet();
}
@Override
public Optional<UUID> getCreator(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getCreator(x, y, z);
}
@Override
public Optional<UUID> getNotifier(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getNotifier(x, y, z);
}
@Override
public void setCreator(int x, int y, int z, @Nullable UUID uuid) {
this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).setCreator(x, y, z, uuid);
}
@Override
public void setNotifier(int x, int y, int z, @Nullable UUID uuid) {
this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).setNotifier(x, y, z, uuid);
}
@Override
public ArchetypeVolume createArchetypeVolume(Vector3i min, Vector3i max, Vector3i origin) {
return null;
}
private void forEachEntity(Consumer<LanternEntity> consumer) {
final Iterator<LanternEntity> iterator = this.entitiesByUniqueId.values().iterator();
while (iterator.hasNext()) {
final LanternEntity entity = iterator.next();
// Only remove the entities that are "destroyed",
// the other ones can be resurrected after chunk loading
if (entity.isRemoved()) {
iterator.remove();
} else {
consumer.accept(entity);
}
}
}
@Override
public Collection<Entity> getEntities() {
final ImmutableList.Builder<Entity> entities = ImmutableList.builder();
forEachEntity(entities::add);
return entities.build();
}
@Override
public Collection<Entity> getEntities(Predicate<Entity> filter) {
return getEntities().stream().filter(filter).collect(GuavaCollectors.toImmutableList());
}
@Override
public Entity createEntity(EntityType type, Vector3d position) {
checkNotNull(position, "position");
final LanternEntityType entityType = (LanternEntityType) checkNotNull(type, "type");
//noinspection unchecked
final LanternEntity entity = (LanternEntity) entityType.getEntityConstructor().apply(UUID.randomUUID());
entity.setPositionAndWorld(this, position);
return entity;
}
@Override
public Optional<Entity> createEntity(DataContainer entityContainer) {
// TODO Auto-generated method stub
return Optional.empty();
}
@Override
public Optional<Entity> createEntity(DataContainer entityContainer, Vector3d position) {
// TODO Auto-generated method stub
return Optional.empty();
}
@Override
public boolean spawnEntities(Iterable<? extends Entity> entities, Cause cause) {
boolean spawned = true;
for (Entity entity : entities) {
spawned &= spawnEntity(entity, cause);
}
return spawned;
}
@Override
public Collection<TileEntity> getTileEntities() {
// TODO Auto-generated method stub
return Collections.emptyList();
}
@Override
public Collection<TileEntity> getTileEntities(Predicate<TileEntity> filter) {
// TODO Auto-generated method stub
return Collections.emptyList();
}
@Override
public Optional<TileEntity> getTileEntity(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getTileEntity(x, y, z);
}
@Override
public boolean containsBlock(int x, int y, int z) {
return VecHelper.inBounds(x, y, z, BLOCK_MIN, BLOCK_MAX);
}
@Override
public BlockState getBlock(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getBlock(x, y, z);
}
@Override
public BlockType getBlockType(int x, int y, int z) {
return getBlock(x, y, z).getType();
}
@Override
public void setBiome(int x, int y, int z, BiomeType biome) {
this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).setBiome(x, y, z, biome);
}
@Override
public Vector3i getBiomeMin() {
return BIOME_MIN;
}
@Override
public Vector3i getBiomeMax() {
return BIOME_MAX;
}
@Override
public Vector3i getBiomeSize() {
return BIOME_SIZE;
}
@Override
public Vector3i getBlockMin() {
return BLOCK_MIN;
}
@Override
public Vector3i getBlockMax() {
return BLOCK_MAX;
}
@Override
public Vector3i getBlockSize() {
return BLOCK_SIZE;
}
@Override
public boolean containsBiome(int x, int y, int z) {
return VecHelper.inBounds(x, y, z, BIOME_MIN, BIOME_MAX);
}
@Override
public BiomeType getBiome(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getBiome(x, y, z);
}
@Override
public boolean setBlock(int x, int y, int z, BlockState blockState, Cause cause) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).setBlock(x, y, z, blockState, cause);
}
@Override
public boolean setBlock(int x, int y, int z, BlockState blockState, BlockChangeFlag flag, Cause cause) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).setBlock(x, y, z, blockState, flag, cause);
}
@Override
public BlockSnapshot createSnapshot(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).createSnapshot(x, y, z);
}
@Override
public boolean restoreSnapshot(int x, int y, int z, BlockSnapshot snapshot, boolean force,
BlockChangeFlag flag, Cause cause) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4)
.restoreSnapshot(x, y, z, snapshot, force, flag, cause);
}
@Override
public boolean restoreSnapshot(BlockSnapshot snapshot, boolean force, BlockChangeFlag flag, Cause cause) {
final Vector3i pos = checkNotNull(snapshot, "snapshot").getPosition();
return this.chunkManager.getOrLoadChunk(pos.getX() >> 4, pos.getZ() >> 4)
.restoreSnapshot(pos.getX(), pos.getY(), pos.getZ(), snapshot, force, flag, cause);
}
@Override
public <T extends Property<?, ?>> Optional<T> getProperty(int x, int y, int z, Direction direction, Class<T> propertyClass) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getProperty(new Vector3i(x, y, z), direction, propertyClass);
}
@Override
public Collection<Direction> getFacesWithProperty(int x, int y, int z, Class<? extends Property<?, ?>> propertyClass) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getFacesWithProperty(x, y, z, propertyClass);
}
@Override
public <T extends Property<?, ?>> Optional<T> getProperty(int x, int y, int z, Class<T> propertyClass) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getProperty(x, y, z, propertyClass);
}
@Override
public Collection<Property<?, ?>> getProperties(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getProperties(x, y, z);
}
@Override
public <E> Optional<E> get(int x, int y, int z, Key<? extends BaseValue<E>> key) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).get(x, y, z, key);
}
@Override
public <T extends DataManipulator<?, ?>> Optional<T> get(int x, int y, int z, Class<T> manipulatorClass) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).get(x, y, z, manipulatorClass);
}
@Override
public <T extends DataManipulator<?, ?>> Optional<T> getOrCreate(int x, int y, int z, Class<T> manipulatorClass) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getOrCreate(x, y, z, manipulatorClass);
}
@Override
public <E> E getOrNull(int x, int y, int z, Key<? extends BaseValue<E>> key) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getOrNull(x, y, z, key);
}
@Override
public <E> E getOrElse(int x, int y, int z, Key<? extends BaseValue<E>> key, E defaultValue) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getOrElse(x, y, z, key, defaultValue);
}
@Override
public <E, V extends BaseValue<E>> Optional<V> getValue(int x, int y, int z, Key<V> key) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getValue(x, y, z, key);
}
@Override
public boolean supports(int x, int y, int z, Key<?> key) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).supports(x, y, z, key);
}
@Override
public boolean supports(int x, int y, int z, BaseValue<?> value) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).supports(x, y, z, value);
}
@Override
public boolean supports(int x, int y, int z, Class<? extends DataManipulator<?, ?>> manipulatorClass) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).supports(x, y, z, manipulatorClass);
}
@Override
public boolean supports(int x, int y, int z, DataManipulator<?, ?> manipulator) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).supports(x, y, z, manipulator);
}
@Override
public ImmutableSet<Key<?>> getKeys(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getKeys(x, y, z);
}
@Override
public ImmutableSet<ImmutableValue<?>> getValues(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getValues(x, y, z);
}
@Override
public <E> DataTransactionResult transform(int x, int y, int z, Key<? extends BaseValue<E>> key, Function<E, E> function) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).transform(x, y, z, key, function);
}
@Override
public <E> DataTransactionResult offer(int x, int y, int z, Key<? extends BaseValue<E>> key, E value) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).offer(x, y, z, key, value);
}
@Override
public <E> DataTransactionResult offer(int x, int y, int z, Key<? extends BaseValue<E>> key, E value, Cause cause) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).offer(x, y, z, key, value, cause);
}
@Override
public <E> DataTransactionResult offer(int x, int y, int z, BaseValue<E> value) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).offer(x, y, z, value);
}
@Override
public DataTransactionResult offer(int x, int y, int z, DataManipulator<?, ?> manipulator) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).offer(x, y, z, manipulator);
}
@Override
public DataTransactionResult offer(int x, int y, int z, DataManipulator<?, ?> manipulator, MergeFunction function) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).offer(x, y, z, manipulator, function);
}
@Override
public DataTransactionResult offer(int x, int y, int z, DataManipulator<?, ?> manipulator, MergeFunction function, Cause cause) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).offer(x, y, z, manipulator, function, cause);
}
@Override
public DataTransactionResult offer(int x, int y, int z, Iterable<DataManipulator<?, ?>> manipulators) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).offer(x, y, z, manipulators);
}
@Override
public DataTransactionResult offer(Vector3i coords, Iterable<DataManipulator<?, ?>> values, MergeFunction function) {
return this.chunkManager.getOrLoadChunk(coords.getX() >> 4, coords.getZ() >> 4).offer(coords, values, function);
}
@Override
public DataTransactionResult remove(int x, int y, int z, Class<? extends DataManipulator<?, ?>> manipulatorClass) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).remove(x, y, z, manipulatorClass);
}
@Override
public DataTransactionResult remove(int x, int y, int z, Key<?> key) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).remove(x, y, z, key);
}
@Override
public DataTransactionResult undo(int x, int y, int z, DataTransactionResult result) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).undo(x, y, z, result);
}
@Override
public DataTransactionResult copyFrom(int x, int y, int z, DataHolder from) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).copyFrom(x, y, z, from);
}
@Override
public DataTransactionResult copyFrom(int xTo, int yTo, int zTo, int xFrom, int yFrom, int zFrom) {
// TODO Auto-generated method stub
return null;
}
@Override
public DataTransactionResult copyFrom(int x, int y, int z, DataHolder from, MergeFunction function) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).copyFrom(x, y, z, from, function);
}
@Override
public DataTransactionResult copyFrom(int xTo, int yTo, int zTo, int xFrom, int yFrom, int zFrom, MergeFunction function) {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<DataManipulator<?, ?>> getManipulators(int x, int y, int z) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).getManipulators(x, y, z);
}
@Override
public boolean validateRawData(int x, int y, int z, DataView container) {
return this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).validateRawData(x, y, z, container);
}
@Override
public void setRawData(int x, int y, int z, DataView container) throws InvalidDataException {
this.chunkManager.getOrLoadChunk(x >> 4, z >> 4).setRawData(x, y, z, container);
}
@Override
public UUID getUniqueId() {
return this.properties.uniqueId;
}
@Override
public LanternWeather getWeather() {
if (this.weatherUniverse != null) {
return this.weatherUniverse.getWeather();
}
return (LanternWeather) Weathers.CLEAR;
}
@Override
public long getRemainingDuration() {
if (this.weatherUniverse != null) {
return this.weatherUniverse.getRemainingDuration();
}
// Will always be clear
return Long.MAX_VALUE;
}
@Override
public long getRunningDuration() {
if (this.weatherUniverse != null) {
return this.weatherUniverse.getRunningDuration();
}
// Will always be clear
return Long.MAX_VALUE;
}
@Override
public void setWeather(Weather weather) {
if (this.weatherUniverse != null) {
this.weatherUniverse.setWeather(weather);
}
}
@Override
public void setWeather(Weather weather, long duration) {
if (this.weatherUniverse != null) {
this.weatherUniverse.setWeather(weather, duration);
}
}
@Override
public void spawnParticles(ParticleEffect particleEffect, Vector3d position) {
checkNotNull(particleEffect, "particleEffect");
checkNotNull(position, "position");
this.spawnParticles(this.players.iterator(), particleEffect, position);
}
@Override
public void spawnParticles(ParticleEffect particleEffect, Vector3d position, int radius) {
checkNotNull(particleEffect, "particleEffect");
checkNotNull(position, "position");
this.spawnParticles(this.players.stream().filter(
player -> player.getLocation().getPosition().distanceSquared(position) < radius * radius).iterator(),
particleEffect, position);
}
@Override
public void playSound(SoundType sound, SoundCategory category, Vector3d position, double volume, double pitch, double minVolume) {
checkNotNull(sound, "sound");
checkNotNull(position, "position");
checkNotNull(category, "category");
this.broadcast(() -> ((LanternSoundType) sound).createMessage(position,
category, (float) Math.max(minVolume, volume), (float) pitch));
}
private void spawnParticles(Iterator<LanternPlayer> players, ParticleEffect particleEffect, Vector3d position) {
if (!players.hasNext()) {
return;
}
MessagePlayOutParticleEffect message = new MessagePlayOutParticleEffect(position, particleEffect);
while (players.hasNext()) {
players.next().getConnection().send(message);
}
}
@Override
public void sendMessage(Text message) {
this.sendMessage(ChatTypes.CHAT, message);
}
@Override
public MessageChannel getMessageChannel() {
return this.messageChannel;
}
@Override
public void setMessageChannel(MessageChannel channel) {
this.messageChannel = checkNotNull(channel, "channel");
}
@Override
public void sendMessage(ChatType type, Text message) {
checkNotNull(type, "chatType");
checkNotNull(message, "message");
if (!this.players.isEmpty()) {
final Map<Locale, Message> networkMessages = new HashMap<>();
for (LanternPlayer player : this.players) {
player.getConnection().send(networkMessages.computeIfAbsent(player.getLocale(),
locale -> ((LanternChatType) type).getMessageProvider().apply(message, locale)));
}
}
}
@Override
public void sendTitle(Title title) {
checkNotNull(title, "title");
if (!this.players.isEmpty()) {
final List<Message> networkMessages = LanternTitles.getMessages(title);
this.players.forEach(player -> player.getConnection().send(networkMessages));
}
}
@Override
public void sendBookView(BookView bookView) {
checkNotNull(bookView, "bookView");
this.players.forEach(player -> player.sendBookView(bookView));
}
@Override
public void sendBlockChange(int x, int y, int z, BlockState state) {
}
@Override
public void resetBlockChange(int x, int y, int z) {
}
@Override
public Context getContext() {
return this.worldContext;
}
@Override
public Location<World> getLocation(int x, int y, int z) {
return new Location<>(this, x, y, z);
}
@Override
public Location<World> getLocation(double x, double y, double z) {
return new Location<>(this, x, y, z);
}
@Override
public Difficulty getDifficulty() {
return this.properties.getDifficulty();
}
@Override
public String getName() {
return this.properties.getWorldName();
}
@Override
public Optional<Chunk> getChunk(Vector3i position) {
return Optional.ofNullable(this.chunkManager.getChunk(position.toVector2(true)));
}
@Override
public Optional<Chunk> getChunk(int x, int y, int z) {
return Optional.ofNullable(this.chunkManager.getChunk(x, z));
}
@Override
public Optional<Chunk> loadChunk(int x, int y, int z, boolean generate) {
if (!VecHelper.inBounds(x, y, z, SPACE_MIN, SPACE_MAX)) {
return Optional.empty();
}
if (generate) {
return Optional.of(this.chunkManager.getOrCreateChunk(new Vector2i(x, z),
() -> Cause.source(this.game.getMinecraftPlugin()).owner(this).build(), true));
} else {
return Optional.ofNullable(this.chunkManager.getChunk(x, z));
}
}
@Override
public boolean unloadChunk(Chunk chunk) {
return chunk.unloadChunk();
}
@Override
public Iterable<Chunk> getLoadedChunks() {
return this.chunkManager.getLoadedChunks();
}
@Override
public Optional<Entity> getEntity(UUID uuid) {
return Optional.ofNullable(this.entitiesByUniqueId.get(checkNotNull(uuid, "uuid")));
}
@Override
public boolean spawnEntity(Entity entity, Cause cause) {
checkNotNull(entity, "entity");
checkArgument(!entity.isRemoved(), "The entity may not be removed.");
checkArgument(entity.getWorld() == this, "The entity is not be located in this world.");
checkNotNull(cause, "cause");
final LanternEntity entity1 = addEntity((LanternEntity) entity);
if (entity1 != null) {
if (entity == entity1) {
throw new IllegalArgumentException("The entity is already spawned.");
} else {
throw new IllegalArgumentException("There is already a entity spawned with the unique id.");
}
}
final LanternEntity entity2 = (LanternEntity) entity;
final Vector3i position = entity2.getPosition().toInt();
final Vector3i chunkPos = new Vector3i(position.getX() >> 4, fixEntityYSection(position.getY() >> 4), position.getZ() >> 4);
final LanternChunk chunk = (LanternChunk) loadChunk(chunkPos.getX(), 0, chunkPos.getZ(), true).get();
chunk.addEntity(entity2, chunkPos.getY());
return true;
}
public void addEntities(Iterable<Entity> entities) {
for (Entity entity : entities) {
addEntity((LanternEntity) entity);
}
}
@Nullable
private LanternEntity addEntity(LanternEntity entity) {
LanternEntity entity1 = this.entitiesByUniqueId.putIfAbsent(entity.getUniqueId(), entity);
if (entity1 != null) {
return entity1;
}
final EntityProtocolType entityProtocolType = entity.getEntityProtocolType();
if (entityProtocolType != null) {
//noinspection unchecked
this.entityProtocolManager.add(entity, entityProtocolType);
}
entity.setPositionAndWorld(this, entity.getPosition());
return null;
}
private void pulseEntities() {
// Pulse the entities
final Iterator<LanternEntity> iterator = this.entitiesByUniqueId.values().iterator();
while (iterator.hasNext()) {
final LanternEntity entity = iterator.next();
if (entity.isRemoved()) {
final Vector3i lastChunk = entity.getLastChunkSectionCoords();
if (lastChunk != null && entity.getRemoveState() == LanternEntity.RemoveState.DESTROYED) {
final LanternChunk chunk = this.chunkManager.getChunkIfLoaded(lastChunk.getX(), lastChunk.getZ());
if (chunk != null) {
chunk.removeEntity(entity, lastChunk.getY() >> 4);
}
}
this.entityProtocolManager.remove(entity);
iterator.remove();
} else {
final Vector3i lastChunkSection = entity.getLastChunkSectionCoords();
entity.pulse();
final Vector3i pos = entity.getPosition().toInt();
final Vector3i newChunk = new Vector3i(pos.getX() >> 4, fixEntityYSection(pos.getY() >> 4), pos.getZ() >> 4);
if (lastChunkSection == null || !lastChunkSection.equals(newChunk)) {
LanternChunk chunk;
if (lastChunkSection != null && (chunk = this.chunkManager.getChunkIfLoaded(
lastChunkSection.getX(), lastChunkSection.getZ())) != null) {
chunk.removeEntity(entity, lastChunkSection.getY());
}
chunk = this.chunkManager.getOrLoadChunk(newChunk.getX(), newChunk.getZ());
chunk.addEntity(entity, newChunk.getY());
entity.setLastChunkCoords(newChunk);
}
}
}
}
@Override
public LanternWorldBorder getWorldBorder() {
return this.worldBorder;
}
@Override
public Optional<String> getGameRule(String gameRule) {
return this.properties.getGameRule(gameRule);
}
@Override
public Map<String, String> getGameRules() {
return this.properties.getGameRules();
}
@Override
public <T> Optional<Rule<T>> getRule(RuleType<T> ruleType) {
return this.properties.getRules().getRule(ruleType);
}
@Override
public <T> Rule<T> getOrCreateRule(RuleType<T> ruleType) {
return this.properties.getRules().getOrCreateRule(ruleType);
}
@Override
public Dimension getDimension() {
return this.dimension;
}
@Override
public WorldGenerator getWorldGenerator() {
return this.chunkManager.getWorldGenerator();
}
@Override
public boolean doesKeepSpawnLoaded() {
return this.properties.doesKeepSpawnLoaded();
}
@Override
public void setKeepSpawnLoaded(boolean keepLoaded) {
this.properties.setKeepSpawnLoaded(keepLoaded);
}
@Override
public WorldStorage getWorldStorage() {
return this.chunkManager.getChunkIOService();
}
@Override
public LanternWorldProperties getProperties() {
return this.properties;
}
@Override
public Path getDirectory() {
return this.directory;
}
@Override
public Location<World> getSpawnLocation() {
return new Location<>(this, this.properties.getSpawnPosition());
}
@Override
public void triggerExplosion(Explosion explosion, Cause cause) {
// TODO Auto-generated method stub
}
@Override
public Optional<Entity> restoreSnapshot(EntitySnapshot snapshot, Vector3d position) {
// TODO Auto-generated method stub
return null;
}
@Override
public PortalAgent getPortalAgent() {
return this.portalAgent;
}
@Override
public ChunkPreGenerate newChunkPreGenerate(Vector3d center, double diameter) {
return new LanternChunkPreGenerate(this, checkNotNull(center, "center"), diameter);
}
public void pulse() {
this.chunkManager.pulse();
this.timeUniverse.pulse();
if (this.weatherUniverse != null) {
this.weatherUniverse.pulse();
}
// Pulse the entities
pulseEntities();
// Pulse the tile entities
getLoadedChunks().forEach(chunk -> ((LanternChunk) chunk).pulse());
// TODO: Maybe async?
this.observedChunkManager.pulse();
this.entityProtocolManager.updateTrackers(this.players);
}
public void broadcast(Supplier<Message> message) {
this.broadcast(message, null);
}
public void broadcast(Supplier<Message> message, @Nullable Predicate<LanternPlayer> filter) {
Set<LanternPlayer> players = this.players;
if (filter != null) {
players = players.stream().filter(filter).collect(Collectors.toSet());
}
if (players.isEmpty()) {
return;
}
final Message message0 = message.get();
players.forEach(player -> player.getConnection().send(message0));
}
@Override
public boolean hitBlock(int x, int y, int z, Direction side, Cause cause) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean interactBlockWith(int x, int y, int z, ItemStack itemStack, Direction side, Cause cause) {
cause = Cause.builder().from(cause).named(Parameters.USED_ITEM_STACK.getName(), itemStack).build();
return interactBlock(x, y, z, side, cause, ctx -> ctx.set(Parameters.USED_ITEM_STACK, itemStack));
}
@Override
public boolean interactBlock(int x, int y, int z, Direction side, Cause cause) {
return interactBlock(x, y, z, side, cause, ctx -> {});
}
public boolean interactBlock(int x, int y, int z, Direction side, Cause cause, Consumer<BehaviorContext> consumer) {
final LanternBlockType blockType = ((LanternBlockType) getBlockType(x, y, z));
final BehaviorPipeline<Behavior> pipeline = blockType.getPipeline();
final BehaviorContextImpl context = new BehaviorContextImpl(cause);
context.set(Parameters.INTERACTION_FACE, side);
context.set(Parameters.BLOCK_LOCATION, new Location<>(this, x, y, z));
context.set(Parameters.BLOCK_TYPE, blockType);
consumer.accept(context);
// Just pass an object trough to make sure that a value is present when successful
return context.process(pipeline.pipeline(InteractWithBlockBehavior.class),
(ctx, behavior) -> behavior.tryInteract(pipeline, ctx));
}
@Override
public boolean placeBlock(int x, int y, int z, BlockState block, Direction side, Cause cause) {
cause = Cause.builder().from(cause).named(Parameters.USED_BLOCK_STATE.getName(), block).build();
final LanternBlockType blockType = ((LanternBlockType) getBlockType(x, y, z));
final BehaviorPipeline<Behavior> pipeline = blockType.getPipeline();
final BehaviorContextImpl context = new BehaviorContextImpl(cause);
context.set(Parameters.INTERACTION_FACE, side);
context.set(Parameters.BLOCK_LOCATION, new Location<>(this, x, y, z));
context.set(Parameters.BLOCK_TYPE, blockType);
// Just pass an object trough to make sure that a value is present when successful
return context.process(pipeline.pipeline(PlaceBlockBehavior.class),
(ctx, behavior) -> behavior.tryPlace(pipeline, ctx));
}
@Override
public boolean digBlock(int x, int y, int z, Cause cause) {
return false;
}
@Override
public boolean digBlockWith(int x, int y, int z, ItemStack itemStack, Cause cause) {
cause = Cause.builder().from(cause).named(Parameters.USED_ITEM_STACK.getName(), itemStack).build();
return digBlock(x, y, z, cause);
}
@Override
public int getBlockDigTimeWith(int x, int y, int z, ItemStack itemStack, Cause cause) {
// TODO Auto-generated method stub
return 0;
}
public EntityProtocolManager getEntityProtocolManager() {
return this.entityProtocolManager;
}
public MultiWorldEventListener getEventListener() {
return this.worldEventListener;
}
public void addBlockAction(Vector3i position, BlockType blockType, BlockAction blockAction) {
addBlockAction(position.getX(), position.getY(), position.getZ(), blockType, blockAction);
}
public void addBlockAction(int x, int y, int z, BlockType blockType, BlockAction blockAction) {
final LanternChunk chunk = this.chunkManager.getChunkIfLoaded(x >> 4, z >> 4);
if (chunk != null) {
chunk.addBlockAction(x, y, z, blockType, blockAction);
}
}
}