diff --git a/src/main/java/org/lanternpowered/server/data/key/LanternKeys.java b/src/main/java/org/lanternpowered/server/data/key/LanternKeys.java index a665ec866..e30eb9dfb 100644 --- a/src/main/java/org/lanternpowered/server/data/key/LanternKeys.java +++ b/src/main/java/org/lanternpowered/server/data/key/LanternKeys.java @@ -140,6 +140,22 @@ public final class LanternKeys { public static final Key> DUMMY = makeValueKey(Boolean.class, DataQuery.of("Dummy"), "dummy"); + /////////////////// + /// Boats /// + /////////////////// + + public static final Key> MAX_SPEED = + makeValueKey(Double.class, DataQuery.of("MaxSpeed"), "max_speed"); + + public static final Key> CAN_MOVE_ON_LAND = + makeValueKey(Boolean.class, DataQuery.of("CanMoveOnLand"), "can_move_on_land"); + + public static final Key> OCCUPIED_DECELERATION = + makeValueKey(Double.class, DataQuery.of("OccupiedDeceleration"), "occupied_deceleration"); + + public static final Key> UNOCCUPIED_DECELERATION = + makeValueKey(Double.class, DataQuery.of("UnoccupiedDeceleration"), "unoccupied_deceleration"); + private LanternKeys() { } } diff --git a/src/main/java/org/lanternpowered/server/effect/entity/animation/DefaultLivingHurtAnimation.java b/src/main/java/org/lanternpowered/server/effect/entity/animation/DefaultLivingHurtAnimation.java index 2ad513e0c..131852708 100644 --- a/src/main/java/org/lanternpowered/server/effect/entity/animation/DefaultLivingHurtAnimation.java +++ b/src/main/java/org/lanternpowered/server/effect/entity/animation/DefaultLivingHurtAnimation.java @@ -27,7 +27,7 @@ import org.lanternpowered.server.effect.entity.EntityEffect; import org.lanternpowered.server.entity.LanternEntity; -import org.lanternpowered.server.entity.event.DamagedEntityEvent; +import org.lanternpowered.api.entity.event.animation.DamageEntityAnimation; /** * Plays a entity hurt animation. This will make a @@ -37,6 +37,6 @@ public class DefaultLivingHurtAnimation implements EntityEffect { @Override public void play(LanternEntity entity) { - entity.triggerEvent(DamagedEntityEvent.of()); + entity.getShardeventBus().post(DamageEntityAnimation.INSTANCE); } } diff --git a/src/main/java/org/lanternpowered/server/entity/LanternEntity.java b/src/main/java/org/lanternpowered/server/entity/LanternEntity.java index 626ea9dd5..00bce4c22 100644 --- a/src/main/java/org/lanternpowered/server/entity/LanternEntity.java +++ b/src/main/java/org/lanternpowered/server/entity/LanternEntity.java @@ -42,12 +42,12 @@ import org.lanternpowered.server.data.key.LanternKeys; import org.lanternpowered.server.data.property.AbstractPropertyHolder; import org.lanternpowered.server.effect.entity.EntityEffectCollection; -import org.lanternpowered.server.entity.event.EntityEvent; +import org.lanternpowered.server.entity.interfaces.IEntity; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.lanternpowered.server.event.LanternEventContextKeys; import org.lanternpowered.server.game.LanternGame; import org.lanternpowered.server.game.registry.type.entity.EntityTypeRegistryModule; -import org.lanternpowered.server.network.entity.EntityProtocolType; +import org.lanternpowered.server.shards.AbstractComponentHolder; import org.lanternpowered.server.text.LanternTexts; import org.lanternpowered.server.util.Quaternions; import org.lanternpowered.server.world.LanternWorld; @@ -108,7 +108,7 @@ import javax.annotation.Nullable; -public class LanternEntity implements Entity, IAdditionalDataHolder, AbstractPropertyHolder { +public class LanternEntity extends AbstractComponentHolder implements IEntity, IAdditionalDataHolder, AbstractPropertyHolder { @SuppressWarnings("unused") private static boolean bypassEntityTypeLookup; @@ -143,11 +143,6 @@ public class LanternEntity implements Entity, IAdditionalDataHolder, AbstractPro */ private EntityEffectCollection effectCollection = EntityEffectCollection.build(); - /** - * The entity protocol type of this entity. - */ - @Nullable private EntityProtocolType entityProtocolType; - /** * The state of the removal of this entity. */ @@ -257,19 +252,6 @@ public Direction getHorizontalDirection(Direction.Division division) { return Direction.getClosest(getHorizontalDirectionVector(), division); } - @Nullable - public EntityProtocolType getEntityProtocolType() { - return this.entityProtocolType; - } - - public void setEntityProtocolType(@Nullable EntityProtocolType entityProtocolType) { - if (entityProtocolType != null) { - checkArgument(entityProtocolType.getEntityType().isInstance(this), - "The protocol type %s is not applicable to this entity."); - } - this.entityProtocolType = entityProtocolType; - } - /** * Gets whether this {@link Entity} is dead, should * only be implemented by a {@link Living}. @@ -296,6 +278,16 @@ void postDestructEvent(DestructEntityEvent event) { } } + /** + * Gets whether this {@link LanternEntity} currently + * exists in a {@link LanternWorld}. + * + * @return Exists in world + */ + public boolean existsInWorld() { + return this.world != null; + } + @Override public boolean isRemoved() { return this.removeState != null; @@ -871,15 +863,6 @@ public EntityArchetype createArchetype() { return null; } - /** - * Triggers the {@link EntityEvent} for this entity. - * - * @param event The event - */ - public void triggerEvent(EntityEvent event) { - getWorld().getEntityProtocolManager().triggerEvent(this, event); - } - /** * Gets the {@link SoundCategory} of this entity. * diff --git a/src/main/java/org/lanternpowered/server/entity/LanternHumanoid.java b/src/main/java/org/lanternpowered/server/entity/LanternHumanoid.java index 95f0d534a..8aa21d1d9 100644 --- a/src/main/java/org/lanternpowered/server/entity/LanternHumanoid.java +++ b/src/main/java/org/lanternpowered/server/entity/LanternHumanoid.java @@ -26,12 +26,12 @@ package org.lanternpowered.server.entity; import org.lanternpowered.server.data.key.LanternKeys; -import org.spongepowered.api.entity.living.Humanoid; +import org.lanternpowered.server.entity.interfaces.living.IHumanoid; import java.util.HashSet; import java.util.UUID; -public abstract class LanternHumanoid extends LanternLiving implements Humanoid, AbstractArmorEquipable { +public abstract class LanternHumanoid extends LanternLiving implements IHumanoid { public LanternHumanoid(UUID uniqueId) { super(uniqueId); diff --git a/src/main/java/org/lanternpowered/server/entity/LanternItem.java b/src/main/java/org/lanternpowered/server/entity/LanternItem.java index e0529273d..365031284 100644 --- a/src/main/java/org/lanternpowered/server/entity/LanternItem.java +++ b/src/main/java/org/lanternpowered/server/entity/LanternItem.java @@ -32,7 +32,8 @@ import org.lanternpowered.server.effect.entity.EntityEffectCollection; import org.lanternpowered.server.effect.entity.EntityEffectTypes; import org.lanternpowered.server.effect.entity.particle.item.ItemDeathParticleEffect; -import org.lanternpowered.server.entity.event.CollectEntityEvent; +import org.lanternpowered.server.entity.shard.NetworkShard; +import org.lanternpowered.api.entity.event.animation.CollectEntityAnimation; import org.lanternpowered.server.event.LanternEventContextKeys; import org.lanternpowered.server.inventory.IInventory; import org.lanternpowered.server.inventory.LanternItemStack; @@ -80,7 +81,7 @@ public class LanternItem extends LanternEntity implements Item { public LanternItem(UUID uniqueId) { super(uniqueId); - setEntityProtocolType(EntityProtocolTypes.ITEM); + addShard(NetworkShard.class).get().setEntityProtocolType(EntityProtocolTypes.ITEM); setBoundingBoxBase(BOUNDING_BOX_BASE); setEffectCollection(DEFAULT_EFFECT_COLLECTION.copy()); } @@ -229,7 +230,7 @@ private void tryToPickupItems() { .forEach(transaction -> transaction.getSlot().set(transaction.getFinal().createStack())); final int added = originalStack.getQuantity() - stack.getQuantity(); if (added != 0 && entity instanceof Living) { - triggerEvent(new CollectEntityEvent((Living) entity, added)); + getShardeventBus().post(new CollectEntityAnimation((Living) entity, added)); } if (isRemoved()) { stack.clear(); diff --git a/src/main/java/org/lanternpowered/server/entity/LanternLiving.java b/src/main/java/org/lanternpowered/server/entity/LanternLiving.java index fee5d3fe3..6d10303f3 100644 --- a/src/main/java/org/lanternpowered/server/entity/LanternLiving.java +++ b/src/main/java/org/lanternpowered/server/entity/LanternLiving.java @@ -40,6 +40,7 @@ import org.lanternpowered.server.effect.entity.sound.DefaultLivingFallSoundEffect; import org.lanternpowered.server.effect.entity.sound.DefaultLivingSoundEffect; import org.lanternpowered.server.effect.potion.LanternPotionEffectType; +import org.lanternpowered.server.entity.interfaces.living.ILiving; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.lanternpowered.server.game.LanternGame; import org.lanternpowered.server.util.collect.Lists2; @@ -87,7 +88,7 @@ import java.util.stream.Collectors; @SuppressWarnings("ConstantConditions") -public class LanternLiving extends LanternEntity implements Living { +public class LanternLiving extends LanternEntity implements ILiving { public static final EntityEffectCollection DEFAULT_EFFECT_COLLECTION = EntityEffectCollection.builder() .add(EntityEffectTypes.HURT, new DefaultLivingSoundEffect(EntityBodyPosition.HEAD, SoundTypes.ENTITY_GENERIC_HURT)) diff --git a/src/main/java/org/lanternpowered/server/entity/event/SwingHandEntityEvent.java b/src/main/java/org/lanternpowered/server/entity/event/SwingHandEntityEvent.java deleted file mode 100644 index c89948be7..000000000 --- a/src/main/java/org/lanternpowered/server/entity/event/SwingHandEntityEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of LanternServer, licensed under the MIT License (MIT). - * - * Copyright (c) LanternPowered - * Copyright (c) SpongePowered - * 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.entity.event; - -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkNotNull; - -import org.spongepowered.api.data.type.HandType; -import org.spongepowered.api.data.type.HandTypes; - -public final class SwingHandEntityEvent implements EntityEvent { - - public static SwingHandEntityEvent of(HandType handType) { - checkNotNull(handType, "handType"); - return handType == HandTypes.MAIN_HAND ? Holder.MAIN_HAND : - handType == HandTypes.OFF_HAND ? Holder.OFF_HAND : new SwingHandEntityEvent(handType); - } - - private final HandType handType; - - private SwingHandEntityEvent(HandType handType) { - this.handType = handType; - } - - public HandType getHandType() { - return this.handType; - } - - @Override - public EntityEventType type() { - return EntityEventType.ALIVE; - } - - @Override - public String toString() { - return toStringHelper(this).add("handType", this.handType.getKey()).toString(); - } - - private static class Holder { - static final SwingHandEntityEvent MAIN_HAND = new SwingHandEntityEvent(HandTypes.MAIN_HAND); - static final SwingHandEntityEvent OFF_HAND = new SwingHandEntityEvent(HandTypes.OFF_HAND); - } -} diff --git a/src/main/java/org/lanternpowered/server/entity/living/player/LanternPlayer.java b/src/main/java/org/lanternpowered/server/entity/living/player/LanternPlayer.java index a232e7684..ba659dbdd 100644 --- a/src/main/java/org/lanternpowered/server/entity/living/player/LanternPlayer.java +++ b/src/main/java/org/lanternpowered/server/entity/living/player/LanternPlayer.java @@ -50,7 +50,8 @@ import org.lanternpowered.server.effect.sound.LanternSoundType; import org.lanternpowered.server.entity.EntityBodyPosition; import org.lanternpowered.server.entity.LanternLiving; -import org.lanternpowered.server.entity.event.SpectateEntityEvent; +import org.lanternpowered.server.entity.shard.NetworkShard; +import org.lanternpowered.server.entity.event.SpectateEntityShardevent; import org.lanternpowered.server.entity.living.player.gamemode.LanternGameMode; import org.lanternpowered.server.entity.living.player.tab.GlobalTabList; import org.lanternpowered.server.entity.living.player.tab.GlobalTabListEntry; @@ -297,6 +298,8 @@ public LanternPlayer(LanternGameProfile gameProfile, NetworkSession session) { setBoundingBoxBase(BOUNDING_BOX_BASE); // Attach this player to the proxy user and load player data getProxyUser().setInternalUser(this); + // Set the fastest update rate + getShard(NetworkShard.class).ifPresent(networkComponent -> networkComponent.setTrackingUpdateRate(1)); } public Set getBossBars() { @@ -1130,7 +1133,7 @@ public Optional getSpectatorTarget() { @Override public void setSpectatorTarget(@Nullable Entity entity) { this.spectatorEntity = entity; - triggerEvent(new SpectateEntityEvent(entity)); + getShardeventBus().post(new SpectateEntityShardevent(entity)); } @Override diff --git a/src/main/java/org/lanternpowered/server/entity/living/player/PlayerInteractionHandler.java b/src/main/java/org/lanternpowered/server/entity/living/player/PlayerInteractionHandler.java index 799e7efe1..a3281e347 100644 --- a/src/main/java/org/lanternpowered/server/entity/living/player/PlayerInteractionHandler.java +++ b/src/main/java/org/lanternpowered/server/entity/living/player/PlayerInteractionHandler.java @@ -36,7 +36,7 @@ import org.lanternpowered.server.block.behavior.types.BreakBlockBehavior; import org.lanternpowered.server.block.behavior.types.InteractWithBlockBehavior; import org.lanternpowered.server.data.key.LanternKeys; -import org.lanternpowered.server.entity.event.SwingHandEntityEvent; +import org.lanternpowered.api.entity.event.animation.SwingHandAnimation; import org.lanternpowered.server.game.Lantern; import org.lanternpowered.server.game.LanternGame; import org.lanternpowered.server.inventory.AbstractSlot; @@ -382,11 +382,16 @@ public void handleBlockPlacing(MessagePlayInPlayerBlockPlacement message) { } } + static class SwingHandEntityShardevents { + static final SwingHandAnimation MAIN_HAND = new SwingHandAnimation(HandTypes.MAIN_HAND); + static final SwingHandAnimation OFF_HAND = new SwingHandAnimation(HandTypes.OFF_HAND); + } + public void handleSwingArm(MessagePlayInPlayerSwingArm message) { if (message.getHandType() == HandTypes.OFF_HAND) { return; } - this.player.triggerEvent(SwingHandEntityEvent.of(HandTypes.MAIN_HAND)); + this.player.getShardeventBus().post(SwingHandEntityShardevents.MAIN_HAND); } public void handleFinishItemInteraction(MessagePlayInOutFinishUsingItem message) { @@ -488,7 +493,7 @@ public void handleItemInteraction(MessagePlayInPlayerUseItem message) { } */ this.player.getConnection().send(new MessagePlayOutEntityAnimation(this.player.getNetworkId(), 3)); - this.player.triggerEvent(SwingHandEntityEvent.of(HandTypes.OFF_HAND)); + this.player.getShardeventBus().post(SwingHandEntityShardevents.OFF_HAND); /* final CooldownTracker cooldownTracker = this.player.getCooldownTracker(); cooldownTracker.set(handItem.get().getType(), 15); diff --git a/src/main/java/org/lanternpowered/server/entity/weather/LanternLightning.java b/src/main/java/org/lanternpowered/server/entity/weather/LanternLightning.java index 6644977e8..30dc9f7c5 100644 --- a/src/main/java/org/lanternpowered/server/entity/weather/LanternLightning.java +++ b/src/main/java/org/lanternpowered/server/entity/weather/LanternLightning.java @@ -31,7 +31,9 @@ import org.lanternpowered.server.effect.entity.EntityEffectTypes; import org.lanternpowered.server.effect.entity.sound.weather.LightningSoundEffect; import org.lanternpowered.server.entity.LanternEntity; +import org.lanternpowered.server.entity.shard.NetworkShard; import org.lanternpowered.server.game.registry.type.cause.DamageTypeRegistryModule; +import org.lanternpowered.server.entity.interfaces.weather.ILightning; import org.lanternpowered.server.network.entity.EntityProtocolTypes; import org.lanternpowered.server.world.LanternWorld; import org.spongepowered.api.Sponge; @@ -51,7 +53,7 @@ import java.util.List; import java.util.UUID; -public class LanternLightning extends LanternEntity implements AbstractLightning { +public class LanternLightning extends LanternEntity implements ILightning { public static final EntityEffectCollection DEFAULT_SOUND_COLLECTION = EntityEffectCollection.builder() .add(EntityEffectTypes.LIGHTNING, new LightningSoundEffect()) @@ -70,9 +72,12 @@ public class LanternLightning extends LanternEntity implements AbstractLightning public LanternLightning(UUID uniqueId) { super(uniqueId); - setEntityProtocolType(EntityProtocolTypes.LIGHTNING); setEffectCollection(DEFAULT_SOUND_COLLECTION.copy()); setSoundCategory(SoundCategories.WEATHER); + + final NetworkShard networkComponent = addShard(NetworkShard.class).get(); + networkComponent.setEntityProtocolType(EntityProtocolTypes.LIGHTNING); + networkComponent.setTrackingRange(512); } @Override diff --git a/src/main/java/org/lanternpowered/server/entity/event/EntityEvent.java b/src/main/java/org/lanternpowered/server/entity/weather/package-info.java similarity index 92% rename from src/main/java/org/lanternpowered/server/entity/event/EntityEvent.java rename to src/main/java/org/lanternpowered/server/entity/weather/package-info.java index f9b550ebb..cdb5744b5 100644 --- a/src/main/java/org/lanternpowered/server/entity/event/EntityEvent.java +++ b/src/main/java/org/lanternpowered/server/entity/weather/package-info.java @@ -23,9 +23,5 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.event; - -public interface EntityEvent { - - EntityEventType type(); -} +@org.spongepowered.api.util.annotation.NonnullByDefault +package org.lanternpowered.server.entity.weather; diff --git a/src/main/java/org/lanternpowered/server/game/LanternGame.java b/src/main/java/org/lanternpowered/server/game/LanternGame.java index ee60aedfd..f7a983284 100644 --- a/src/main/java/org/lanternpowered/server/game/LanternGame.java +++ b/src/main/java/org/lanternpowered/server/game/LanternGame.java @@ -53,6 +53,7 @@ import org.lanternpowered.server.config.user.ban.BanConfig; import org.lanternpowered.server.data.LanternDataManager; import org.lanternpowered.server.data.property.LanternPropertyRegistry; +import org.lanternpowered.server.entity.shard.DefaultBossShard; import org.lanternpowered.server.event.LanternEventManager; import org.lanternpowered.server.game.version.LanternMinecraftVersion; import org.lanternpowered.server.game.version.MinecraftVersionCache; @@ -70,6 +71,7 @@ import org.lanternpowered.server.service.permission.LanternPermissionService; import org.lanternpowered.server.service.sql.LanternSqlService; import org.lanternpowered.server.service.user.LanternUserStorageService; +import org.lanternpowered.server.shards.internal.ComponentType; import org.lanternpowered.server.world.chunk.LanternChunkTicketManager; import org.slf4j.Logger; import org.spongepowered.api.Game; @@ -260,6 +262,8 @@ public void initialize() throws IOException { throw new RuntimeException("An error occurred while loading the plugins.", e); } + System.out.println("BOSS COMPONENT: " + ComponentType.get(DefaultBossShard.class)); + this.gameRegistry.registerDefaults(); this.gameRegistry.earlyRegistry(); diff --git a/src/main/java/org/lanternpowered/server/game/LanternGameRegistry.java b/src/main/java/org/lanternpowered/server/game/LanternGameRegistry.java index abc5cc31e..1a6010b81 100644 --- a/src/main/java/org/lanternpowered/server/game/LanternGameRegistry.java +++ b/src/main/java/org/lanternpowered/server/game/LanternGameRegistry.java @@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; import com.google.inject.Singleton; +import org.jetbrains.annotations.NotNull; import org.lanternpowered.api.catalog.CatalogKeys; import org.lanternpowered.api.cause.CauseStack; import org.lanternpowered.api.item.enchantment.EnchantmentTypeBuilder; @@ -43,6 +44,7 @@ import org.lanternpowered.api.script.function.value.DoubleValueProviderType; import org.lanternpowered.api.script.function.value.FloatValueProviderType; import org.lanternpowered.api.script.function.value.IntValueProviderType; +import org.lanternpowered.api.shard.ShardRegistry; import org.lanternpowered.api.x.XGameRegistry; import org.lanternpowered.api.x.text.XTextFactory; import org.lanternpowered.server.advancement.LanternAdvancementBuilder; @@ -271,6 +273,7 @@ import org.lanternpowered.server.script.function.value.DoubleValueProviderTypeRegistryModule; import org.lanternpowered.server.script.function.value.FloatValueProviderTypeRegistryModule; import org.lanternpowered.server.script.function.value.IntValueProviderTypeRegistryModule; +import org.lanternpowered.server.shard.LanternShardRegistry; import org.lanternpowered.server.statistic.builder.BlockStatisticBuilder; import org.lanternpowered.server.statistic.builder.EntityStatisticBuilder; import org.lanternpowered.server.statistic.builder.ItemStatisticBuilder; @@ -1325,4 +1328,9 @@ public LanternSelectorFactory getSelectorFactory() { public Locale getLocale(String locale) { return LocaleCache.get(locale); } + + @Override + public ShardRegistry getShardRegistry() { + return LanternShardRegistry.INSTANCE; + } } diff --git a/src/main/java/org/lanternpowered/server/network/NetworkSession.java b/src/main/java/org/lanternpowered/server/network/NetworkSession.java index 0f92d3c2c..1b4a2c93d 100644 --- a/src/main/java/org/lanternpowered/server/network/NetworkSession.java +++ b/src/main/java/org/lanternpowered/server/network/NetworkSession.java @@ -50,6 +50,7 @@ import org.lanternpowered.server.LanternServer; import org.lanternpowered.server.config.world.WorldConfig; import org.lanternpowered.server.entity.LanternEntity; +import org.lanternpowered.server.entity.shard.NetworkShard; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.lanternpowered.server.entity.living.player.tab.GlobalTabList; import org.lanternpowered.server.game.Lantern; @@ -889,7 +890,8 @@ public void initPlayer() { * the server. */ private void finalizePlayer() { - this.player.setEntityProtocolType(EntityProtocolTypes.PLAYER); + final NetworkShard networkComponent = this.player.addShard(NetworkShard.class).get(); + networkComponent.setEntityProtocolType(EntityProtocolTypes.PLAYER); LanternWorld world = this.player.getWorld(); if (world == null) { diff --git a/src/main/java/org/lanternpowered/server/network/entity/AbstractEntityProtocol.java b/src/main/java/org/lanternpowered/server/network/entity/AbstractEntityProtocol.java index 723b7b763..f7e32060a 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/AbstractEntityProtocol.java +++ b/src/main/java/org/lanternpowered/server/network/entity/AbstractEntityProtocol.java @@ -34,12 +34,13 @@ import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import org.lanternpowered.server.entity.LanternEntity; -import org.lanternpowered.server.entity.event.EntityEvent; -import org.lanternpowered.server.entity.event.EntityEventType; +import org.lanternpowered.server.entity.shard.NetworkShard; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.lanternpowered.server.network.message.Message; +import org.lanternpowered.api.shard.event.Shardevent; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.util.Tuple; import java.util.ArrayList; import java.util.Collections; @@ -74,20 +75,15 @@ public abstract class AbstractEntityProtocol { private int entityId = INVALID_ENTITY_ID; /** - * The amount of ticks between every update. + * The {@link NetworkShard} will provide + * settings related to tracking range and update rate. */ - private int tickRate = 4; - - /** - * The tracking range of the entity. - */ - private double trackingRange = 64; + NetworkShard networkComponent; private int tickCounter = 0; final Object2LongMap playerInteractTimes = new Object2LongOpenHashMap<>(); - - final List entityEvents = new ArrayList<>(); + final List> entityEvents = new ArrayList<>(); public AbstractEntityProtocol(E entity) { this.entity = entity; @@ -165,40 +161,8 @@ protected int getRootEntityId() { return this.entityId; } - /** - * Sets the tick rate of this entity protocol. - * - * @param tickRate The tick rate - */ - public void setTickRate(int tickRate) { - this.tickRate = tickRate; - } - - /** - * Gets the tick rate of this entity protocol. - * - * @return The tick rate - */ - public int getTickRate() { - return this.tickRate; - } - - /** - * Gets the tracking range of the entity. - * - * @return The tracking range - */ - public double getTrackingRange() { - return this.trackingRange; - } - - /** - * Sets the tracking range of the entity. - * - * @param trackingRange The tracking range - */ - public void setTrackingRange(double trackingRange) { - this.trackingRange = trackingRange; + protected NetworkShard getNetworkComponent() { + return this.networkComponent; } /** @@ -214,7 +178,7 @@ void destroy(EntityProtocolInitContext context) { final TempEvents events = processEvents(true, true); ctx.trackers = this.trackers; if (events != null && events.deathOrAlive != null) { - events.deathOrAlive.forEach(event -> handleEvent(ctx, event)); + events.deathOrAlive.forEach(entry -> handleEvent(ctx, entry.getFirst())); } destroy(ctx); this.trackers.clear(); @@ -299,7 +263,7 @@ TrackerUpdateContextData buildUpdateContextData(Set players) { } } - boolean flag0 = this.tickCounter++ % this.tickRate == 0 && !this.trackers.isEmpty(); + boolean flag0 = this.tickCounter++ % this.networkComponent.getTrackingUpdateRate() == 0 && !this.trackers.isEmpty(); boolean flag1 = !added.isEmpty(); boolean flag2 = !removed.isEmpty(); @@ -327,7 +291,7 @@ void updateTrackers(TrackerUpdateContextData contextData) { if (contextData.removed != null) { ctx.trackers = contextData.removed; if (events != null && events.deathOrAlive != null) { - events.deathOrAlive.forEach(event -> handleEvent(ctx, event)); + events.deathOrAlive.forEach(entry -> handleEvent(ctx, entry.getFirst())); } destroy(ctx); synchronized (this.playerInteractTimes) { @@ -355,7 +319,7 @@ void updateTrackers(TrackerUpdateContextData contextData) { } if (trackers != null) { ctx.trackers = trackers; - events.alive.forEach(event -> handleEvent(ctx, event)); + events.alive.forEach(entry -> handleEvent(ctx, entry.getFirst())); } } @@ -370,10 +334,11 @@ void updateTrackerLocale(LanternPlayer player) { private final class TempEvents { - @Nullable private final List deathOrAlive; - private final List alive; + @Nullable private final List> deathOrAlive; + private final List> alive; - private TempEvents(@Nullable List deathOrAlive, List alive) { + private TempEvents(@Nullable List> deathOrAlive, + List> alive) { this.deathOrAlive = deathOrAlive; this.alive = alive; } @@ -384,7 +349,7 @@ private TempEvents processEvents(boolean death, boolean alive) { if (!death && !alive) { return null; } - List aliveList = null; + List> aliveList = null; synchronized (this.entityEvents) { if (!this.entityEvents.isEmpty()) { aliveList = new ArrayList<>(this.entityEvents); @@ -394,10 +359,10 @@ private TempEvents processEvents(boolean death, boolean alive) { if (aliveList == null) { return null; } - List deathOrAliveList = null; + List> deathOrAliveList = null; if (death) { - for (EntityEvent event : aliveList) { - if (event.type() == EntityEventType.DEATH_OR_ALIVE) { + for (Tuple event : aliveList) { + if (event.getSecond() == EntityProtocolShardeventType.DEATH_OR_ALIVE) { if (deathOrAliveList == null) { deathOrAliveList = new ArrayList<>(); } @@ -421,7 +386,8 @@ void postUpdateTrackers(TrackerUpdateContextData contextData) { } private boolean isVisible(Vector3d pos, LanternPlayer tracker) { - return pos.distanceSquared(tracker.getPosition()) < this.trackingRange * this.trackingRange && isVisible(tracker); + final double trackingRange = this.networkComponent.getTrackingRange(); + return pos.distanceSquared(tracker.getPosition()) < trackingRange * trackingRange && isVisible(tracker); } /** @@ -462,7 +428,7 @@ protected boolean isVisible(LanternPlayer tracker) { */ protected abstract void updateTranslations(EntityProtocolUpdateContext context); - protected void handleEvent(EntityProtocolUpdateContext context, EntityEvent event) { + protected void handleEvent(EntityProtocolUpdateContext context, Shardevent event) { } /** @@ -503,4 +469,17 @@ protected void playerInteract(LanternPlayer player, int entityId, @Nullable Vect */ protected void playerAttack(LanternPlayer player, int entityId) { } + + /** + * Adds a {@link Shardevent} that will + * be handled within the new protocol update. + * + * @param event The event + * @param type The type + */ + public void addEvent(Shardevent event, EntityProtocolShardeventType type) { + synchronized (this.entityEvents) { + this.entityEvents.add(new Tuple<>(event, type)); + } + } } diff --git a/src/main/java/org/lanternpowered/server/network/entity/EntityProtocolManager.java b/src/main/java/org/lanternpowered/server/network/entity/EntityProtocolManager.java index 0a29ca2dd..1c2f11f07 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/EntityProtocolManager.java +++ b/src/main/java/org/lanternpowered/server/network/entity/EntityProtocolManager.java @@ -34,7 +34,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.lanternpowered.server.entity.LanternEntity; -import org.lanternpowered.server.entity.event.EntityEvent; +import org.lanternpowered.server.entity.shard.NetworkShard; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.text.Text; @@ -254,16 +254,6 @@ public int getProtocolId(LanternEntity entity) { return INVALID_ENTITY_ID; } - /** - * Adds the {@link Entity} to be tracked. - * - * @param entity The entity - */ - public void add(LanternEntity entity) { - //noinspection ConstantConditions,unchecked - add(entity, (EntityProtocolType) entity.getEntityProtocolType()); - } - /** * Adds the {@link Entity} to be tracked with a specific {@link EntityProtocolType}. * @@ -271,12 +261,15 @@ public void add(LanternEntity entity) { * already a protocol.

* * @param entity The entity - * @param protocolType The protocol type + * @param networkComponent The network component */ - public void add(E entity, EntityProtocolType protocolType) { + @SuppressWarnings("unchecked") + public AbstractEntityProtocol add(E entity, NetworkShard networkComponent) { checkNotNull(entity, "entity"); - checkNotNull(protocolType, "protocolType"); - final AbstractEntityProtocol entityProtocol = protocolType.getSupplier().apply(entity); + checkNotNull(networkComponent, "networkComponent"); + final AbstractEntityProtocol entityProtocol = + ((LanternEntityProtocolType) networkComponent.getEntityProtocolType()).getSupplier().apply(entity); + entityProtocol.networkComponent = networkComponent; entityProtocol.entityProtocolManager = this; final AbstractEntityProtocol removed = this.entityProtocols.put(entity, entityProtocol); if (removed != null) { @@ -291,6 +284,7 @@ public void add(E entity, EntityProtocolType protoc allocatorLock.unlockWrite(stamp); } } + return entityProtocol; } /** @@ -312,6 +306,7 @@ public void remove(LanternEntity entity) { * * @param players The players */ + @SuppressWarnings("unchecked") public void updateTrackers(Set players) { // TODO: Sync the updates in a different thread? if (this.pulseCounter++ % UPDATE_RATE != 0) { @@ -330,7 +325,6 @@ public void updateTrackers(Set players) { synchronized (protocol.trackers) { final AbstractEntityProtocol.TrackerUpdateContextData contextData = protocol.buildUpdateContextData(players); if (contextData != null) { - //noinspection unchecked protocol.updateTrackers(contextData); updateContextDataList.add(contextData); } @@ -383,12 +377,4 @@ private void playerUseEntity(LanternPlayer player, int entityId, } }); } - - public void triggerEvent(LanternEntity entity, EntityEvent event) { - getEntityProtocolByEntity(entity).ifPresent(entityProtocol -> { - synchronized (entityProtocol.entityEvents) { - entityProtocol.entityEvents.add(event); - } - }); - } } diff --git a/src/main/java/org/lanternpowered/server/entity/event/EntityEventType.java b/src/main/java/org/lanternpowered/server/network/entity/EntityProtocolShardeventType.java similarity index 86% rename from src/main/java/org/lanternpowered/server/entity/event/EntityEventType.java rename to src/main/java/org/lanternpowered/server/network/entity/EntityProtocolShardeventType.java index daef32e66..c10daebbf 100644 --- a/src/main/java/org/lanternpowered/server/entity/event/EntityEventType.java +++ b/src/main/java/org/lanternpowered/server/network/entity/EntityProtocolShardeventType.java @@ -23,18 +23,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.event; +package org.lanternpowered.server.network.entity; +import org.lanternpowered.api.shard.event.Shardevent; import org.spongepowered.api.entity.Entity; -public enum EntityEventType { +public enum EntityProtocolShardeventType { + /** - * The {@link EntityEvent} will occur when + * The {@link Shardevent} will occur when * the {@link Entity} is alive. */ ALIVE, + /** - * The {@link EntityEvent} will occur when + * The {@link Shardevent} will occur when * the {@link Entity} is alive or dying. */ DEATH_OR_ALIVE, diff --git a/src/main/java/org/lanternpowered/server/network/entity/vanilla/AnimalEntityProtocol.java b/src/main/java/org/lanternpowered/server/network/entity/vanilla/AnimalEntityProtocol.java index c375d0758..0d288a401 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/vanilla/AnimalEntityProtocol.java +++ b/src/main/java/org/lanternpowered/server/network/entity/vanilla/AnimalEntityProtocol.java @@ -26,10 +26,10 @@ package org.lanternpowered.server.network.entity.vanilla; import org.lanternpowered.server.entity.LanternEntity; -import org.lanternpowered.server.entity.event.EntityEvent; -import org.lanternpowered.server.entity.event.LoveModeEntityEvent; +import org.lanternpowered.api.entity.event.animation.EntityLoveModeAnimation; import org.lanternpowered.server.network.entity.EntityProtocolUpdateContext; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutEntityStatus; +import org.lanternpowered.api.shard.event.Shardevent; public abstract class AnimalEntityProtocol extends AgeableEntityProtocol { @@ -38,8 +38,8 @@ protected AnimalEntityProtocol(E entity) { } @Override - protected void handleEvent(EntityProtocolUpdateContext context, EntityEvent event) { - if (event instanceof LoveModeEntityEvent) { + protected void handleEvent(EntityProtocolUpdateContext context, Shardevent event) { + if (event instanceof EntityLoveModeAnimation) { context.sendToAll(() -> new MessagePlayOutEntityStatus(getRootEntityId(), 18)); } else { super.handleEvent(context, event); diff --git a/src/main/java/org/lanternpowered/server/network/entity/vanilla/EntityProtocol.java b/src/main/java/org/lanternpowered/server/network/entity/vanilla/EntityProtocol.java index 2b41134bc..6849336ed 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/vanilla/EntityProtocol.java +++ b/src/main/java/org/lanternpowered/server/network/entity/vanilla/EntityProtocol.java @@ -35,9 +35,8 @@ import it.unimi.dsi.fastutil.ints.IntSets; import org.lanternpowered.server.entity.LanternEntity; import org.lanternpowered.server.entity.LanternLiving; -import org.lanternpowered.server.entity.event.CollectEntityEvent; -import org.lanternpowered.server.entity.event.EntityEvent; import org.lanternpowered.server.inventory.IInventory; +import org.lanternpowered.api.entity.event.animation.CollectEntityAnimation; import org.lanternpowered.server.inventory.LanternItemStack; import org.lanternpowered.server.network.entity.AbstractEntityProtocol; import org.lanternpowered.server.network.entity.EntityProtocolUpdateContext; @@ -55,6 +54,7 @@ import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutEntityTeleport; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutEntityVelocity; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutSetEntityPassengers; +import org.lanternpowered.api.shard.event.Shardevent; import org.lanternpowered.server.text.LanternTexts; import org.spongepowered.api.data.key.Keys; import org.spongepowered.api.entity.Entity; @@ -262,11 +262,11 @@ protected boolean hasEquipment() { } @Override - protected void handleEvent(EntityProtocolUpdateContext context, EntityEvent event) { - if (event instanceof CollectEntityEvent) { - final LanternLiving collector = (LanternLiving) ((CollectEntityEvent) event).getCollector(); + protected void handleEvent(EntityProtocolUpdateContext context, Shardevent event) { + if (event instanceof CollectEntityAnimation) { + final LanternLiving collector = (LanternLiving) ((CollectEntityAnimation) event).getCollector(); context.getId(collector).ifPresent(id -> { - final int count = ((CollectEntityEvent) event).getCollectedItemsCount(); + final int count = ((CollectEntityAnimation) event).getCollectedItemsCount(); context.sendToAll(() -> new MessagePlayOutEntityCollectItem(id, getRootEntityId(), count)); }); } else { diff --git a/src/main/java/org/lanternpowered/server/network/entity/vanilla/IronGolemEntityProcotol.java b/src/main/java/org/lanternpowered/server/network/entity/vanilla/IronGolemEntityProcotol.java index f6ea5ceea..88ecaa95d 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/vanilla/IronGolemEntityProcotol.java +++ b/src/main/java/org/lanternpowered/server/network/entity/vanilla/IronGolemEntityProcotol.java @@ -27,11 +27,11 @@ import org.lanternpowered.server.data.key.LanternKeys; import org.lanternpowered.server.entity.LanternEntity; -import org.lanternpowered.server.entity.event.EntityEvent; -import org.lanternpowered.server.entity.event.SwingHandEntityEvent; +import org.lanternpowered.api.entity.event.animation.SwingHandAnimation; import org.lanternpowered.server.network.entity.EntityProtocolUpdateContext; import org.lanternpowered.server.network.entity.parameter.ParameterList; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutEntityStatus; +import org.lanternpowered.api.shard.event.Shardevent; import org.spongepowered.api.data.type.HandType; import org.spongepowered.api.data.type.HandTypes; @@ -71,7 +71,7 @@ protected void update(EntityProtocolUpdateContext context) { super.update(context); final boolean holdsPoppy = getEntity().get(LanternKeys.HOLDS_POPPY).orElse(false); if (holdsPoppy) { - this.lastHoldPoppyTime -= getTickRate(); + this.lastHoldPoppyTime -= getNetworkComponent().getTrackingUpdateRate(); if (this.lastHoldPoppyTime <= 0) { context.sendToAll(() -> new MessagePlayOutEntityStatus(getRootEntityId(), POPPY_ADD_STATUS)); this.lastHoldPoppyTime = POPPY_RESEND_DELAY; @@ -83,9 +83,9 @@ protected void update(EntityProtocolUpdateContext context) { } @Override - protected void handleEvent(EntityProtocolUpdateContext context, EntityEvent event) { - if (event instanceof SwingHandEntityEvent) { - final HandType handType = ((SwingHandEntityEvent) event).getHandType(); + protected void handleEvent(EntityProtocolUpdateContext context, Shardevent event) { + if (event instanceof SwingHandAnimation) { + final HandType handType = ((SwingHandAnimation) event).getHandType(); // Doesn't matter which hand type, just play the swing animation, // the golem will use both arms at the same time if (handType == HandTypes.MAIN_HAND || handType == HandTypes.OFF_HAND) { diff --git a/src/main/java/org/lanternpowered/server/network/entity/vanilla/LightningEntityProtocol.java b/src/main/java/org/lanternpowered/server/network/entity/vanilla/LightningEntityProtocol.java index dd0808388..a52c8c024 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/vanilla/LightningEntityProtocol.java +++ b/src/main/java/org/lanternpowered/server/network/entity/vanilla/LightningEntityProtocol.java @@ -34,7 +34,6 @@ public class LightningEntityProtocol extends EntityProt public LightningEntityProtocol(E entity) { super(entity); - setTrackingRange(512); } @Override diff --git a/src/main/java/org/lanternpowered/server/network/entity/vanilla/LivingEntityProtocol.java b/src/main/java/org/lanternpowered/server/network/entity/vanilla/LivingEntityProtocol.java index 4009c87ad..7e3c8eb6d 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/vanilla/LivingEntityProtocol.java +++ b/src/main/java/org/lanternpowered/server/network/entity/vanilla/LivingEntityProtocol.java @@ -27,15 +27,15 @@ import org.lanternpowered.server.data.key.LanternKeys; import org.lanternpowered.server.entity.LanternEntity; -import org.lanternpowered.server.entity.event.DamagedEntityEvent; -import org.lanternpowered.server.entity.event.EntityEvent; -import org.lanternpowered.server.entity.event.SwingHandEntityEvent; +import org.lanternpowered.api.entity.event.animation.DamageEntityAnimation; +import org.lanternpowered.api.entity.event.animation.SwingHandAnimation; import org.lanternpowered.server.game.LanternGame; import org.lanternpowered.server.network.entity.EntityProtocolUpdateContext; import org.lanternpowered.server.network.entity.parameter.ParameterList; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutAddPotionEffect; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutEntityAnimation; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutRemovePotionEffect; +import org.lanternpowered.api.shard.event.Shardevent; import org.spongepowered.api.data.key.Keys; import org.spongepowered.api.data.type.HandType; import org.spongepowered.api.data.type.HandTypes; @@ -146,11 +146,11 @@ private MessagePlayOutAddPotionEffect createAddMessage(PotionEffect potionEffect } @Override - protected void handleEvent(EntityProtocolUpdateContext context, EntityEvent event) { - if (event instanceof DamagedEntityEvent) { + protected void handleEvent(EntityProtocolUpdateContext context, Shardevent event) { + if (event instanceof DamageEntityAnimation) { context.sendToAll(() -> new MessagePlayOutEntityAnimation(getRootEntityId(), 1)); - } else if (event instanceof SwingHandEntityEvent) { - final HandType handType = ((SwingHandEntityEvent) event).getHandType(); + } else if (event instanceof SwingHandAnimation) { + final HandType handType = ((SwingHandAnimation) event).getHandType(); if (handType == HandTypes.MAIN_HAND) { context.sendToAllExceptSelf(() -> new MessagePlayOutEntityAnimation(getRootEntityId(), 0)); } else if (handType == HandTypes.OFF_HAND) { diff --git a/src/main/java/org/lanternpowered/server/network/entity/vanilla/PlayerEntityProtocol.java b/src/main/java/org/lanternpowered/server/network/entity/vanilla/PlayerEntityProtocol.java index c1bb583f7..6b32950c9 100644 --- a/src/main/java/org/lanternpowered/server/network/entity/vanilla/PlayerEntityProtocol.java +++ b/src/main/java/org/lanternpowered/server/network/entity/vanilla/PlayerEntityProtocol.java @@ -30,9 +30,8 @@ import com.flowpowered.math.vector.Vector3d; import it.unimi.dsi.fastutil.ints.IntSet; import org.lanternpowered.server.data.key.LanternKeys; -import org.lanternpowered.server.entity.event.EntityEvent; -import org.lanternpowered.server.entity.event.RefreshAbilitiesPlayerEvent; -import org.lanternpowered.server.entity.event.SpectateEntityEvent; +import org.lanternpowered.server.entity.event.RequestPlayerAbilitiesRefreshShardevent; +import org.lanternpowered.server.entity.event.SpectateEntityShardevent; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.lanternpowered.server.entity.living.player.gamemode.LanternGameMode; import org.lanternpowered.server.extra.accessory.TopHat; @@ -54,6 +53,7 @@ import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutSetGameMode; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutSpawnMob; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutSpawnObject; +import org.lanternpowered.api.shard.event.Shardevent; import org.spongepowered.api.data.key.Keys; import org.spongepowered.api.data.type.DyeColor; import org.spongepowered.api.entity.Entity; @@ -93,7 +93,6 @@ public class PlayerEntityProtocol extends HumanoidEntityProtocol public PlayerEntityProtocol(LanternPlayer entity) { super(entity); - setTickRate(1); } @Override @@ -363,15 +362,15 @@ protected void update(EntityProtocolUpdateContext context) { } @Override - protected void handleEvent(EntityProtocolUpdateContext context, EntityEvent event) { - if (event instanceof SpectateEntityEvent) { - final Entity entity = ((SpectateEntityEvent) event).getSpectatedEntity().orElse(null); + protected void handleEvent(EntityProtocolUpdateContext context, Shardevent event) { + if (event instanceof SpectateEntityShardevent) { + final Entity entity = ((SpectateEntityShardevent) event).getSpectatedEntity(); if (entity == null) { context.sendToSelf(() -> new MessagePlayOutSetCamera(getRootEntityId())); } else { context.getId(entity).ifPresent(id -> context.sendToSelf(() -> new MessagePlayOutSetCamera(id))); } - } else if (event instanceof RefreshAbilitiesPlayerEvent) { + } else if (event instanceof RequestPlayerAbilitiesRefreshShardevent) { final GameMode gameMode = this.entity.get(Keys.GAME_MODE).get(); final float flySpeed = getFlySpeed(); final float fov = getFovModifier(); diff --git a/src/main/java/org/lanternpowered/server/network/vanilla/message/handler/play/HandlerPlayInPlayerAbilities.java b/src/main/java/org/lanternpowered/server/network/vanilla/message/handler/play/HandlerPlayInPlayerAbilities.java index 6ef6a4758..bf882838a 100644 --- a/src/main/java/org/lanternpowered/server/network/vanilla/message/handler/play/HandlerPlayInPlayerAbilities.java +++ b/src/main/java/org/lanternpowered/server/network/vanilla/message/handler/play/HandlerPlayInPlayerAbilities.java @@ -28,7 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import org.lanternpowered.server.block.property.SolidSideProperty; import org.lanternpowered.server.data.key.LanternKeys; -import org.lanternpowered.server.entity.event.RefreshAbilitiesPlayerEvent; +import org.lanternpowered.server.entity.event.RequestPlayerAbilitiesRefreshShardevent; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.lanternpowered.server.network.NetworkContext; import org.lanternpowered.server.network.message.handler.Handler; @@ -94,7 +94,7 @@ public void handle(NetworkContext context, MessagePlayInPlayerAbilities message) } } } - player.triggerEvent(RefreshAbilitiesPlayerEvent.of()); + player.getShardeventBus().post(RequestPlayerAbilitiesRefreshShardevent.INSTANCE); } } } diff --git a/src/main/java/org/lanternpowered/server/shards/AbstractComponentHolder.java b/src/main/java/org/lanternpowered/server/shards/AbstractComponentHolder.java new file mode 100644 index 000000000..b7aba3592 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/AbstractComponentHolder.java @@ -0,0 +1,112 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import kotlin.reflect.KClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; +import org.lanternpowered.api.shard.ShardType; +import org.lanternpowered.api.shard.event.ShardeventBus; +import org.lanternpowered.server.shards.internal.ComponentContainer; + +import java.util.Collection; +import java.util.Optional; + +public class AbstractComponentHolder implements ShardHolder { + + private final ComponentContainer componentContainer = new ComponentContainer(this); + + @Override + public ShardeventBus getShardeventBus() { + return this.componentContainer.getShardeventBus(); + } + + @NotNull @Override public > T addShard(@NotNull ShardType type) { + return null; + } + + @Override public boolean addShard(@NotNull Shard shard) throws IllegalArgumentException { + return false; + } + + @Override public , I extends T> boolean replaceShard(@NotNull Class type, @NotNull I component) + throws IllegalArgumentException { + return false; + } + + @Override public , I extends T> boolean replaceShard(@NotNull KClass type, @NotNull I component) + throws IllegalArgumentException { + return false; + } + + @NotNull @Override public , I extends T> Optional replaceShard(@NotNull Class type, @NotNull Class component) + throws IllegalArgumentException { + return Optional.empty(); + } + + @Nullable @Override public , I extends T> I replaceShard(@NotNull KClass type, @NotNull Class component) + throws IllegalArgumentException { + return null; + } + + @NotNull @Override public > Optional getShard(@NotNull Class type) { + return Optional.empty(); + } + + @Nullable @Override public > T getShard(@NotNull KClass type) { + return null; + } + + @NotNull @Override public > Optional removeShard(@NotNull Class type) { + return Optional.empty(); + } + + @NotNull @Override public Optional getShardOfType(@NotNull Class type) { + return Optional.empty(); + } + + @Nullable @Override public T getShardOfType(@NotNull KClass type) { + return null; + } + + @NotNull @Override public Collection getShardsOfType(@NotNull Class type) { + return null; + } + + @NotNull @Override public Collection getShardsOfType(@NotNull KClass type) { + return null; + } + + @NotNull @Override public > Optional addShard(@NotNull Class type) { + return Optional.empty(); + } + + @Nullable @Override public > T addShard(@NotNull KClass type) { + return null; + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/AnnotatedBindingBuilder.java b/src/main/java/org/lanternpowered/server/shards/AnnotatedBindingBuilder.java new file mode 100644 index 000000000..b5e6d396a --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/AnnotatedBindingBuilder.java @@ -0,0 +1,35 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import java.lang.annotation.Annotation; + +public interface AnnotatedBindingBuilder extends BindingBuilder { + + BindingBuilder annotatedWith(Class annotationType); + + BindingBuilder annotatedWith(Annotation annotation); +} diff --git a/src/main/java/org/lanternpowered/server/shards/BindingBuilder.java b/src/main/java/org/lanternpowered/server/shards/BindingBuilder.java new file mode 100644 index 000000000..ccbd3f7b0 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/BindingBuilder.java @@ -0,0 +1,37 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import java.util.function.Supplier; + +public interface BindingBuilder { + + void to(Class implementation); + + void to(Supplier supplier); + + void to(SupplierFactory supplierFactory); +} diff --git a/src/main/java/org/lanternpowered/server/shards/DefaultShardImpl.java b/src/main/java/org/lanternpowered/server/shards/DefaultShardImpl.java new file mode 100644 index 000000000..5f98a7b13 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/DefaultShardImpl.java @@ -0,0 +1,51 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import org.lanternpowered.api.shard.Shard; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * This annotation can be applied to a {@link Shard} + * subclass to provide a default implementation. + */ +@Target(TYPE) +@Retention(RUNTIME) +public @interface DefaultShardImpl { + + /** + * Provides a default implementation {@link Shard} for a + * abstract {@link Shard} class. + * + * @return The implementation class + */ + Class value(); +} diff --git a/src/main/java/org/lanternpowered/server/shards/DefaultSupplier.java b/src/main/java/org/lanternpowered/server/shards/DefaultSupplier.java new file mode 100644 index 000000000..4d3cb2d9f --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/DefaultSupplier.java @@ -0,0 +1,62 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import ninja.leaping.configurate.objectmapping.Setting; +import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; +import org.lanternpowered.api.shard.Shard; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.function.Supplier; + +/** + * This annotation can be applied to a {@link Shard} + * subclass to provide a default implementation. + */ +@Target(TYPE) +@Retention(RUNTIME) +public @interface DefaultSupplier { + + /** + * Applies a {@link Supplier} class that should be used to + * provide a implementation for the target {@link Shard}. The class + * must have a empty constructor. + *

If set to {@code ComponentProvider.class}, the default behavior will + * be used and a empty constructor of the {@link Shard} will be used.

+ *

If the {@link DefaultSupplier} annotated class is also annotated with + * {@link ConfigSerializable} a custom {@link Supplier} will be generate + * from {@link Setting} annotated fields from the given class, the contents of fields + * in the generated class will be transferred to the every constructed + * {@link Shard}.

+ * + * @return The component provider class + */ + Class provider() default Supplier.class; +} diff --git a/src/main/java/org/lanternpowered/server/shards/Holder.java b/src/main/java/org/lanternpowered/server/shards/Holder.java new file mode 100644 index 000000000..4ff266a0d --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/Holder.java @@ -0,0 +1,51 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * This annotation can be applied to a field or parameter to inject + * the {@link ShardHolder} into a target {@link Shard}. + *

+ * The type of the field or parameter also defines the target + * {@link ShardHolder} type of the {@link Shard}. Attempting + * to attach {@link Shard} to a different {@link ShardHolder} + * will result in a failure. + */ +@Target({ FIELD, PARAMETER }) +@Retention(RUNTIME) +public @interface Holder { + +} diff --git a/src/main/java/org/lanternpowered/server/shards/InjectableType.java b/src/main/java/org/lanternpowered/server/shards/InjectableType.java new file mode 100644 index 000000000..fefb33677 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/InjectableType.java @@ -0,0 +1,89 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.reflect.TypeToken; +import org.lanternpowered.server.shards.internal.inject.LanternInjectableType; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Arrays; + +import javax.annotation.Nullable; + +public interface InjectableType extends AnnotatedElement { + + /** + * Creates a new {@link InjectableType} with the + * given {@link TypeToken}. + * + * @param type The type token + * @param The type + * @return The injectable type + */ + static InjectableType of(TypeToken type) { + checkNotNull(type, "type"); + return new LanternInjectableType<>(type); + } + + /** + * Creates a new {@link InjectableType} with the + * given {@link TypeToken} and {@link Annotation}s. + * + * @param type The type token + * @param annotations The annotations + * @param The type + * @return The injectable type + */ + static InjectableType of(TypeToken type, Annotation[] annotations) { + checkNotNull(type, "type"); + checkNotNull(annotations, "annotations"); + return new LanternInjectableType<>(type, Arrays.copyOf(annotations, annotations.length)); + } + + /** + * Gets the {@link TypeToken}. + * + * @return The type token + */ + TypeToken getType(); + + @Nullable + @Override + A getAnnotation(Class annotationClass); + + /** + * {@inheritDoc} + * + * @deprecated Declared annotations are not supported by {@link InjectableType}s. + */ + @Nullable + @Override + @Deprecated + A getDeclaredAnnotation(Class annotationClass); +} diff --git a/src/main/java/org/lanternpowered/server/shards/InjectionRegistry.java b/src/main/java/org/lanternpowered/server/shards/InjectionRegistry.java new file mode 100644 index 000000000..f21725393 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/InjectionRegistry.java @@ -0,0 +1,112 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import com.google.common.reflect.TypeToken; +import com.google.inject.Module; + +import java.util.Optional; +import java.util.function.Supplier; + +public interface InjectionRegistry { + + /** + * Creates a {@link AnnotatedBindingBuilder} for + * the given object {@link Class}. + * + * @param objectType The object type + * @param The object type + * @return The binding builder + */ + default AnnotatedBindingBuilder bind(Class objectType) { + return bind(TypeToken.of(objectType)); + } + + /** + * Creates a {@link AnnotatedBindingBuilder} for + * the given object {@link TypeToken}. + * + * @param objectType The object type + * @param The object type + * @return The binding builder + */ + AnnotatedBindingBuilder bind(TypeToken objectType); + + /** + * Binds a global {@link SupplierFactory}. + * + * @param supplierFactory The supplier factory + */ + void bindFactory(SupplierFactory supplierFactory); + + /** + * Gets a {@link Supplier} for the given + * object {@link Class}. + * + * @param objectType The object type + * @param The object type + * @return The supplier + */ + default Optional> getSupplier(Class objectType) { + return getSupplier(TypeToken.of(objectType)); + } + + /** + * Gets a {@link Supplier} for the given + * object {@link TypeToken}. + * + * @param objectType The object type + * @param The object type + * @return The supplier + */ + Optional> getSupplier(TypeToken objectType); + + /** + * Gets a {@link Supplier} for the given + * {@link InjectableType}. + * + * @param injectableType The injectable type + * @param The object type + * @return The supplier + */ + Optional> getSupplier(InjectableType injectableType); + + /** + * Installs the guice {@link Module} to provide + * additional injections. + * + * @param module The module + */ + void install(Module module); + + /** + * Creates a new child {@link InjectionRegistry} + * from this registry. + * + * @return The child injection registry + */ + InjectionRegistry newChild(); +} diff --git a/src/main/java/org/lanternpowered/server/shards/OnAttach.java b/src/main/java/org/lanternpowered/server/shards/OnAttach.java new file mode 100644 index 000000000..e5fadddc4 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/OnAttach.java @@ -0,0 +1,46 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * This annotation can be applied to a method within a {@link Shard} + * that will be called when the {@link Shard} is being attached to + * a {@link ShardHolder}. + */ +@Target(METHOD) +@Retention(RUNTIME) +public @interface OnAttach { + +} diff --git a/src/main/java/org/lanternpowered/server/shards/OnDetach.java b/src/main/java/org/lanternpowered/server/shards/OnDetach.java new file mode 100644 index 000000000..5b7cd9f78 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/OnDetach.java @@ -0,0 +1,45 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * This annotation can be applied to a method that will be called when a + * {@link Shard} is being detached from a {@link ShardHolder}. + */ +@Target(METHOD) +@Retention(RUNTIME) +public @interface OnDetach { + +} diff --git a/src/main/java/org/lanternpowered/server/shards/OnUpdate.java b/src/main/java/org/lanternpowered/server/shards/OnUpdate.java new file mode 100644 index 000000000..8e084c25b --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/OnUpdate.java @@ -0,0 +1,45 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(METHOD) +@Retention(RUNTIME) +public @interface OnUpdate { + + /** + * The amount of pulses between + * every update. + * + * @return The pulse rate + */ + int pulseRate() default 1; +} diff --git a/src/main/java/org/lanternpowered/server/shards/Opt.java b/src/main/java/org/lanternpowered/server/shards/Opt.java new file mode 100644 index 000000000..e55de7875 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/Opt.java @@ -0,0 +1,209 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import static java.util.Objects.requireNonNull; + +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; + +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +/** + * A {@link Supplier} that optionally gets a value of the type {@link T}. + *

+ * This interface should be inside a {@link Shard} for the injection + * of optional values ({@link Shard}s) and being able to retrieve them. + * For example: + *

+ * {@code
+ * class FooComponent extends AbstractComponent {
+ *
+ *     @Inject Opt optBarComponent;
+ *
+ *     // This @Holder will be provided if the holder this component
+ *     // is attached to this class extends. The type of the {@link ShardHolder }
+ *     @Inject Opt optFooComponentHolder;
+ *
+ *     public void pulse() {
+ *         optBarComponent.ifPresent(bar -> {
+ *             bar.doStuff();
+ *         });
+ *     }
+ * }
+ * }
+ * 
+ * This can also be applied to {@link ShardHolder} injection to allow + * injection if the holder the target class implements. + * For example: + *
+ * {@code
+ * class FooComponent extends AbstractComponent {
+ *
+ *     @Inject @Holder Opt optFooComponentHolder;
+ *
+ *     public void pulse() {
+ *         optFooComponentHolder.ifPresent(holder -> {
+ *             holder.doStuff();
+ *         });
+ *     }
+ * }
+ * }
+ * 
+ * + * @param The component type + */ +@SuppressWarnings("unchecked") +@FunctionalInterface +public interface Opt extends Supplier { + + /** + * Returns an empty {@link Opt} instance, no value is present. + * + * @return An empty {@link Opt} + */ + static Opt empty() { + return OptImpl.EMPTY; + } + + /** + * Returns an {@link Opt} with the specified non-null value. + * + * @param value The value + * @return An {@link Opt} with the value present + */ + static Opt of(T value) { + requireNonNull(value); + return () -> value; + } + + /** + * Returns an {@link Opt} with the specified value if non-null, + * otherwise no value is present. + * + * @param value The value + * @return An {@link Opt} with the value if present, otherwise {@link #empty()} + */ + static Opt ofNullable(@Nullable T value) { + return value == null ? empty() : of(value); + } + + @Nullable + T getOrNull(); + + /** + * Gets the value if present, otherwise throws {@link NoSuchElementException}. + * + * @return The value + * @throws NoSuchElementException If there is no value present + */ + @Override + default T get() throws NoSuchElementException { + final T value = getOrNull(); + if (value == null) { + throw new NoSuchElementException("No value present"); + } + return value; + } + + /** + * Gets whether the returned value (through {@link #get()}) + * is present (isn't {@code null}). + * + * @return Is present + */ + default boolean isPresent() { + return getOrNull() != null; + } + + /** + * Gets the value as a {@link Optional}. + * + * @return The optional + */ + default Optional asOptional() { + return Optional.ofNullable(getOrNull()); + } + + /** + * If a value is present, invoke the specified consumer with the value, + * otherwise do nothing. + * + * @param consumer The consumer to execute + */ + default void ifPresent(Consumer consumer) { + final T value = getOrNull(); + if (value != null) { + consumer.accept(value); + } + } + + /** + * Return the value if present, otherwise return {@code other}. + * + * @param other The value to be returned if there is no value present + * @return The value if present, otherwise the other value + */ + default T orElse(T other) { + final T value = getOrNull(); + return value != null ? value : other; + } + + /** + * Return the value if present, otherwise invoke {@code other} and return + * the result of that invocation. + * + * @param other A supplier whose result is returned if there is no value present + * @return The value if present, otherwise the result of the supplier + */ + default T orElseGet(Supplier other) { + final T value = getOrNull(); + return value != null ? value : other.get(); + } + + /** + * Return the value if present, otherwise throw the + * {@link Throwable} that is provided by the {@link Supplier}. + * + * @param exceptionSupplier The exception supplier + * @param The exception type + * @return The value + * @throws X The exception + */ + default T orElseThrow(Supplier exceptionSupplier) throws X { + final T value = getOrNull(); + if (value != null) { + return value; + } else { + throw exceptionSupplier.get(); + } + } +} diff --git a/src/main/java/org/lanternpowered/server/entity/weather/AbstractLightning.java b/src/main/java/org/lanternpowered/server/shards/OptImpl.java similarity index 87% rename from src/main/java/org/lanternpowered/server/entity/weather/AbstractLightning.java rename to src/main/java/org/lanternpowered/server/shards/OptImpl.java index be0f588fd..7f325e0b6 100644 --- a/src/main/java/org/lanternpowered/server/entity/weather/AbstractLightning.java +++ b/src/main/java/org/lanternpowered/server/shards/OptImpl.java @@ -23,10 +23,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.weather; +package org.lanternpowered.server.shards; -import org.spongepowered.api.entity.weather.Lightning; +final class OptImpl { -public interface AbstractLightning extends AbstractWeatherEffect, Lightning { + static final Opt EMPTY = () -> null; + private OptImpl() { + } } diff --git a/src/main/java/org/lanternpowered/server/shards/SupplierFactory.java b/src/main/java/org/lanternpowered/server/shards/SupplierFactory.java new file mode 100644 index 000000000..74f6c0c48 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/SupplierFactory.java @@ -0,0 +1,43 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards; + +import java.util.Optional; +import java.util.function.Supplier; + +@FunctionalInterface +public interface SupplierFactory { + + /** + * Attempts to get a {@link Supplier} for + * the {@link InjectableType}. + * + * @param registry The registry + * @param injectableType The injectable type + * @return The supplier, if successful, otherwise {@link Optional#empty()} + */ + Optional> get(InjectionRegistry registry, InjectableType injectableType); +} diff --git a/src/main/java/org/lanternpowered/server/shards/dependency/AutoAttach.java b/src/main/java/org/lanternpowered/server/shards/dependency/AutoAttach.java new file mode 100644 index 000000000..81eb2194b --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/dependency/AutoAttach.java @@ -0,0 +1,49 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.dependency; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Can be applied to a field or parameter that extend {@link Shard}, + * this is to define that the target component should be automatically + * attached to the {@link ShardHolder} when there is an attempt to + * attach the current {@link Shard} type. + * This is the equivalent of {@link Dependency#autoAttach()}. + */ +@Target({ FIELD, PARAMETER }) +@Retention(RUNTIME) +public @interface AutoAttach { + +} diff --git a/src/main/java/org/lanternpowered/server/shards/dependency/Dependencies.java b/src/main/java/org/lanternpowered/server/shards/dependency/Dependencies.java new file mode 100644 index 000000000..b01ace9f7 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/dependency/Dependencies.java @@ -0,0 +1,52 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.dependency; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import org.lanternpowered.api.shard.Shard; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Can be applied to a {@link Shard} type to define a list of {@link Shard}s + * that the current {@link Shard} is depending on. The {@link Shard}s may + * be required or optional, and can be automatically attached by setting + * {@link Dependency#autoAttach()} to {@code true}. + */ +@Retention(RUNTIME) +@Target(TYPE) +public @interface Dependencies { + + /** + * The list of dependencies. + * + * @return The dependencies + */ + Dependency[] value(); +} diff --git a/src/main/java/org/lanternpowered/server/shards/dependency/Dependency.java b/src/main/java/org/lanternpowered/server/shards/dependency/Dependency.java new file mode 100644 index 000000000..ce06e6513 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/dependency/Dependency.java @@ -0,0 +1,69 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.dependency; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; +import org.lanternpowered.server.shards.InjectionRegistry; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Represents the dependency of a specific {@link Shard}. + */ +@Target({}) +@Retention(RUNTIME) +public @interface Dependency { + + /** + * Gets the {@link Shard} type of the dependency. + * + * @return The component type + */ + Class value(); + + /** + * Gets the {@link Requirement} of the dependency. + * + * @return The dependency type + */ + Requirement type() default Requirement.REQUIRED; + + /** + * Whether the dependency can be automatically attached to + * a {@link ShardHolder}. + *

+ * This expects that when this method returns {@code true} and the {@link Shard} + * is abstract, a default implementation is provided through + * the {@link InjectionRegistry}. + * + * @return Auto attach + */ + boolean autoAttach() default false; +} diff --git a/src/main/java/org/lanternpowered/server/shards/dependency/Requirement.java b/src/main/java/org/lanternpowered/server/shards/dependency/Requirement.java new file mode 100644 index 000000000..e6e956c01 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/dependency/Requirement.java @@ -0,0 +1,77 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.dependency; + +import org.lanternpowered.server.shards.Opt; +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; + +import javax.annotation.Nullable; + +/** + * Represents how strict a dependency {@link Shard} type should be present + * on a {@link ShardHolder} for the depending {@link Shard} to function. + */ +public enum Requirement { + + /** + * The dependency {@link Shard} is optionally available for + * the target {@link ShardHolder} in order for the depending + * {@link Shard} to function. + *

+ * The equivalent of this by using annotations is specifying a + * {@link Opt} with a specific {@link Shard} type, for example: + *

+     * {@code
+     *     @Inject Opt optFooComponent;
+     * }
+     * 
+ * Another option is to mark the field or parameter + * with {@link Nullable}, for example: + *
+     * {@code
+     *     @Inject @Nullable FooComponent optFooComponent;
+     * }
+     * 
+ */ + OPTIONAL, + + /** + * The dependency {@link Shard} must be available for the + * the target {@link ShardHolder} in order for the depending + * {@link Shard} to function. This also blocks the given + * {@link Shard} from being removed from a {@link ShardHolder}. + *

+ * The equivalent of this by using annotations is specifying a + * a specific {@link Shard} type, for example: + *

+     * {@code
+     *     @Inject FooComponent fooComponent;
+     * }
+     * 
+ */ + REQUIRED, +} diff --git a/src/main/java/org/lanternpowered/server/shards/dependency/package-info.java b/src/main/java/org/lanternpowered/server/shards/dependency/package-info.java new file mode 100644 index 000000000..15a4f9d1e --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/dependency/package-info.java @@ -0,0 +1,27 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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. + */ +@org.spongepowered.api.util.annotation.NonnullByDefault +package org.lanternpowered.server.shards.dependency; diff --git a/src/main/java/org/lanternpowered/server/shards/internal/ComponentContainer.java b/src/main/java/org/lanternpowered/server/shards/internal/ComponentContainer.java new file mode 100644 index 000000000..a3385e7a0 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/ComponentContainer.java @@ -0,0 +1,273 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.api.shard.ShardHolder; +import org.lanternpowered.server.shards.InjectionRegistry; +import org.lanternpowered.api.shard.event.Shardevent; +import org.lanternpowered.api.shard.event.ShardeventBus; +import org.lanternpowered.server.shards.internal.event.LanternShardeventBus; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * A simple container to hold {@link Shard}s + * and handle {@link Shardevent}s. + */ +@SuppressWarnings({"unchecked", "ConstantConditions"}) +public final class ComponentContainer { + + private final ShardHolder holder; + private final ShardeventBus shardeventBus = new LanternShardeventBus(); + private final Map, Shard> components = new ConcurrentHashMap<>(); + + /** + * A cache to lookup implementations based on a + * {@link Shard} interface or super class. + */ + private final LoadingCache, List> componentCache = Caffeine.newBuilder().build(key -> { + List list = null; + for (Shard component : this.components.values()) { + if (key.isInstance(component)) { + if (list == null) { + list = new ArrayList<>(); + } + list.add(component); + } + } + return list == null ? Collections.emptyList() : ImmutableList.copyOf(list); + }); + + /** + * Constructs a new {@link ComponentContainer} + * for the given {@link ShardHolder}. + * + * @param holder The component holder + */ + public ComponentContainer(ShardHolder holder) { + checkNotNull(holder, "holder"); + this.holder = holder; + } + + /** + * Gets the {@link ShardeventBus}. + * + * @return The shardevent bus + */ + public ShardeventBus getShardeventBus() { + return this.shardeventBus; + } + + /** + * Gets the {@link ShardHolder}. + * + * @return The component holder + */ + public ShardHolder getHolder() { + return holder; + } + + private static class ComponentEntry { + + private final ComponentType componentType; + private final Supplier supplier; + + private ComponentEntry(ComponentType componentType, Supplier supplier) { + this.componentType = componentType; + this.supplier = supplier; + } + } + + + /** + * Attempts to attach a {@link Shard} of the given type to the {@link ShardHolder}, + * if there is already a component of the given type, then that instance will be returned. + *

+ * This method expects that when the {@link Shard} is either abstract or an interface, + * a default implementation is provided through the {@link InjectionRegistry}. + * + * @param type The component type to attach + * @return The component instance + *//* + public Optional addComponent(Class type) { + return addComponent(ComponentType.get(type), LanternInjectionRegistry.get().getSupplier(type)); + } + + /** + * Attempts to attach the given {@link Shard} to this {@link ShardHolder}. The method + * will return {@code true} if it was successful, adding a component will be successful if there + * isn't a {@link Shard} with the same type. + * + * @param component The component to attach + * @return Whether the attachment was successful + * @throws IllegalArgumentException If the given component instance is already attached + *//* + public boolean addComponent(Shard component) throws IllegalArgumentException { + return addComponent(ComponentType.get(component.getClass()), (Supplier) (() -> component)).isPresent(); + } + + private Optional addComponent(ComponentType type, Supplier supplier) { + // Check if the holder can be attached + if (!isHolderCompatible(type)) { + return Optional.empty(); + } + final List suppliers = new ArrayList<>(); + // Initialize all the dependencies of the component + for (DependencySpec dependencySpec : type.getDependencies()) { + final ComponentType dependencyComponentType = ComponentType.get(dependencySpec.getType()); + // Check if the dependency is required + if (dependencySpec.getDependencyType() != Requirement.OPTIONAL) { + // Check if there is already a component attached of this type + final Optional optComponent = getComponent(dependencySpec.getType()); + // We already have a component of this type, no need to attach a new one + if (optComponent.isPresent()) { + continue; + } + // No auto attach, so we need to fail + if (!dependencySpec.getAutoAttach()) { + return Optional.empty(); + } + } else if (!dependencySpec.getAutoAttach() || // Skip if it's optional and not auto attach + !isHolderCompatible(dependencyComponentType)) { // Or optional and incompatible holder requirements + continue; + } + // Construct the dependency component and add it + final Supplier dependencyComponentSupplier = LanternInjectionRegistry.get().getSupplier(dependencySpec.getType()); + suppliers.add(dependencyComponentSupplier); + } + // Construct all the components + final T component = supplier.get(); + this.components.put(component.getClass(), component); + for (Supplier componentSupplier : suppliers) { + final Shard dependencyComponent = (Shard) componentSupplier.get(); + this.components.putIfAbsent(dependencyComponent.getClass(), dependencyComponent); + } + // Invalidate the cache + this.componentCache.invalidateAll(); + return Optional.of(component); + } + + private boolean isHolderCompatible(ComponentType componentType) { + for (Class holderRequirement : componentType.getHolderRequirements()) { + if (!holderRequirement.isInstance(this.holder)) { + return false; + } + } + return true; + } + + /* + private List addComponents(List componentEntries) { + // Check if the holder can be attached + for (Class holderRequirement : type.getHolderRequirements()) { + if (!holderRequirement.isInstance(this.holder)) { + return Optional.empty(); + } + } + // Initialize all the dependencies of the component + for (DependencySpec dependencySpec : type.getDependencies()) { + + } + return Optional.empty(); + }*/ + + /** + * Attempts to replace the {@link Shard} attached to the given type. If there are multiple + * ones found, only the first possible one will be replaced. If there were none found, the + * {@link Shard} will just be attached to this holder. + * + * @param type The component type to replace + * @param component The new component instance + * @return Whether the replacement was successful + * @throws IllegalArgumentException If the given component instance is already attached + */ + public boolean replaceComponent(Class type, I component) throws IllegalArgumentException { + return false; + } + + /** + * Attempts to replace the {@link Shard} attached to the given type. If there are multiple + * ones found, only the first possible one will be replaced. If there were none found, the + * {@link Shard} will just be attached to this holder. + * + * @param type The component type to replace + * @param component The new component type + * @return Whether the replacement was successful + * @throws IllegalArgumentException If the given component instance is already attached + */ + public boolean replaceComponent(Class type, Class component) throws IllegalArgumentException { + return false; + } + + /** + * Gets the {@link Shard} of the given type if present, otherwise {@link Optional#empty()}. + *

+ * Only the first {@link Shard} will be returned if there + * are multiple ones for the given type. + * + * @param type The component type + * @return The component instance if present + */ + public Optional getComponent(Class type) { + final List components = (List) this.componentCache.get(type); + return components.isEmpty() ? Optional.empty() : Optional.of(components.get(0)); + } + + /** + * Gets a {@link Collection} with all the {@link Shard}s + * of the given type. + * + * @param type The component type + * @return A collection with the components + */ + public Collection getComponents(Class type) { + return (List) this.componentCache.get(type); + } + + /** + * Attempts to remove all the {@link Shard}s that match the given type, all the components + * that were removed will be present in the result {@link Collection}. + * + * @param type The component type + * @return A collection with the removed components + */ + public Collection removeComponents(Class type) { + return Collections.emptySet(); + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/ComponentType.java b/src/main/java/org/lanternpowered/server/shards/internal/ComponentType.java new file mode 100644 index 000000000..2fb295b5d --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/ComponentType.java @@ -0,0 +1,287 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.google.common.base.MoreObjects; +import com.google.common.reflect.TypeToken; +import com.google.inject.Inject; +import org.lanternpowered.server.shards.Holder; +import org.lanternpowered.server.shards.InjectableType; +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.server.shards.dependency.AutoAttach; +import org.lanternpowered.server.shards.dependency.Dependencies; +import org.lanternpowered.server.shards.dependency.Dependency; +import org.lanternpowered.server.shards.dependency.Requirement; +import org.lanternpowered.server.shards.internal.inject.InjectionEntry; +import org.lanternpowered.server.shards.internal.inject.LanternInjectableType; +import org.lanternpowered.server.util.FieldAccessFactory; +import org.lanternpowered.server.util.LambdaFactory; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +@SuppressWarnings("unchecked") +public final class ComponentType { + + /** + * Gets the {@link ComponentType} for the + * given {@link Shard} class. + * + * @param componentType The component class + * @param The component type + * @return The component type + */ + public static ComponentType get(Class componentType) { + checkNotNull(componentType, "componentType"); + return cache.get(componentType); + } + + private static final LoadingCache, ComponentType> cache = + Caffeine.newBuilder().build(ComponentType::load); + + /** + * Creates the {@link ComponentType} for the given {@link Shard} {@link TypeToken}. + * + * @param key The component type token + * @param The component type + * @return The component type + */ + private static ComponentType load(Class key) { + checkNotNull(key, "key"); + // Collect all the dependencies of the ComponentType + final CollectionContext ctx = new CollectionContext(); + collect(key, ctx); + return new ComponentType<>(key, ctx.dependencies, ctx.injectionEntries, + ctx.holderInjectionEntries.stream() + .map(e -> e.getValueType().getType().getRawType()) + .collect(Collectors.toList())); + } + + private static final class CollectionContext { + + private final Set> processed = new HashSet<>(); + private final Set methodKeys = new HashSet<>(); + + private final List dependencies = new ArrayList<>(); + + private final List injectionEntries = new ArrayList<>(); + private final List holderInjectionEntries = new ArrayList<>(); + } + + /** + * Collects the {@link DependencySpec}s and + * injection fields from the given class type. + * + * @param key The key + */ + private static void collect(Class key, CollectionContext ctx) { + if (!ctx.processed.add(key)) { // Was already processed, skip it + return; + } + final Dependencies dependenciesAnno = key.getAnnotation(Dependencies.class); + if (dependenciesAnno != null) { + for (Dependency dependency : dependenciesAnno.value()) { + DependencySpec.mergeAndAdd(ctx.dependencies, dependency.value(), dependency.type(), dependency.autoAttach()); + } + } + for (Field field : key.getDeclaredFields()) { + field.setAccessible(true); + if (field.getAnnotation(Inject.class) == null) { + continue; + } + final TypeToken typeToken = TypeToken.of(field.getGenericType()); + final Annotation[] annotations = field.getAnnotations(); + + final LanternInjectableType injectableType = new LanternInjectableType<>(typeToken, annotations); + addInjectionEntry(injectableType, FieldAccessFactory.createSetter(field), ctx); + + // Component dependency handling + addDependency(ctx.dependencies, injectableType); + } + for (Method method : key.getDeclaredMethods()) { + method.setAccessible(true); + if (method.getAnnotation(Inject.class) == null) { + continue; + } + // Skip private methods, public methods were generated in the ShardsClassTransformer + if (Modifier.isPrivate(method.getModifiers())) { + continue; + } + final Type[] paramTypes = method.getGenericParameterTypes(); + if (paramTypes.length != 1) { + throw new IllegalStateException("Expected one argument for the method: " + method.getName() + " in " + key.getName()); + } + if (!ctx.methodKeys.add(new MethodKey(method))) { + continue; + } + final TypeToken typeToken = TypeToken.of(paramTypes[0]); + final Annotation[] annotations = method.getParameterAnnotations()[0]; + + final LanternInjectableType injectableType = new LanternInjectableType<>(typeToken, annotations); + addInjectionEntry(injectableType, LambdaFactory.createBiConsumer(method), ctx); + + // Component dependency handling + addDependency(ctx.dependencies, injectableType); + } + for (Class interf : key.getInterfaces()) { + collect(interf, ctx); + } + final Class superClass = key.getSuperclass(); + if (superClass != null && superClass != Object.class) { + collect(superClass, ctx); + } + } + + private static void addInjectionEntry(LanternInjectableType injectableType, + BiConsumer setter, CollectionContext ctx) { + final InjectionEntry injectionEntry = new InjectionEntry(injectableType, setter); + ctx.injectionEntries.add(injectionEntry); + // All the holder types must be checked if they are + // compatible, and throw a exception if otherwise + if (injectableType.getAnnotation(Holder.class) == null) { + return; + } + final Class type = injectableType.getType().getRawType(); + for (InjectionEntry otherEntry : ctx.holderInjectionEntries) { + final Class otherType = otherEntry.getValueType().getType().getRawType(); + // Check if it's a sub class or super class, they are both allowed + if (type.isAssignableFrom(otherType) && + otherType.isAssignableFrom(type)) { + continue; + } + // Check if it's not a final class and a interface could be implemented + if ((!Modifier.isFinal(type.getModifiers()) && otherType.isInterface()) || + (!Modifier.isFinal(otherType.getModifiers()) && type.isInterface())) { + continue; + } + throw new IllegalStateException("Found incompatible holder types within the dependencies: \"" + + type.getName() + "\" and \"" + otherType.getName() + "\""); + } + ctx.holderInjectionEntries.add(injectionEntry); + } + + private static void addDependency(List dependencies, InjectableType injectableType) { + final TypeToken typeToken = injectableType.getType(); + final Class raw = typeToken.getRawType(); + // Component dependency handling + Requirement dependencyType = null; + Class componentClass = null; + if (Shard.class.isAssignableFrom(raw)) { + dependencyType = Requirement.REQUIRED; + componentClass = raw; + } else { + /* + if (Dyn.class.isAssignableFrom(raw)) { + dependencyType = Requirement.REQUIRED_DYNAMIC; + componentClass = typeToken.resolveType(Dyn.class.getTypeParameters()[0]).getRawType(); + } else if (Opt.class.isAssignableFrom(raw)) { + dependencyType = Requirement.OPTIONAL; + componentClass = typeToken.resolveType(Opt.class.getTypeParameters()[0]).getRawType(); + } + if (componentClass == null || !Shard.class.isAssignableFrom(componentClass)) { + return; + }*/ + } + final boolean autoAttach = injectableType.getAnnotation(AutoAttach.class) != null; + DependencySpec.mergeAndAdd(dependencies, (Class) componentClass, dependencyType, autoAttach); + } + + /** + * A key which ignores the declaring class to check equality. + */ + private static final class MethodKey { + + private final Method method; + + private MethodKey(Method method) { + this.method = method; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + final MethodKey key = (MethodKey) obj; + return key.method.getName().equals(this.method.getName()) && + Arrays.equals(key.method.getParameterTypes(), this.method.getParameterTypes()); + } + + @Override + public int hashCode() { + return Objects.hash(this.method.getName(), this.method.getParameterTypes()); + } + } + + private final Class componentClass; + private final List dependencies; + private final List injectionEntries; + private final List> holderRequirements; + + private ComponentType(Class componentClass, List dependencies, + List injectionEntries, List> holderRequirements) { + this.componentClass = componentClass; + this.dependencies = dependencies; + this.injectionEntries = injectionEntries; + this.holderRequirements = holderRequirements; + } + + public Class getComponentClass() { + return this.componentClass; + } + + public List getDependencies() { + return this.dependencies; + } + + public List> getHolderRequirements() { + return this.holderRequirements; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass().getSimpleName()) + .add("component", this.componentClass.getName()) + .add("dependencies", this.dependencies) + .add("injectionEntries", this.injectionEntries) + .toString(); + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/DependencySpec.java b/src/main/java/org/lanternpowered/server/shards/internal/DependencySpec.java new file mode 100644 index 000000000..9bbd8d7cb --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/DependencySpec.java @@ -0,0 +1,132 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal; + +import com.google.common.base.MoreObjects; +import org.lanternpowered.api.shard.Shard; +import org.lanternpowered.server.shards.dependency.Requirement; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +public final class DependencySpec { + + static List merge(Iterable dependencies) { + final List newDependencies = new ArrayList<>(); + for (DependencySpec spec : dependencies) { + mergeAndAdd(newDependencies, spec.type, spec.dependencyType, spec.autoAttach); + } + return newDependencies; + } + + /** + * Adds and merges the given {@link DependencySpec} settings. + * + * @param dependencies The dependencies + * @param type The dependency class + * @param requiredType The dependency type + * @param autoAttach Auto attach mode + */ + static void mergeAndAdd(List dependencies, + Class type, Requirement requiredType, boolean autoAttach) { + final Iterator it = dependencies.iterator(); + while (it.hasNext()) { + final DependencySpec spec = it.next(); + if (spec.getType().isAssignableFrom(type)) { // We got a more specific dependency + requiredType = merge(spec.getDependencyType(), requiredType); + autoAttach = spec.getAutoAttach() || autoAttach; + } else if (type.isAssignableFrom(spec.getType())) { // We got a less specific type + requiredType = merge(spec.getDependencyType(), requiredType); + autoAttach = spec.getAutoAttach() || autoAttach; + type = spec.getType(); + } else { + continue; + } + it.remove(); + } + dependencies.add(new DependencySpec(type, requiredType, autoAttach)); + } + + private static Requirement merge(Requirement type, Requirement otherType) { + // Required is dominant + if (type == Requirement.REQUIRED || + otherType == Requirement.REQUIRED) { + return Requirement.REQUIRED; + // Then dynamic required + } else { + return Requirement.OPTIONAL; + } + } + + private final Class type; + private final boolean autoAttach; + private final Requirement dependencyType; + + DependencySpec(Class type, Requirement requiredType, boolean autoAttach) { + this.dependencyType = requiredType; + this.autoAttach = autoAttach; + this.type = type; + } + + public Requirement getDependencyType() { + return this.dependencyType; + } + + public Class getType() { + return this.type; + } + + public boolean getAutoAttach() { + return this.autoAttach; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper("Dependency") + .add("component", this.type.getName()) + .add("requireMode", this.dependencyType) + .add("autoAttach", this.autoAttach) + .toString(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof DependencySpec)) { + return false; + } + final DependencySpec other = (DependencySpec) o; + return this.type == other.type && + this.autoAttach == other.autoAttach && + this.dependencyType == other.dependencyType; + } + + @Override + public int hashCode() { + return Objects.hash(this.type, this.autoAttach, this.dependencyType); + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/KnownTypeSupplier.java b/src/main/java/org/lanternpowered/server/shards/internal/KnownTypeSupplier.java new file mode 100644 index 000000000..fd9494920 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/KnownTypeSupplier.java @@ -0,0 +1,69 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.reflect.TypeToken; + +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +/** + * Represents a {@link Supplier} of which the + * returned object {@link TypeToken} is known. + * + * @param The type + */ +@SuppressWarnings("unchecked") +public final class KnownTypeSupplier implements Supplier { + + @Nullable private TypeToken typeToken; + private final Supplier supplier; + + public KnownTypeSupplier(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public T get() { + final T object = this.supplier.get(); + if (this.typeToken != null) { + checkState(this.typeToken.getRawType().isInstance(object)); + } else { + this.typeToken = TypeToken.of((Class) object.getClass()); + } + return object; + } + + public TypeToken getObjectType() { + if (this.typeToken == null) { + this.typeToken = TypeToken.of((Class) this.supplier.get().getClass()); + } + return this.typeToken; + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/LanternInjectionRegistry.java b/src/main/java/org/lanternpowered/server/shards/internal/LanternInjectionRegistry.java new file mode 100644 index 000000000..35ab68c5e --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/LanternInjectionRegistry.java @@ -0,0 +1,305 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.google.common.base.Objects; +import com.google.common.reflect.TypeToken; +import com.google.inject.BindingAnnotation; +import com.google.inject.ConfigurationException; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.Provider; +import org.lanternpowered.server.shards.AnnotatedBindingBuilder; +import org.lanternpowered.server.shards.BindingBuilder; +import org.lanternpowered.server.shards.InjectableType; +import org.lanternpowered.server.shards.InjectionRegistry; +import org.lanternpowered.server.shards.Opt; +import org.lanternpowered.server.shards.SupplierFactory; +import org.lanternpowered.server.shards.internal.inject.LanternInjectableType; +import org.lanternpowered.server.util.SystemProperties; +import org.lanternpowered.server.util.TypeTokenHelper; +import org.lanternpowered.server.util.supplier.ObjectSupplierGenerator; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +@SuppressWarnings({"unchecked", "ConstantConditions"}) +public class LanternInjectionRegistry implements InjectionRegistry { + + public static final boolean SHARDS_DEBUG_MODE = SystemProperties.get().getBooleanProperty("lantern.debug-shards"); + + private static final LanternInjectionRegistry instance = new LanternInjectionRegistry(null); + + public static LanternInjectionRegistry get() { + return instance; + } + + private static final class FactoryEntry { + + @Nullable private final TypeToken typeToken; + private final SupplierFactory factory; + + private FactoryEntry(@Nullable TypeToken typeToken, SupplierFactory factory) { + this.typeToken = typeToken; + this.factory = factory; + } + } + + /** + * The {@link TypeVariable} of the {@link Opt} class. + */ + private final TypeVariable optTypeVariable = Opt.class.getTypeParameters()[0]; + + /** + * All the registered {@link SupplierFactory}s. + */ + private final List> supplierFactories = new ArrayList<>(); + + /** + * A cache for all the {@link Supplier}s. + */ + private final LoadingCache, Optional>> supplierCache = + Caffeine.newBuilder().build(type -> (Optional) loadSupplier(type)); + + /** + * The cached internal {@link Injector}. + */ + @Nullable private Injector internalGuiceInjector; + + /** + * All the installed guice modules. + */ + private final List guiceModules = new ArrayList<>(); + + @Nullable private final LanternInjectionRegistry parent; + + private LanternInjectionRegistry(@Nullable LanternInjectionRegistry parent) { + this.parent = parent; + // Register some inbuilt factories, only on the root registry + if (parent == null) { + bind(Opt.class).to((registry, injectableType) -> { + // Extract the generic type from the Opt, and lookup a factory for that one + final TypeToken newTypeToken = injectableType.getType().resolveType(this.optTypeVariable); + final InjectableType newInjectableType = new LanternInjectableType<>(newTypeToken, injectableType.getAnnotations()); + final Optional> optSupplier = registry.getSupplier(newInjectableType); + if (!optSupplier.isPresent()) { + return Optional.of(Opt::empty); + } + final Supplier supplier = optSupplier.get(); + return Optional.of(() -> Opt.of(supplier.get())); + }); + } + } + + /** + * Finds a {@link Supplier} for the given {@link InjectableType}. + * + * @param type The injectable type + * @param The object type + * @return The supplier, if found + */ + private Optional> loadSupplier(InjectableType type) { + for (FactoryEntry entry : this.supplierFactories) { + // Check if the factory type is assignable to the requested type + if (entry.typeToken != null && !TypeTokenHelper.isAssignable(entry.typeToken, type.getType())) { + continue; + } + final Optional> optSupplier = ((SupplierFactory) entry.factory).get(this, type); + if (optSupplier.isPresent()) { + return optSupplier; + } + } + // Fallback to guice + if (this.internalGuiceInjector == null) { + if (this.guiceModules.isEmpty()) { + return this.parent == null ? Optional.empty() : this.parent.getSupplier(type); + } + // Create a new injector + this.internalGuiceInjector = Guice.createInjector(this.guiceModules); + } + Annotation bindAnnotation = null; + // Try to find a @BindingAnnotation + for (Annotation annotation : type.getAnnotations()) { + if (annotation.annotationType().getAnnotation(BindingAnnotation.class) != null) { + if (bindAnnotation != null) { + throw new IllegalStateException("Found multiple BindingAnnotations on: " + type); + } + bindAnnotation = annotation; + } + } + final Type genericType = type.getType().getType(); + final Key key = (Key) (bindAnnotation == null ? Key.get(genericType) : Key.get(genericType, bindAnnotation)); + try { + final Provider provider = this.internalGuiceInjector.getProvider(key); + return Optional.of(provider::get); + } catch (ConfigurationException e) { + return this.parent == null ? Optional.empty() : this.parent.getSupplier(type); + } + } + + @Override + public AnnotatedBindingBuilder bind(TypeToken objectType) { + checkNotNull(objectType, "objectType"); + return new LanternBindingBuilder<>(objectType); + } + + @Override + public void bindFactory(SupplierFactory supplierFactory) { + checkNotNull(supplierFactory, "supplierFactory"); + this.supplierFactories.add(new FactoryEntry<>(null, supplierFactory)); + } + + @Override + public Optional> getSupplier(TypeToken objectType) { + checkNotNull(objectType, "objectType"); + return getSupplier(new LanternInjectableType<>(objectType)); + } + + @Override + public Optional> getSupplier(InjectableType injectableType) { + return (Optional) this.supplierCache.get(injectableType); + } + + @Override + public void install(Module module) { + checkNotNull(module, "module"); + // Add the guice module + this.guiceModules.add(module); + // Force the guice injector to rebuild + this.internalGuiceInjector = null; + } + + @Override + public InjectionRegistry newChild() { + return new LanternInjectionRegistry(this); + } + + private final class LanternBindingBuilder implements AnnotatedBindingBuilder { + + private final TypeToken typeToken; + + @Nullable private Annotation annotation; + @Nullable private Class annotationType; + + private LanternBindingBuilder(TypeToken typeToken) { + this.typeToken = typeToken; + } + + @Override + public BindingBuilder annotatedWith(Class annotationType) { + checkNotNull(annotationType, "annotationType"); + this.annotationType = annotationType; + return this; + } + + @Override + public BindingBuilder annotatedWith(Annotation annotation) { + checkNotNull(annotation, "annotation"); + this.annotation = annotation; + return this; + } + + @Override + public void to(Class implementation) { + checkNotNull(implementation, "implementation"); + // Generate a supplier and bind it + to(ObjectSupplierGenerator.getSupplier(implementation)); + } + + @Override + public void to(Supplier supplier) { + checkNotNull(supplier, "supplier"); + to((registry, injectableType) -> Optional.of(supplier)); + } + + @Override + public void to(SupplierFactory supplierFactory) { + checkNotNull(supplierFactory, "supplierFactory"); + // Apply the annotation requirements, if present + if (this.annotation != null) { + supplierFactory = new AnnotationSupplierFactory<>(supplierFactory, this.annotation); + } else if (this.annotationType != null) { + supplierFactory = new AnnotationTypeSupplierFactory<>(supplierFactory, this.annotationType); + } + // Register the supplier factory + supplierFactories.add(new FactoryEntry<>(this.typeToken, supplierFactory)); + } + } + + private static final class AnnotationSupplierFactory implements SupplierFactory { + + private final SupplierFactory supplierFactory; + private final Annotation annotation; + + private AnnotationSupplierFactory(SupplierFactory supplierFactory, Annotation annotation) { + this.supplierFactory = supplierFactory; + this.annotation = annotation; + } + + @Override + public Optional> get(InjectionRegistry registry, InjectableType injectableType) { + final Annotation annotation = injectableType.getAnnotation(this.annotation.getClass()); + if (!Objects.equal(this.annotation, annotation)) { + return Optional.empty(); + } + return this.supplierFactory.get(registry, injectableType); + } + } + + private static final class AnnotationTypeSupplierFactory implements SupplierFactory { + + private final SupplierFactory supplierFactory; + private final Class annotationType; + + private AnnotationTypeSupplierFactory(SupplierFactory supplierFactory, + Class annotationType) { + this.supplierFactory = supplierFactory; + this.annotationType = annotationType; + } + + @Override + public Optional> get(InjectionRegistry registry, InjectableType injectableType) { + final Annotation annotation = injectableType.getAnnotation(this.annotationType); + if (annotation == null) { + return Optional.empty(); + } + return this.supplierFactory.get(registry, injectableType); + } + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/event/LanternShardeventBus.java b/src/main/java/org/lanternpowered/server/shards/internal/event/LanternShardeventBus.java new file mode 100644 index 000000000..88e716baf --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/event/LanternShardeventBus.java @@ -0,0 +1,253 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal.event; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.reflect.TypeToken; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; +import kotlin.jvm.functions.Function1; +import kotlin.reflect.KClass; +import org.jetbrains.annotations.NotNull; +import org.lanternpowered.server.game.Lantern; +import org.lanternpowered.api.shard.event.Shardevent; +import org.lanternpowered.api.shard.event.ShardeventBus; +import org.lanternpowered.api.shard.event.ShardeventListener; +import org.lanternpowered.server.util.LambdaFactory; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@SuppressWarnings({"unchecked", "ConstantConditions"}) +public class LanternShardeventBus implements ShardeventBus { + + @Override public void post(@NotNull Class eventType, @NotNull Function0 supplier) { + + } + + @Override public void register(@NotNull Class eventType, @NotNull Function1 handler) { + + } + + @Override public void unregister(@NotNull Class eventType, @NotNull Function1 handler) { + + } + + @Override public void post(@NotNull KClass eventType, @NotNull Function0 supplier) { + + } + + @Override public void register(@NotNull KClass eventType, @NotNull Function1 handler) { + + } + + @Override public void unregister(@NotNull KClass eventType, @NotNull Function1 handler) { + + } + + /** + * A handler that isn't targeting a specific handler object. This means + * that this handler can be reused for multiple objects. This is only + * used for method event handlers. + */ + final static class UntargetedMethodHandler { + + final Class eventClass; + final BiConsumer handler; + + UntargetedMethodHandler(Class eventClass, + BiConsumer handler) { + this.eventClass = eventClass; + this.handler = handler; + } + } + + /** + * Cache all the method event handlers, things will get registered/unregistered + * quite a lot so avoid regenerating classes/reflection lookups. + */ + private static final Map, List> untargetedMethodHandlersByObjectClass = new ConcurrentHashMap<>(); + + /** + * A map with all the generated {@link UntargetedMethodHandler} for a specific method. + */ + private static final Map untargetedMethodHandlerByMethod = new ConcurrentHashMap<>(); + + private static List loadUntargetedMethodHandlers(Class objectClass) { + final List handlers = new ArrayList<>(); + loadUntargetedMethodHandlers(handlers, objectClass); + return handlers; + } + + private static void loadUntargetedMethodHandlers( + List handlers, Class objectClass) { + for (Method method : objectClass.getDeclaredMethods()) { + // Only add entries for methods that are declared, to avoid + // duplicate entries when methods are overridden. + if (method.getDeclaredAnnotation(ShardeventListener.class) == null) { + continue; + } + checkState(!Modifier.isStatic(method.getModifiers()), + "ShardeventListener methods cannot be static"); + checkState(method.getReturnType().equals(void.class), + "ShardeventListener methods cannot have a return type"); + checkState(method.getParameterCount() == 1 && Shardevent.class.isAssignableFrom(method.getParameterTypes()[0]), + "ShardeventListener methods can only have one parameter and must extend Shardevent"); + // Generate a Shardevent handler for the method + final UntargetedMethodHandler methodHandler = untargetedMethodHandlerByMethod + .computeIfAbsent(method, method1 -> new UntargetedMethodHandler( + method1.getParameterTypes()[0], LambdaFactory.createBiConsumer(method1))); + handlers.add(methodHandler); + } + for (Class interf : objectClass.getInterfaces()) { + loadUntargetedMethodHandlers(handlers, interf); + } + objectClass = objectClass.getSuperclass(); + if (objectClass != null && objectClass != Object.class) { + loadUntargetedMethodHandlers(handlers, objectClass); + } + } + + /** + * Gets all the {@link UntargetedMethodHandler}s that are + * present on the given object class. + * + * @param objectClass The object class + * @return The untargeted method handlers + */ + private static List getUntargetedMethodHandlers(Class objectClass) { + return untargetedMethodHandlersByObjectClass.computeIfAbsent(objectClass, LanternShardeventBus::loadUntargetedMethodHandlers); + } + + private final Multimap, ShardeventHandler> handlersByClass = HashMultimap.create(); + private final LoadingCache, List> handlerCache = Caffeine.newBuilder().build(this::loadHandlers); + + private List loadHandlers(Class eventClass) { + final List handlers = new ArrayList<>(); + final Set> types = (Set) TypeToken.of(eventClass).getTypes().rawTypes(); + synchronized (this.handlersByClass) { + for (Class type : types) { + if (Shardevent.class.isAssignableFrom(type)) { + handlers.addAll(this.handlersByClass.get(type)); + } + } + } + return handlers; + } + + @Override + public void post(Shardevent event) { + post(event, this.handlerCache.get(event.getClass())); + } + + /* + @Override + public void post(Class eventType, Supplier supplier) { + checkNotNull(eventType, "eventType"); + checkNotNull(supplier, "supplier"); + final List handlers = this.handlerCache.get(eventType); + if (!handlers.isEmpty()) { + post(supplier.get(), handlers); + } + }*/ + + private void post(Shardevent event, List handlers) { + checkNotNull(event, "event"); + for (ShardeventHandler handler : handlers) { + try { + handler.handle(event); + } catch (Exception e) { + Lantern.getLogger().error("Failed to handle Shardevent", e); + } + } + } + + @Override + public void register(Object object) { + checkNotNull(object, "object"); + final List untargetedHandlers = getUntargetedMethodHandlers(object.getClass()); + synchronized (this.handlersByClass) { + for (UntargetedMethodHandler handler : untargetedHandlers) { + this.handlersByClass.put(handler.eventClass, new ShardeventHandler(object) { + @Override + public void handle(Shardevent event) { + handler.handler.accept(this.handle, event); + } + }); + } + } + this.handlerCache.invalidateAll(); + } +/* + @Override + public void register(Class eventType, Consumer handler) { + checkNotNull(eventType, "eventType"); + checkNotNull(handler, "handler"); + synchronized (this.handlersByClass) { + this.handlersByClass.put(eventType, new ShardeventHandler(handler) { + @Override + public void handle(Shardevent event) { + ((Consumer) this.handle).accept(event); + } + }); + } + this.handlerCache.invalidateAll(); + }*/ + + @Override + public void unregister(Object object) { + checkNotNull(object, "object"); + synchronized (this.handlersByClass) { + this.handlersByClass.values().removeIf(shardeventHandler -> shardeventHandler.handle == object); + } + this.handlerCache.invalidateAll(); + } + + /* + @Override + public void unregister(Class eventType, Consumer handler) { + checkNotNull(eventType, "eventType"); + checkNotNull(handler, "handler"); + synchronized (this.handlersByClass) { + this.handlersByClass.get(eventType).removeIf(shardeventHandler -> shardeventHandler.handle == handler); + } + this.handlerCache.invalidateAll(); + }*/ +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/event/ShardeventHandler.java b/src/main/java/org/lanternpowered/server/shards/internal/event/ShardeventHandler.java new file mode 100644 index 000000000..da6114eb0 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/event/ShardeventHandler.java @@ -0,0 +1,44 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal.event; + +import org.lanternpowered.api.shard.event.Shardevent; + +public abstract class ShardeventHandler { + + protected final Object handle; + + ShardeventHandler(Object handle) { + this.handle = handle; + } + + /** + * Handles the {@link Shardevent}. + * + * @param event The shard event + */ + public abstract void handle(Shardevent event); +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/event/package-info.java b/src/main/java/org/lanternpowered/server/shards/internal/event/package-info.java new file mode 100644 index 000000000..1a7120a07 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/event/package-info.java @@ -0,0 +1,27 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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. + */ +@org.spongepowered.api.util.annotation.NonnullByDefault +package org.lanternpowered.server.shards.internal.event; diff --git a/src/main/java/org/lanternpowered/server/shards/internal/inject/InjectionEntry.java b/src/main/java/org/lanternpowered/server/shards/internal/inject/InjectionEntry.java new file mode 100644 index 000000000..babab889a --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/inject/InjectionEntry.java @@ -0,0 +1,59 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal.inject; + +import com.google.common.base.MoreObjects; +import org.lanternpowered.server.shards.InjectableType; + +import java.util.function.BiConsumer; + +public final class InjectionEntry implements BiConsumer { + + private final InjectableType valueType; + private final BiConsumer injectionSetter; + + public InjectionEntry(InjectableType valueType, BiConsumer injectionSetter) { + this.injectionSetter = injectionSetter; + this.valueType = valueType; + } + + @Override + public void accept(Object target, Object value) { + this.injectionSetter.accept(target, value); + } + + public InjectableType getValueType() { + return this.valueType; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass().getSimpleName()) + .add("valueType", this.valueType) + .add("setter", this.injectionSetter) + .toString(); + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/inject/LanternInjectableType.java b/src/main/java/org/lanternpowered/server/shards/internal/inject/LanternInjectableType.java new file mode 100644 index 000000000..216830920 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/inject/LanternInjectableType.java @@ -0,0 +1,108 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal.inject; + +import com.google.common.base.MoreObjects; +import com.google.common.reflect.TypeToken; +import org.lanternpowered.server.shards.InjectableType; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Objects; + +import javax.annotation.Nullable; + +@SuppressWarnings({"unchecked", "ConstantConditions"}) +public final class LanternInjectableType implements InjectableType { + + private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0]; + + private final TypeToken type; + private final Annotation[] annotations; + + private int hashCode; + + public LanternInjectableType(TypeToken type) { + this(type, EMPTY_ANNOTATIONS); + } + + public LanternInjectableType(TypeToken type, Annotation[] annotations) { + this.annotations = annotations; + this.type = type; + } + + @Override + public TypeToken getType() { + return this.type; + } + + @Nullable + @Override + public A getAnnotation(Class annotationClass) { + return (A) Arrays.stream(this.annotations).filter(annotationClass::isInstance).findFirst().orElse(null); + } + + @Nullable + @Override + public A getDeclaredAnnotation(Class annotationClass) { + return null; + } + + @Override + public Annotation[] getAnnotations() { + return this.annotations.length == 0 ? EMPTY_ANNOTATIONS : Arrays.copyOf(this.annotations, this.annotations.length); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return EMPTY_ANNOTATIONS; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof LanternInjectableType)) { + return false; + } + final LanternInjectableType o = (LanternInjectableType) obj; + return o.type.equals(this.type) && Arrays.equals(o.annotations, this.annotations); + } + + @Override + public int hashCode() { + if (this.hashCode == 0) { + this.hashCode = Objects.hash(this.type, this.annotations); + } + return this.hashCode; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("type", this.type) + .add("annotations", Arrays.toString(this.annotations)) + .toString(); + } +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/inject/package-info.java b/src/main/java/org/lanternpowered/server/shards/internal/inject/package-info.java new file mode 100644 index 000000000..88d67d808 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/inject/package-info.java @@ -0,0 +1,27 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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. + */ +@org.spongepowered.api.util.annotation.NonnullByDefault +package org.lanternpowered.server.shards.internal.inject; diff --git a/src/main/java/org/lanternpowered/server/shards/internal/package-info.java b/src/main/java/org/lanternpowered/server/shards/internal/package-info.java new file mode 100644 index 000000000..265ae1672 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/package-info.java @@ -0,0 +1,27 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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. + */ +@org.spongepowered.api.util.annotation.NonnullByDefault +package org.lanternpowered.server.shards.internal; diff --git a/src/main/java/org/lanternpowered/server/shards/internal/test/TestExplosiveComponent.java b/src/main/java/org/lanternpowered/server/shards/internal/test/TestExplosiveComponent.java new file mode 100644 index 000000000..87d79330a --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/test/TestExplosiveComponent.java @@ -0,0 +1,69 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.shards.internal.test; + +import com.google.inject.Inject; +import org.lanternpowered.api.entity.shard.AIShard; +import org.lanternpowered.api.entity.shard.ExplosiveShard; +import org.lanternpowered.server.shards.Holder; +import org.lanternpowered.server.shards.Opt; +import org.spongepowered.api.entity.Entity; + +import java.util.Optional; + +public class TestExplosiveComponent extends ExplosiveShard { + + // Fields will be made public by class transformation + + @Inject private Opt aiComponentOpt; // If it would be useful... + + // Inject the holder + @Holder public Entity entity; + + @Override + public Entity getDetonator() { + return null; + } + + @Override + public void detonate() { + System.out.println("BOOM!"); + } + + // Start generated stuff + + // The lock of this component + public final Object $shards_internal_lock = new Object(); + + @Inject + public void setEntity(Entity entity) { + this.entity = entity; + } + + // The holder, only used if there isn't a @Holder field + // public ComponentHolder $shards_internal_holder; + +} diff --git a/src/main/java/org/lanternpowered/server/shards/internal/test/package-info.java b/src/main/java/org/lanternpowered/server/shards/internal/test/package-info.java new file mode 100644 index 000000000..95dd18a67 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/internal/test/package-info.java @@ -0,0 +1,27 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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. + */ +@org.spongepowered.api.util.annotation.NonnullByDefault +package org.lanternpowered.server.shards.internal.test; diff --git a/src/main/java/org/lanternpowered/server/shards/package-info.java b/src/main/java/org/lanternpowered/server/shards/package-info.java new file mode 100644 index 000000000..616dfb962 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/shards/package-info.java @@ -0,0 +1,27 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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. + */ +@org.spongepowered.api.util.annotation.NonnullByDefault +package org.lanternpowered.server.shards; diff --git a/src/main/java/org/lanternpowered/server/util/DefineableClassLoader.java b/src/main/java/org/lanternpowered/server/util/DefineableClassLoader.java index 5e4cd30eb..bd72213d4 100644 --- a/src/main/java/org/lanternpowered/server/util/DefineableClassLoader.java +++ b/src/main/java/org/lanternpowered/server/util/DefineableClassLoader.java @@ -27,6 +27,10 @@ public class DefineableClassLoader extends ClassLoader { + static { + ClassLoader.registerAsParallelCapable(); + } + public DefineableClassLoader(ClassLoader parent) { super(parent); } diff --git a/src/main/java/org/lanternpowered/server/util/supplier/ObjectSupplierGenerator.java b/src/main/java/org/lanternpowered/server/util/supplier/ObjectSupplierGenerator.java new file mode 100644 index 000000000..e36f91432 --- /dev/null +++ b/src/main/java/org/lanternpowered/server/util/supplier/ObjectSupplierGenerator.java @@ -0,0 +1,62 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.util.supplier; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import org.lanternpowered.server.util.LambdaFactory; + +import java.util.function.Supplier; + +/** + * A generator for {@link Supplier} to supply + * classes with empty constructors. + */ +@SuppressWarnings("unchecked") +public final class ObjectSupplierGenerator { + + /** + * Attempts to generate a {@link Supplier} for the + * given object class. The object must have a empty + * constructor. + * + * @param objectType The object class + * @param The object type + * @return The supplier + */ + public static Supplier getSupplier(Class objectType) { + checkNotNull(objectType, "objectType"); + return (Supplier) supplierCache.get(objectType); + } + + /** + * A cache for all the {@link Supplier}s. + */ + private static final LoadingCache, Supplier> supplierCache = + Caffeine.newBuilder().weakValues().build(LambdaFactory::createSupplier); +} diff --git a/src/main/java/org/lanternpowered/server/world/LanternWorld.java b/src/main/java/org/lanternpowered/server/world/LanternWorld.java index 2828aaa8d..7494b550e 100644 --- a/src/main/java/org/lanternpowered/server/world/LanternWorld.java +++ b/src/main/java/org/lanternpowered/server/world/LanternWorld.java @@ -57,12 +57,12 @@ import org.lanternpowered.server.effect.sound.LanternSoundType; import org.lanternpowered.server.entity.LanternEntity; import org.lanternpowered.server.entity.LanternEntityType; +import org.lanternpowered.api.entity.event.EntityWorldShardevent; 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.vanilla.message.type.play.MessagePlayOutParticleEffect; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutRecord; @@ -1389,10 +1389,7 @@ private LanternEntity addEntity(LanternEntity entity) { if (entity1 != null) { return entity1; } - final EntityProtocolType entityProtocolType = entity.getEntityProtocolType(); - if (entityProtocolType != null) { - this.entityProtocolManager.add(entity, entityProtocolType); - } + entity.getShardeventBus().post(new EntityWorldShardevent.Join(this)); entity.setPositionAndWorld(this, entity.getPosition()); return null; } @@ -1408,7 +1405,7 @@ private void pulseEntities() { chunk.removeEntity(entity, lastChunk.getY()); } } - this.entityProtocolManager.remove(entity); + entity.getShardeventBus().post(new EntityWorldShardevent.Leave(this)); this.entitiesByUniqueId.remove(entity.getUniqueId()); } else { final Vector3i lastChunkSection = entity.getLastChunkSectionCoords(); diff --git a/src/main/kotlin/org/lanternpowered/api/Lantern.kt b/src/main/kotlin/org/lanternpowered/api/Lantern.kt index 982ac8127..96c088509 100644 --- a/src/main/kotlin/org/lanternpowered/api/Lantern.kt +++ b/src/main/kotlin/org/lanternpowered/api/Lantern.kt @@ -27,6 +27,7 @@ package org.lanternpowered.api import org.lanternpowered.api.event.EventManager import org.lanternpowered.api.plugin.PluginManager +import org.lanternpowered.api.shard.ShardRegistry import org.lanternpowered.api.x.XGameRegistry import org.lanternpowered.api.x.cause.XCauseStackManager import org.spongepowered.api.scheduler.Scheduler @@ -42,4 +43,5 @@ object Lantern { @JvmStatic inline val eventManager: EventManager get() = Sponge.getEventManager() @JvmStatic inline val scheduler: Scheduler get() = Sponge.getScheduler() @JvmStatic inline val serviceManager: ServiceManager get() = Sponge.getServiceManager() + @JvmStatic inline val shardRegistry: ShardRegistry get() = this.registry.shardRegistry } diff --git a/src/main/kotlin/org/lanternpowered/api/entity/event/EntityWorldShardevent.kt b/src/main/kotlin/org/lanternpowered/api/entity/event/EntityWorldShardevent.kt new file mode 100644 index 000000000..9751a34ce --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/event/EntityWorldShardevent.kt @@ -0,0 +1,46 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.event + +import org.lanternpowered.api.shard.event.Shardevent +import org.lanternpowered.server.world.LanternWorld +import org.spongepowered.api.entity.Entity +import org.spongepowered.api.world.World + +abstract class EntityWorldShardevent internal constructor() : Shardevent { + + abstract val world: LanternWorld + + /** + * Is thrown when the [Entity] is added to the [World]. + */ + data class Join(override val world: LanternWorld) : EntityWorldShardevent() + + /** + * Is thrown when the [Entity] left the [World]. + */ + data class Leave(override val world: LanternWorld) : EntityWorldShardevent() +} diff --git a/src/main/kotlin/org/lanternpowered/api/entity/event/TrackerChangeShardevent.kt b/src/main/kotlin/org/lanternpowered/api/entity/event/TrackerChangeShardevent.kt new file mode 100644 index 000000000..77b55d25e --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/event/TrackerChangeShardevent.kt @@ -0,0 +1,50 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.event + +import org.lanternpowered.server.entity.LanternEntity +import org.lanternpowered.server.entity.living.player.LanternPlayer +import org.lanternpowered.api.shard.event.Shardevent + +abstract class TrackerChangeShardevent internal constructor() : Shardevent { + + /** + * The [LanternPlayer]s. + */ + abstract val players: List + + /** + * Is thrown when one or more [LanternPlayer] + * started tracking a [LanternEntity]. + */ + data class Add(override val players: List) : TrackerChangeShardevent() + + /** + * Is thrown when one or more [LanternPlayer] + * stopped tracking a [LanternEntity]. + */ + data class Remove(override val players: List) : TrackerChangeShardevent() +} diff --git a/src/main/kotlin/org/lanternpowered/api/entity/event/animation/CollectEntityAnimation.kt b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/CollectEntityAnimation.kt new file mode 100644 index 000000000..66166c3a8 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/CollectEntityAnimation.kt @@ -0,0 +1,39 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.event.animation + +import org.lanternpowered.api.shard.event.Shardevent +import org.spongepowered.api.entity.living.Living + +/** + * Is posted when a entity is being collected by a + * other entity, the collector. This will play the pull + * animation on the client. + */ +data class CollectEntityAnimation( + val collector: Living, + val collectedItemsCount: Int = Integer.MAX_VALUE +) : Shardevent diff --git a/src/main/kotlin/org/lanternpowered/api/entity/event/animation/DamageEntityAnimation.kt b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/DamageEntityAnimation.kt new file mode 100644 index 000000000..fc1bbcff8 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/DamageEntityAnimation.kt @@ -0,0 +1,33 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.event.animation + +import org.lanternpowered.api.shard.event.Shardevent + +/** + * Can be posted to play the entity damage animation. + */ +object DamageEntityAnimation : Shardevent diff --git a/src/main/java/org/lanternpowered/server/entity/event/LoveModeEntityEvent.java b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/EntityLoveModeAnimation.kt similarity index 77% rename from src/main/java/org/lanternpowered/server/entity/event/LoveModeEntityEvent.java rename to src/main/kotlin/org/lanternpowered/api/entity/event/animation/EntityLoveModeAnimation.kt index 2796633b5..a1ca7f468 100644 --- a/src/main/java/org/lanternpowered/server/entity/event/LoveModeEntityEvent.java +++ b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/EntityLoveModeAnimation.kt @@ -23,25 +23,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.event; +package org.lanternpowered.api.entity.event.animation + +import org.lanternpowered.api.shard.event.Shardevent /** * Plays the love mode animation of the entity. Spawns * hearts above the entity. */ -public final class LoveModeEntityEvent implements EntityEvent { - - public static LoveModeEntityEvent of() { - return INSTANCE; - } - - private static final LoveModeEntityEvent INSTANCE = new LoveModeEntityEvent(); - - private LoveModeEntityEvent() { - } - - @Override - public EntityEventType type() { - return EntityEventType.ALIVE; - } -} +object EntityLoveModeAnimation : Shardevent diff --git a/src/main/kotlin/org/lanternpowered/api/entity/event/animation/SwingHandAnimation.kt b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/SwingHandAnimation.kt new file mode 100644 index 000000000..4889a6c32 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/event/animation/SwingHandAnimation.kt @@ -0,0 +1,34 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.event.animation + +import org.lanternpowered.api.shard.event.Shardevent +import org.spongepowered.api.data.type.HandType + +/** + * Can be posted to trigger a hand swinging animation. + */ +data class SwingHandAnimation(val handType: HandType) : Shardevent diff --git a/src/main/java/org/lanternpowered/server/entity/event/CollectEntityEvent.java b/src/main/kotlin/org/lanternpowered/api/entity/shard/AIShard.kt similarity index 58% rename from src/main/java/org/lanternpowered/server/entity/event/CollectEntityEvent.java rename to src/main/kotlin/org/lanternpowered/api/entity/shard/AIShard.kt index bf2668a1b..e7d359d8d 100644 --- a/src/main/java/org/lanternpowered/server/entity/event/CollectEntityEvent.java +++ b/src/main/kotlin/org/lanternpowered/api/entity/shard/AIShard.kt @@ -23,36 +23,36 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.event; +package org.lanternpowered.api.entity.shard -import static com.google.common.base.Preconditions.checkNotNull; +import org.lanternpowered.api.shard.Shard +import org.spongepowered.api.entity.Entity +import org.spongepowered.api.entity.ai.Goal +import org.spongepowered.api.entity.ai.GoalType +import org.spongepowered.api.entity.ai.task.AITask +import org.spongepowered.api.entity.living.Agent -import org.spongepowered.api.entity.living.Living; +import java.util.Optional -public final class CollectEntityEvent implements EntityEvent { - - private final Living collector; - private final int collectedItemsCount; - - public CollectEntityEvent(Living collector, int collectedItemsCount) { - this.collector = checkNotNull(collector, "collector"); - this.collectedItemsCount = collectedItemsCount; - } - - public CollectEntityEvent(Living collector) { - this(collector, Integer.MAX_VALUE); - } - - public Living getCollector() { - return this.collector; - } - - public int getCollectedItemsCount() { - return this.collectedItemsCount; - } - - @Override - public EntityEventType type() { - return EntityEventType.DEATH_OR_ALIVE; - } +/** + * The [AIShard] shard type. + */ +abstract class AIShard : Shard() { + + /** + * The current target, usually according to the various + * [AITask]s that are acting on this agent. + *

Setting is usually used to bypass what the [AITask]s are + * deciding to be the target. + */ + abstract var target: Entity? + + /** + * Gets a [Goal] based on the [GoalType]. + * + * @param type GoalType to lookup + * @param Inferred agent type + * @return The goal or [Optional.empty] if not found. + */ + abstract fun getGoal(type: GoalType): Goal? } diff --git a/src/main/kotlin/org/lanternpowered/api/entity/shard/BossShard.kt b/src/main/kotlin/org/lanternpowered/api/entity/shard/BossShard.kt new file mode 100644 index 000000000..da25ba113 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/shard/BossShard.kt @@ -0,0 +1,40 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.shard + +import org.lanternpowered.api.boss.BossBar +import org.lanternpowered.api.shard.Shard + +/** + * The [BossShard] shard type. + */ +abstract class BossShard : Shard() { + + /** + * The [BossBar] that will be used by the boss. + */ + abstract val bossBar: BossBar +} diff --git a/src/main/kotlin/org/lanternpowered/api/entity/shard/ExplosiveShard.kt b/src/main/kotlin/org/lanternpowered/api/entity/shard/ExplosiveShard.kt new file mode 100644 index 000000000..2cfd21516 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/shard/ExplosiveShard.kt @@ -0,0 +1,45 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.shard + +import org.lanternpowered.api.shard.Shard +import org.spongepowered.api.entity.Entity + +/** + * The [ExplosiveShard] shard type. + */ +abstract class ExplosiveShard : Shard() { + + /** + * The direct detonator of the [ExplosiveShard]. + */ + abstract val detonator: Entity? + + /** + * Detonates this explosive as soon as possible. + */ + abstract fun detonate() +} diff --git a/src/main/kotlin/org/lanternpowered/api/entity/shard/FusedExplosiveShard.kt b/src/main/kotlin/org/lanternpowered/api/entity/shard/FusedExplosiveShard.kt new file mode 100644 index 000000000..d9ee2d34e --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/shard/FusedExplosiveShard.kt @@ -0,0 +1,56 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.entity.shard + +import org.spongepowered.api.data.key.Keys + +/** + * A extension of the [ExplosiveShard]. + */ +abstract class FusedExplosiveShard : ExplosiveShard() { + + /** + * Whether this explosive is currently primed. + */ + abstract val primed: Boolean + + /** + * Primes this explosive to detonate after the amount of ticks that + * this entity explodes in defined by [Keys.FUSE_DURATION]. + * + * @throws IllegalStateException If explosive is already primed + */ + @Throws(IllegalStateException::class) + abstract fun prime() + + /** + * Cancels an actively primed explosive. + * + * @throws IllegalStateException If explosive is not primed + */ + @Throws(IllegalStateException::class) + abstract fun defuse() +} diff --git a/src/main/java/org/lanternpowered/server/entity/event/RefreshAbilitiesPlayerEvent.java b/src/main/kotlin/org/lanternpowered/api/entity/shard/MerchantShard.kt similarity index 73% rename from src/main/java/org/lanternpowered/server/entity/event/RefreshAbilitiesPlayerEvent.java rename to src/main/kotlin/org/lanternpowered/api/entity/shard/MerchantShard.kt index 3f669a893..5e9b3f62d 100644 --- a/src/main/java/org/lanternpowered/server/entity/event/RefreshAbilitiesPlayerEvent.java +++ b/src/main/kotlin/org/lanternpowered/api/entity/shard/MerchantShard.kt @@ -23,21 +23,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.event; +package org.lanternpowered.api.entity.shard -public final class RefreshAbilitiesPlayerEvent implements EntityEvent { +import org.lanternpowered.api.shard.Shard +import org.spongepowered.api.entity.living.Humanoid - public static RefreshAbilitiesPlayerEvent of() { - return INSTANCE; - } - - private static final RefreshAbilitiesPlayerEvent INSTANCE = new RefreshAbilitiesPlayerEvent(); - - private RefreshAbilitiesPlayerEvent() { - } +/** + * The [MerchantShard] shard type. + */ +abstract class MerchantShard : Shard() { - @Override - public EntityEventType type() { - return EntityEventType.DEATH_OR_ALIVE; - } + /** + * The customer that is currently trading with + * the merchant, or null if none. + *

Setting the customer may open a new + * trading window for this customer. + */ + abstract var customer: Humanoid? } diff --git a/src/main/kotlin/org/lanternpowered/server/ext/DataExt.kt b/src/main/kotlin/org/lanternpowered/api/ext/DataExt.kt similarity index 97% rename from src/main/kotlin/org/lanternpowered/server/ext/DataExt.kt rename to src/main/kotlin/org/lanternpowered/api/ext/DataExt.kt index 0d409a20b..5a8c1ea00 100644 --- a/src/main/kotlin/org/lanternpowered/server/ext/DataExt.kt +++ b/src/main/kotlin/org/lanternpowered/api/ext/DataExt.kt @@ -25,9 +25,8 @@ */ @file:Suppress("NOTHING_TO_INLINE") -package org.lanternpowered.server.ext +package org.lanternpowered.api.ext -import org.lanternpowered.api.ext.* import org.spongepowered.api.data.DataTransactionResult import org.spongepowered.api.data.value.ValueContainer import org.spongepowered.api.data.value.mutable.CompositeValueStore diff --git a/src/main/kotlin/org/lanternpowered/api/ext/ShardExt.kt b/src/main/kotlin/org/lanternpowered/api/ext/ShardExt.kt new file mode 100644 index 000000000..98735180e --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/ext/ShardExt.kt @@ -0,0 +1,34 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.ext + +import org.lanternpowered.api.shard.ShardHolder +import org.lanternpowered.api.shard.Shard + +inline fun > ShardHolder.getShard() = getShard(T::class) +inline fun > ShardHolder.addShard() = addShard(T::class) +inline fun ShardHolder.getShardOfType() = getShardOfType(T::class) +inline fun ShardHolder.getShardsOfType() = getShardsOfType(T::class) diff --git a/src/main/kotlin/org/lanternpowered/api/ext/UtilExt.kt b/src/main/kotlin/org/lanternpowered/api/ext/UtilExt.kt index 483609067..79c53a74d 100644 --- a/src/main/kotlin/org/lanternpowered/api/ext/UtilExt.kt +++ b/src/main/kotlin/org/lanternpowered/api/ext/UtilExt.kt @@ -37,3 +37,16 @@ fun Tuple.toPair() = Pair(first, second) fun Pair.toTuple() = Tuple(first, second) inline fun Any?.uncheckedCast(): T = this as T + +inline fun T?.ifNotNull(fn: (T) -> Unit) { + if (this != null) { + fn(this) + } +} + +inline fun T?.mapIfNotNull(fn: (T) -> R): R? { + if (this != null) { + return fn(this) + } + return null +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/AutoAttach.kt b/src/main/kotlin/org/lanternpowered/api/shard/AutoAttach.kt new file mode 100644 index 000000000..e091f08e0 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/AutoAttach.kt @@ -0,0 +1,44 @@ +package org.lanternpowered.api.shard + +import org.lanternpowered.api.ext.* +import kotlin.reflect.KClass + +/** + * Represents the [Shard] auto attach support. + */ +sealed class AutoAttach> { + + /** + * A custom [AutoAttach] implementation so that you + * can provide a fallback [Shard] implementation. + * + * The [default] will still be used if the provided + * custom one isn't applicable. + * + * @param type The type of the shard implementation + */ + data class Custom>(val type: Class) : AutoAttach() { + + constructor(type: KClass) : this(type.java) + } + + companion object { + + private object Disabled : AutoAttach>() + private object Default : AutoAttach>() + + /** + * Provides the disabled state of the auto attach support. + */ + fun > disabled(): AutoAttach = Disabled.uncheckedCast() + + /** + * A default implementation of a [Shard] will be attempted + * to be used when the [Shard] needs to be attached. + * + * Default implementations can be registered through the + * [ShardRegistry]. + */ + fun > default(): AutoAttach = Default.uncheckedCast() + } +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/Shard.kt b/src/main/kotlin/org/lanternpowered/api/shard/Shard.kt new file mode 100644 index 000000000..1431cae37 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/Shard.kt @@ -0,0 +1,123 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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. + */ +@file:Suppress("unused") + +package org.lanternpowered.api.shard + +/** + * Represents a shard (component) that can be attached to + * a [ShardHolder]. For every direct subclass of this + * class will only be one instance allowed per [ShardHolder]. + * Meaning that for example only object of type {code TestComponent} + * (direct subclass) can be present, even when extending the + * {code TestComponent} is this not allowed. However, these components + * can be swapped out with [ShardHolder.replaceShard]. + * If you want multiple [Shard] to implement a specific type, + * use a interface instead, there are also utilities in the [ShardHolder] + * to get all the instances of this type. + * + * @param T The type of the shard, this should + * always be the direct subclass of shard. + */ +abstract class Shard> { + + /** + * Provides access to the [ShardHolder] of this [Shard], + * will throw [IllegalStateException] if there is no holder. + */ + val holder: ShardHolder + get() = this.theHolder ?: throw IllegalStateException("No holder is available.") + + // Useful properties to access shards of the shard holder + + /** + * Provides a read only property that provides the holder as the given type. A + * cast [Exception] will be thrown if the holder is of the wrong type. + * + * This property will also cause the component attachment to fail early when + * the holder isn't of the required type. + */ + protected inline fun requireHolderOfType() = RequiredHolderOfTypeProperty(T::class) + + /** + * Provides a read only property that provides the first [Shard] that is of the given type. + * Will only be attached when the shard is requested + * through this property.This + * type doesn't have to be a shard type, it can be any available interface. A [ShardNotAvailableException] + * will be thrown if no shard was found. + */ + protected inline fun requiredShardOfType() = RequiredFirstShardOfTypeProperty(T::class) + + /** + * Provides a read only property that provides the first [Shard] that is of the given type. This + * type doesn't have to be a shard type, it can be any available interface. A null will be returned + * if no shard was found. + */ + protected inline fun optionalShardOfType() = OptionalFirstShardOfTypeProperty(T::class) + + /** + * Provides a read only property that provides the [Shard]s that are of the given type. This + * type doesn't have to be a shard type, it can be any available interface. + */ + protected inline fun shardsOfType() = ShardsOfTypeProperty(T::class) + + /** + * Provides a read only property that provides the [Shard] of the given shard type. + * A [ShardNotAvailableException] will be thrown if no shard was found. + * + * @param autoAttach The auto attach feature that should be used if the + * given shard isn't present on the shard holder. + * Will only be attached when the shard is requested + * through this property. + */ + protected inline fun > requiredShard(autoAttach: AutoAttach = AutoAttach.disabled()) = + RequiredShardProperty(T::class, autoAttach) + + /** + * Provides a read only property that provides the [Shard] of the given shard type. + * A null will be returned if no shard was found. + * + * @param autoAttach The auto attach feature that should be used if the + * given shard isn't present on the shard holder. + * Will only be attached when the shard is requested + * through this property. + */ + protected inline fun > optionalShard(autoAttach: AutoAttach = AutoAttach.disabled()) = + OptionalShardProperty(T::class, autoAttach) + + // The following fields have only internal access only, DO NOT MODIFY! + + /** + * The holder of this [Shard], used to + * prevent multiple holders per shard. + */ + private val theHolder: ShardHolder? = null + + /** + * The lock of this [Shard]. + */ + private val lock = Any() +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/ShardHolder.kt b/src/main/kotlin/org/lanternpowered/api/shard/ShardHolder.kt new file mode 100644 index 000000000..b62a866e5 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/ShardHolder.kt @@ -0,0 +1,203 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.shard + +import org.lanternpowered.api.Lantern +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.shard.event.ShardeventBus +import java.util.Optional +import kotlin.reflect.KClass + +interface ShardHolder { + + /** + * The [ShardeventBus] from this [ShardHolder]. + */ + val shardeventBus: ShardeventBus + + /** + * Attempts to attach a [Shard] of the given type to this [ShardHolder], + * if there is already a shard of the given type, then that instance will be returned. + * + * This method expects that when the [Shard] is either abstract or an interface, + * a default implementation is provided through the [ShardRegistry]. + * + * @param type The shard type to attach + * @return The shard instance + */ + fun > addShard(type: Class) = addShard(Lantern.shardRegistry.getType(type)).optional() + + /** + * Attempts to attach a [Shard] of the given type to this [ShardHolder], + * if there is already a shard of the given type, then that instance will be returned. + * + * This method expects that when the [Shard] is either abstract or an interface, + * a default implementation is provided through the [ShardRegistry]. + * + * @param type The shard type to attach + * @return The shard instance + */ + fun > addShard(type: KClass): T? = addShard(Lantern.shardRegistry.getType(type)) + + /** + * Attempts to attach a [Shard] of the given type to this [ShardHolder], + * if there is already a shard of the given type, then that instance will be returned. + * + * This method expects that when the [Shard] is either abstract or an interface, + * a default implementation is provided through the [ShardRegistry]. + * + * @param type The shard type to attach + * @return The shard instance + */ + fun > addShard(type: ShardType): T + + /** + * Attempts to attach the given [Shard] to this [ShardHolder]. The method + * will return `true` if it was successful, adding a shard will be successful if there + * isn't a [Shard] with the same type. + * + * @param shard The shard to attach + * @return Whether the attachment was successful + * @throws IllegalArgumentException If the given shard instance is already attached + */ + @Throws(IllegalArgumentException::class) + fun addShard(shard: Shard<*>): Boolean + + /** + * Attempts to replace the [Shard] attached to the given type. If there are multiple + * ones found, only the first possible one will be replaced. If there were none found, the + * [Shard] will just be attached to this holder. + * + * @param type The shard type to replace + * @param component The new shard instance + * @return Whether the replacement was successful + * @throws IllegalArgumentException If the given shard instance is already attached + */ + @Throws(IllegalArgumentException::class) + fun , I : T> replaceShard(type: Class, component: I): Boolean + + /** + * Attempts to replace the [Shard] attached to the given type. If there are multiple + * ones found, only the first possible one will be replaced. If there were none found, the + * [Shard] will just be attached to this holder. + * + * @param type The shard type to replace + * @param component The new shard instance + * @return Whether the replacement was successful + * @throws IllegalArgumentException If the given shard instance is already attached + */ + @Throws(IllegalArgumentException::class) + fun , I : T> replaceShard(type: KClass, component: I): Boolean + + /** + * Attempts to replace the [Shard] attached to the given type. If there are multiple + * ones found, only the first possible one will be replaced. If there were none found, the + * [Shard] will just be attached to this holder. + * + * @param type The shard type to replace + * @param component The new shard type + * @return The newly attached shard + * @throws IllegalArgumentException If the given shard instance is already attached + */ + @Throws(IllegalArgumentException::class) + fun , I : T> replaceShard(type: Class, component: Class): Optional + + /** + * Attempts to replace the [Shard] attached to the given type. If there are multiple + * ones found, only the first possible one will be replaced. If there were none found, the + * [Shard] will just be attached to this holder. + * + * @param type The shard type to replace + * @param component The new shard type + * @return The newly attached shard + * @throws IllegalArgumentException If the given shard instance is already attached + */ + @Throws(IllegalArgumentException::class) + fun , I : T> replaceShard(type: KClass, component: Class): I? + + /** + * Gets the [Shard] of the given type if present, otherwise [Optional.empty]. + * + * Only the first [Shard] will be returned if there + * are multiple ones for the given type. + * + * @param type The shard type + * @return The shard instance if present + */ + fun > getShard(type: Class): Optional + + /** + * Gets the [Shard] of the given type if present, otherwise [Optional.empty]. + * + * Only the first [Shard] will be returned if there + * are multiple ones for the given type. + * + * @param type The shard type + * @return The shard instance if present + */ + fun > getShard(type: KClass): T? + + /** + * Attempts to remove all the [Shard]s that match the given type, all the shards + * that were removed will be present in the result [Collection]. + * + * @param type The shard type + * @return A collection with the removed shards + */ + fun > removeShard(type: Class): Optional + + /** + * Gets the first [Shard] of the given type. + * + * @param type The type + * @return A collection with the components + */ + fun getShardOfType(type: Class): Optional + + /** + * Gets the first [Shard] of the given type. + * + * @param type The type + * @return A collection with the components + */ + fun getShardOfType(type: KClass): T? + + /** + * Gets a [Collection] with all the [Shard]s of the given type. + * + * @param type The type + * @return A collection with the components + */ + fun getShardsOfType(type: Class): Collection + + /** + * Gets a [Collection] with all the [Shard]s of the given type. + * + * @param type The type + * @return A collection with the components + */ + fun getShardsOfType(type: KClass): Collection +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/ShardNotAvailableException.kt b/src/main/kotlin/org/lanternpowered/api/shard/ShardNotAvailableException.kt new file mode 100644 index 000000000..24fd9c4bb --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/ShardNotAvailableException.kt @@ -0,0 +1,37 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.shard + +/** + * A [Exception] that is thrown when a [Shard] couldn't be found. + */ +class ShardNotAvailableException : RuntimeException { + + constructor() + constructor(message: String): super(message) + constructor(message: String, cause: Throwable): super(message, cause) + constructor(cause: Throwable): super(cause) +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/ShardProperty.kt b/src/main/kotlin/org/lanternpowered/api/shard/ShardProperty.kt new file mode 100644 index 000000000..1b5b4f7f6 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/ShardProperty.kt @@ -0,0 +1,79 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.shard + +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import kotlin.reflect.full.cast + +class RequiredHolderOfTypeProperty(private val type: KClass) : ReadOnlyProperty, T> { + + override fun getValue(thisRef: Shard<*>, property: KProperty<*>): T { + return type.cast(thisRef.holder) + } +} + +class OptionalFirstShardOfTypeProperty(private val type: KClass) : ReadOnlyProperty, T?> { + + override fun getValue(thisRef: Shard<*>, property: KProperty<*>): T? { + return thisRef.holder.getShardOfType(this.type) + } +} + +class RequiredFirstShardOfTypeProperty(private val type: KClass) : ReadOnlyProperty, T> { + + override fun getValue(thisRef: Shard<*>, property: KProperty<*>): T { + return thisRef.holder.getShardOfType(this.type) ?: throw ShardNotAvailableException("No shard of type $type is available.") + } +} + +class ShardsOfTypeProperty(private val type: KClass) : ReadOnlyProperty, Collection> { + + override fun getValue(thisRef: Shard<*>, property: KProperty<*>): Collection { + return thisRef.holder.getShardsOfType(this.type) + } +} + +class OptionalShardProperty>( + private val type: KClass, + private val autoAttach: AutoAttach +) : ReadOnlyProperty, T?> { + + override fun getValue(thisRef: Shard<*>, property: KProperty<*>): T? { + return thisRef.holder.getShard(this.type) + } +} + +class RequiredShardProperty>( + private val type: KClass, + private val autoAttach: AutoAttach +) : ReadOnlyProperty, T> { + + override fun getValue(thisRef: Shard<*>, property: KProperty<*>): T { + return thisRef.holder.getShard(this.type) ?: throw ShardNotAvailableException("No shard of type $type is available.") + } +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/ShardRegistry.kt b/src/main/kotlin/org/lanternpowered/api/shard/ShardRegistry.kt new file mode 100644 index 000000000..7865cff12 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/ShardRegistry.kt @@ -0,0 +1,83 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.shard + +import kotlin.reflect.KClass + +interface ShardRegistry { + + /** + * Gets the [ShardType] for the given shard class. + * + * The generic [Shard] type must be resolvable, otherwise is the + * given shard class invalid to have a [ShardType], and a + * [IllegalArgumentException] will be thrown. + */ + fun > getType(shardClass: KClass) = getType(shardClass.java) + + /** + * Gets the [ShardType] for the given shard class. + * + * The generic [Shard] type must be resolvable, otherwise is the + * given shard class invalid to have a [ShardType], and a + * [IllegalArgumentException] will be thrown. + */ + fun > getType(shardClass: Class): ShardType + + /** + * Registers the default implementation for the [ShardType]. Multiple default + * implementations can be registered, the first applicable one will + * be used when a default one is requested. + */ + fun , I : T> registerDefault(shardType: ShardType, defaultImpl: Class) + + /** + * Registers the default implementation for the [ShardType]. Multiple default + * implementations can be registered, the first applicable one will + * be used when a default one is requested. + */ + fun , I : T> registerDefault(shardType: ShardType, defaultImpl: KClass) { + registerDefault(shardType, defaultImpl.java) + } + + /** + * Registers the default implementation for the [Shard] type. Multiple default + * implementations can be registered, the first applicable one will + * be used when a default one is requested. + */ + fun , I : T> registerDefault(shardType: KClass, defaultImpl: KClass) { + registerDefault(getType(shardType), defaultImpl) + } + + /** + * Registers the default implementation for the [Shard] type. Multiple default + * implementations can be registered, the first applicable one will + * be used when a default one is requested. + */ + fun , I : T> registerDefault(shardType: Class, defaultImpl: Class) { + registerDefault(getType(shardType), defaultImpl) + } +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/ShardType.kt b/src/main/kotlin/org/lanternpowered/api/shard/ShardType.kt new file mode 100644 index 000000000..3682d18f9 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/ShardType.kt @@ -0,0 +1,13 @@ +package org.lanternpowered.api.shard + +/** + * Represents the type of a [Shard]. + */ +interface ShardType> { + + /** + * The base [Shard] class which + * represents this shard type. + */ + val type: Class +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/event/Shardevent.kt b/src/main/kotlin/org/lanternpowered/api/shard/event/Shardevent.kt new file mode 100644 index 000000000..cebe89990 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/event/Shardevent.kt @@ -0,0 +1,43 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.shard.event + +import org.lanternpowered.api.shard.Shard +import org.lanternpowered.api.shard.ShardHolder +import org.spongepowered.api.event.Event +import org.spongepowered.api.event.cause.Cause + +/** + * Similar to a [Event], but will be used internally + * by a [Shard] and [ShardHolder]. All + * these events will be handled using a internal bus that + * won't affect other [ShardHolder]s. + * + * These events should be fast to update internal component + * states, the creation of unnecessary objects like + * [Cause] in most cases should be avoided. + */ +interface Shardevent diff --git a/src/main/kotlin/org/lanternpowered/api/shard/event/ShardeventBus.kt b/src/main/kotlin/org/lanternpowered/api/shard/event/ShardeventBus.kt new file mode 100644 index 000000000..41352d7b0 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/event/ShardeventBus.kt @@ -0,0 +1,121 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.shard.event + +import kotlin.reflect.KClass + +/** + * Represents the bus to handle [Shardevent]s. + */ +interface ShardeventBus { + + /** + * Posts the [Shardevent]. + * + * @param event The event + */ + fun post(event: Shardevent) + + /** + * Posts the [Shardevent] provided by the given supplier if + * there are handlers registered for the given [Shardevent] type. + * + * @param eventType The event type + * @param supplier The event supplier + * @param T The type of the event + */ + fun post(eventType: Class, supplier: () -> T) + + /** + * Posts the [Shardevent] provided by the given supplier if + * there are handlers registered for the given [Shardevent] type. + * + * @param eventType The event type + * @param supplier The event supplier + * @param T The type of the event + */ + fun post(eventType: KClass, supplier: () -> T) { + post(eventType.java, supplier) + } + + /** + * Registers all the methods of the object annotated with + * [ShardeventListener] as event handlers. + * + * @param any The object + */ + fun register(any: Any) + + /** + * Registers the handler for the given [Shardevent] type. + * + * @param eventType The event type + * @param handler The handler + * @param T The type of the event + */ + fun register(eventType: KClass, handler: (T) -> Unit) { + register(eventType.java, handler) + } + + /** + * Registers the handler for the given [Shardevent] type. + * + * @param eventType The event type + * @param handler The handler + * @param T The type of the event + */ + fun register(eventType: Class, handler: (T) -> Unit) + + /** + * Unregisters all the handlers methods that + * got registered by using the object. + * + * @param any The object + */ + fun unregister(any: Any) + + /** + * Unregisters the given handler from the + * given [Shardevent] type. + * + * @param eventType The event type + * @param handler The handler + * @param T The type of the event + */ + fun unregister(eventType: KClass, handler: (T) -> Unit) { + unregister(eventType.java, handler) + } + + /** + * Unregisters the given handler from the + * given [Shardevent] type. + * + * @param eventType The event type + * @param handler The handler + * @param T The type of the event + */ + fun unregister(eventType: Class, handler: (T) -> Unit) +} diff --git a/src/main/kotlin/org/lanternpowered/api/shard/event/ShardeventListener.kt b/src/main/kotlin/org/lanternpowered/api/shard/event/ShardeventListener.kt new file mode 100644 index 000000000..47e6ef336 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/shard/event/ShardeventListener.kt @@ -0,0 +1,37 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.api.shard.event + +/** + * Marks a method as a handler for a [Shardevent]. The method must + * have a `void` return type and only one parameter that extends + * [Shardevent]. The method can be public, private or protected. + * Private methods will be transformed at runtime to allow public + * access without reflection. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class ShardeventListener diff --git a/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt b/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt index 953f11cc6..016364a6f 100644 --- a/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt +++ b/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt @@ -26,9 +26,15 @@ package org.lanternpowered.api.x import org.lanternpowered.api.GameRegistry +import org.lanternpowered.api.shard.ShardRegistry import org.lanternpowered.api.x.text.XTextFactory interface XGameRegistry : GameRegistry { override fun getTextFactory(): XTextFactory + + /** + * The shard registry. + */ + val shardRegistry: ShardRegistry } diff --git a/src/main/kotlin/org/lanternpowered/server/entity/event/RequestPlayerAbilitiesRefreshShardevent.kt b/src/main/kotlin/org/lanternpowered/server/entity/event/RequestPlayerAbilitiesRefreshShardevent.kt new file mode 100644 index 000000000..4ecac954a --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/event/RequestPlayerAbilitiesRefreshShardevent.kt @@ -0,0 +1,34 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.event + +import org.lanternpowered.server.entity.living.player.LanternPlayer +import org.lanternpowered.api.shard.event.Shardevent + +/** + * Can be thrown to request a [LanternPlayer]s its abilities to be refreshed. + */ +object RequestPlayerAbilitiesRefreshShardevent : Shardevent diff --git a/src/main/kotlin/org/lanternpowered/server/entity/event/SpectateEntityShardevent.kt b/src/main/kotlin/org/lanternpowered/server/entity/event/SpectateEntityShardevent.kt new file mode 100644 index 000000000..cd96e4121 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/event/SpectateEntityShardevent.kt @@ -0,0 +1,34 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.event + +import org.lanternpowered.api.shard.event.Shardevent +import org.spongepowered.api.entity.Entity + +/** + * Is thrown to start/stop the target entity from spectating a target entity. + */ +data class SpectateEntityShardevent(val spectatedEntity: Entity?) : Shardevent diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/IEntity.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/IEntity.kt new file mode 100644 index 000000000..0b396d047 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/IEntity.kt @@ -0,0 +1,60 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces + +import org.lanternpowered.api.entity.shard.MerchantShard +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.shard.ShardHolder +import org.lanternpowered.server.entity.interfaces.explosive.IExplosive +import org.spongepowered.api.entity.AreaEffectCloud +import org.spongepowered.api.entity.EnderCrystal +import org.spongepowered.api.entity.Entity +import org.spongepowered.api.entity.ExperienceOrb +import org.spongepowered.api.entity.FallingBlock +import org.spongepowered.api.entity.Item +import org.spongepowered.api.entity.ShulkerBullet +import org.spongepowered.api.entity.Tamer +import org.spongepowered.api.entity.living.Humanoid +import org.spongepowered.api.item.merchant.Merchant + +interface IEntity : Entity, ShardHolder + +interface IAreaEffectCloud : IEntity, AreaEffectCloud +interface IEnderCrystal : IExplosive, EnderCrystal +interface IExperienceOrb : IEntity, ExperienceOrb +interface IFallingBlock : IEntity, FallingBlock +interface IItem : IEntity, Item +interface IShulkerBullet : IEntity, ShulkerBullet +interface ITamerEntity : IEntity, Tamer + +interface IMerchantEntity : IEntity, Merchant { + + @JvmDefault override fun setCustomer(humanoid: Humanoid?) { + getShard()?.customer = humanoid + } + + @JvmDefault override fun getCustomer() = getShard()?.customer.optional() +} diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/explosive/IExplosive.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/explosive/IExplosive.kt new file mode 100644 index 000000000..90b50b3af --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/explosive/IExplosive.kt @@ -0,0 +1,61 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces.explosive + +import org.lanternpowered.api.ext.* +import org.lanternpowered.server.entity.interfaces.IEntity +import org.lanternpowered.api.entity.shard.ExplosiveShard +import org.lanternpowered.api.entity.shard.FusedExplosiveShard +import org.spongepowered.api.entity.explosive.Explosive +import org.spongepowered.api.entity.explosive.FusedExplosive +import org.spongepowered.api.entity.explosive.PrimedTNT +import org.spongepowered.api.entity.living.Living + +interface IExplosive : Explosive, IEntity { + + @JvmDefault override fun detonate() { + getShard()?.detonate() + } +} + +interface IFusedExplosive : IExplosive, FusedExplosive { + + @JvmDefault override fun isPrimed() = getShardOfType()?.primed ?: false + + @JvmDefault override fun prime() { + getShardOfType()?.prime() + } + + @JvmDefault override fun defuse() { + getShardOfType()?.defuse() + } +} + + +interface IPrimedTNT : IFusedExplosive, PrimedTNT { + + @JvmDefault override fun getDetonator() = (getShard()?.detonator as? Living).optional() +} diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/hanging/IHanging.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/hanging/IHanging.kt new file mode 100644 index 000000000..58fecf700 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/hanging/IHanging.kt @@ -0,0 +1,38 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces.hanging + +import org.lanternpowered.server.entity.interfaces.IEntity +import org.spongepowered.api.entity.hanging.Hanging +import org.spongepowered.api.entity.hanging.ItemFrame +import org.spongepowered.api.entity.hanging.LeashHitch +import org.spongepowered.api.entity.hanging.Painting + +interface IHanging : IEntity, Hanging + +interface IItemFrame : IHanging, ItemFrame +interface ILeashHitch : IHanging, LeashHitch +interface IPainting : IHanging, Painting diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/ILiving.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/ILiving.kt new file mode 100644 index 000000000..6f3a62bd9 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/ILiving.kt @@ -0,0 +1,85 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces.living + +import org.lanternpowered.api.entity.shard.AIShard +import org.lanternpowered.api.ext.* +import org.lanternpowered.server.entity.AbstractArmorEquipable +import org.lanternpowered.server.entity.interfaces.IEntity +import org.lanternpowered.server.entity.interfaces.IMerchantEntity +import org.spongepowered.api.entity.Entity +import org.spongepowered.api.entity.ai.GoalType +import org.spongepowered.api.entity.living.Aerial +import org.spongepowered.api.entity.living.Ageable +import org.spongepowered.api.entity.living.Agent +import org.spongepowered.api.entity.living.Ambient +import org.spongepowered.api.entity.living.Aquatic +import org.spongepowered.api.entity.living.ArmorStand +import org.spongepowered.api.entity.living.Bat +import org.spongepowered.api.entity.living.Creature +import org.spongepowered.api.entity.living.Hostile +import org.spongepowered.api.entity.living.Human +import org.spongepowered.api.entity.living.Humanoid +import org.spongepowered.api.entity.living.Living +import org.spongepowered.api.entity.living.Ranger +import org.spongepowered.api.entity.living.Squid +import org.spongepowered.api.entity.living.Villager + +interface ILiving : IEntity, Living + +interface IAgent : ILiving, Agent { + + override fun getTarget() = getShard()?.target.optional() + + override fun setTarget(target: Entity?) { + getShard()?.target = target + } + + override fun getGoal(type: GoalType) = getShard()?.getGoal(type).optional() +} + +interface ICreature : IAgent, Creature +interface IRanger : IAgent, Ranger + +interface IAgeable : ICreature, Ageable { + + @JvmDefault override fun setScaleForAge() {} +} + +interface IAerial : IAgent, Aerial +interface IAmbient : IAgent, Ambient +interface IAquatic : ICreature, Aquatic +interface IHostile : ILiving, Hostile + +interface IArmorStand : ILiving, ArmorStand, AbstractArmorEquipable +interface IBat : IAerial, IAmbient, Bat +interface ISquid : IAquatic, Squid +interface IHumanoid : ILiving, Humanoid, AbstractArmorEquipable +interface IHuman : IHumanoid, Human + +interface IVillager : IAgeable, IMerchantEntity, Villager { + @JvmDefault override fun isTrading() = customer.isPresent +} diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/animal/IAnimal.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/animal/IAnimal.kt new file mode 100644 index 000000000..ecb77e11c --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/animal/IAnimal.kt @@ -0,0 +1,69 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces.living.animal + +import org.lanternpowered.server.entity.interfaces.living.IAgeable +import org.spongepowered.api.entity.living.animal.Animal +import org.spongepowered.api.entity.living.animal.Chicken +import org.spongepowered.api.entity.living.animal.Cow +import org.spongepowered.api.entity.living.animal.Donkey +import org.spongepowered.api.entity.living.animal.Horse +import org.spongepowered.api.entity.living.animal.Llama +import org.spongepowered.api.entity.living.animal.Mooshroom +import org.spongepowered.api.entity.living.animal.Mule +import org.spongepowered.api.entity.living.animal.Ocelot +import org.spongepowered.api.entity.living.animal.Parrot +import org.spongepowered.api.entity.living.animal.Pig +import org.spongepowered.api.entity.living.animal.PolarBear +import org.spongepowered.api.entity.living.animal.Rabbit +import org.spongepowered.api.entity.living.animal.RideableHorse +import org.spongepowered.api.entity.living.animal.Sheep +import org.spongepowered.api.entity.living.animal.SkeletonHorse +import org.spongepowered.api.entity.living.animal.Wolf +import org.spongepowered.api.entity.living.animal.ZombieHorse + +interface IAnimal : IAgeable, Animal + +interface IChicken : IAnimal, Chicken +interface IOcelot : IAnimal, Ocelot +interface IParrot : IAnimal, Parrot +interface IPig : IAnimal, Pig +interface IPolarBear : IAnimal, PolarBear +interface IRabbit : IAnimal, Rabbit +interface ISheep : IAnimal, Sheep +interface ISkeletonHorse : IHorseBase, SkeletonHorse +interface IWolf : IAnimal, Wolf +interface IZombieHorse : IHorseBase, ZombieHorse + +interface ICow : IAnimal, Cow +interface IMooshroom : ICow, Mooshroom + +interface IHorseBase : IAnimal, Horse + +interface ILlama : IHorseBase, Llama +interface IHorse : IHorseBase, RideableHorse +interface IDonkey : IHorseBase, Donkey +interface IMule : IHorseBase, Mule diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/golem/IGolem.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/golem/IGolem.kt new file mode 100644 index 000000000..fd6078efc --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/living/golem/IGolem.kt @@ -0,0 +1,38 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces.living.golem + +import org.lanternpowered.server.entity.interfaces.living.ICreature +import org.spongepowered.api.entity.living.golem.Golem +import org.spongepowered.api.entity.living.golem.IronGolem +import org.spongepowered.api.entity.living.golem.Shulker +import org.spongepowered.api.entity.living.golem.SnowGolem + +interface IGolem : ICreature, Golem + +interface IIronGolem : IGolem, IronGolem +interface IShulker : IGolem, Shulker +interface ISnowGolem : IGolem, SnowGolem diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/vehicle/IBoat.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/vehicle/IBoat.kt new file mode 100644 index 000000000..3650e6e62 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/vehicle/IBoat.kt @@ -0,0 +1,69 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces.vehicle + +import org.lanternpowered.server.data.key.LanternKeys +import org.lanternpowered.server.entity.interfaces.IEntity +import org.spongepowered.api.block.BlockTypes +import org.spongepowered.api.entity.vehicle.Boat +import org.spongepowered.api.util.AABB + +interface IBoat : IEntity, Boat { + + override fun isInWater(): Boolean { + val loc = location + if (loc.block.type != BlockTypes.WATER) { + return false + } + var aabb: AABB? = boundingBox.orElse(null) ?: return false + val pos = loc.blockPosition.toDouble() + aabb = aabb!!.expand(0.0, -0.1, 0.0) + val min = pos.add(0.05, 0.05, 0.05) + val max = pos.add(0.95, 0.95, 0.95) + aabb = AABB(aabb!!.min.max(min), aabb.max.min(max)) + return !loc.extent.getIntersectingBlockCollisionBoxes(aabb).isEmpty() + } + + override fun getMaxSpeed(): Double = get(LanternKeys.MAX_SPEED).orElse(DEFAULT_MAX_SPEED) + override fun setMaxSpeed(maxSpeed: Double) { offer(LanternKeys.MAX_SPEED, maxSpeed) } + + override fun canMoveOnLand(): Boolean = get(LanternKeys.CAN_MOVE_ON_LAND).orElse(DEFAULT_CAN_MOVE_ON_LAND) + override fun setMoveOnLand(moveOnLand: Boolean) { offer(LanternKeys.CAN_MOVE_ON_LAND, moveOnLand) } + + override fun getOccupiedDeceleration(): Double = get(LanternKeys.OCCUPIED_DECELERATION).orElse(DEFAULT_OCCUPIED_DECELERATION) + override fun setOccupiedDeceleration(occupiedDeceleration: Double) { offer(LanternKeys.OCCUPIED_DECELERATION, occupiedDeceleration) } + + override fun getUnoccupiedDeceleration(): Double = get(LanternKeys.UNOCCUPIED_DECELERATION).orElse(DEFAULT_UNOCCUPIED_DECELERATION) + override fun setUnoccupiedDeceleration(unoccupiedDeceleration: Double) { offer(LanternKeys.UNOCCUPIED_DECELERATION, unoccupiedDeceleration) } + + companion object { + + const val DEFAULT_MAX_SPEED = 0.4 + const val DEFAULT_OCCUPIED_DECELERATION = 0.0 + const val DEFAULT_UNOCCUPIED_DECELERATION = 0.8 + const val DEFAULT_CAN_MOVE_ON_LAND = false + } +} diff --git a/src/main/kotlin/org/lanternpowered/server/entity/interfaces/weather/IWeatherEntity.kt b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/weather/IWeatherEntity.kt new file mode 100644 index 000000000..e7ab0e187 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/interfaces/weather/IWeatherEntity.kt @@ -0,0 +1,41 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.interfaces.weather + +import org.lanternpowered.server.data.key.LanternKeys +import org.lanternpowered.server.entity.interfaces.IEntity +import org.spongepowered.api.entity.weather.Lightning +import org.spongepowered.api.entity.weather.WeatherEffect + +interface IWeatherEffect : IEntity, WeatherEffect { + + @JvmDefault override fun isEffect(): Boolean = get(LanternKeys.IS_EFFECT).orElse(false) + @JvmDefault override fun setEffect(effect: Boolean) { + offer(LanternKeys.IS_EFFECT, effect) + } +} + +interface ILightning : IWeatherEffect, Lightning diff --git a/src/main/kotlin/org/lanternpowered/server/entity/shard/DefaultBossShard.kt b/src/main/kotlin/org/lanternpowered/server/entity/shard/DefaultBossShard.kt new file mode 100644 index 000000000..a7c24a461 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/shard/DefaultBossShard.kt @@ -0,0 +1,105 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.shard + +import org.lanternpowered.api.boss.BossBar +import org.lanternpowered.api.boss.BossBarColors +import org.lanternpowered.api.boss.BossBarOverlays +import org.lanternpowered.api.entity.shard.BossShard +import org.lanternpowered.api.text.Text +import org.lanternpowered.server.entity.LanternEntity +import org.lanternpowered.api.entity.event.TrackerChangeShardevent +import org.lanternpowered.server.shards.OnAttach +import org.lanternpowered.server.shards.OnUpdate +import org.lanternpowered.api.shard.event.ShardeventListener +import org.spongepowered.api.data.key.Keys +import org.spongepowered.api.entity.living.player.Player + +class DefaultBossShard : BossShard() { + + private val entityHolder: LanternEntity by requireHolderOfType() + + private lateinit var defaultName: Text + private lateinit var theBossBar: BossBar + + /** + * The boss bar that will be displayed to the surrounding players. + */ + override val bossBar: BossBar get() = this.theBossBar + + @OnAttach + private fun onAttach() { + // Get a name we can fallback to if there is no display name + this.defaultName = Text(this.entityHolder.type.translation) + // Create the boss bar + this.theBossBar = BossBar(this.defaultName) { + overlay(BossBarOverlays.NOTCHED_12) // Is this OK? + color(BossBarColors.PURPLE) + visible(true) + } + // TODO: More boss bar settings? + } + + /** + * Updates the [BossBar] for the + * given health and max health. + */ + private fun update() { + val health = this.entityHolder.require(Keys.HEALTH) + val maxHealth = this.entityHolder.require(Keys.MAX_HEALTH) + // Update the percent of the boss bar + this.bossBar.percent = Math.min(health / maxHealth, 1.0).toFloat() + // Update the name of the boss bar + this.bossBar.name = this.entityHolder[Keys.DISPLAY_NAME].orElse(this.defaultName) + } + + @OnUpdate(pulseRate = 5) + private fun onUpdate() { + if (this.bossBar.players.isEmpty()) { // Only update if necessary. + return + } + update() + } + + /** + * Is called when trackers are added. + */ + @ShardeventListener + private fun onTrackerChangeAdd(event: TrackerChangeShardevent.Add) { + if (this.bossBar.players.isEmpty()) { + update() // Update things when no players were present + } + this.bossBar.addPlayers(event.players as List) + } + + /** + * Is called when trackers are removed. + */ + @ShardeventListener + private fun onTrackerChangeRemove(event: TrackerChangeShardevent.Remove) { + this.bossBar.removePlayers(event.players as List) + } +} diff --git a/src/main/kotlin/org/lanternpowered/server/entity/shard/NetworkShard.kt b/src/main/kotlin/org/lanternpowered/server/entity/shard/NetworkShard.kt new file mode 100644 index 000000000..0647230e5 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/entity/shard/NetworkShard.kt @@ -0,0 +1,166 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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.entity.shard + +import ninja.leaping.configurate.objectmapping.Setting +import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable +import org.lanternpowered.api.shard.Shard +import org.lanternpowered.server.entity.LanternEntity +import org.lanternpowered.api.entity.event.animation.CollectEntityAnimation +import org.lanternpowered.api.entity.event.animation.DamageEntityAnimation +import org.lanternpowered.api.entity.event.EntityWorldShardevent +import org.lanternpowered.api.entity.event.animation.EntityLoveModeAnimation +import org.lanternpowered.server.entity.event.RequestPlayerAbilitiesRefreshShardevent +import org.lanternpowered.server.entity.event.SpectateEntityShardevent +import org.lanternpowered.api.entity.event.animation.SwingHandAnimation +import org.lanternpowered.server.network.entity.AbstractEntityProtocol +import org.lanternpowered.server.network.entity.EntityProtocolShardeventType +import org.lanternpowered.server.network.entity.EntityProtocolType +import org.lanternpowered.server.network.entity.EntityProtocolTypes +import org.lanternpowered.server.shards.OnAttach +import org.lanternpowered.server.shards.OnDetach +import org.lanternpowered.api.shard.event.Shardevent +import org.lanternpowered.api.shard.event.ShardeventListener +import org.spongepowered.api.entity.Entity +import kotlin.reflect.KClass + +/** + * A [Shard] that can be attached to a [Entity] to + * give it the possibility to be visible/rendered on the client. Without + * this component will be client never know about the [Entity]. + */ +@ConfigSerializable +class NetworkShard : Shard { + + private val entity: LanternEntity by requireHolderOfType() + + @Setting(value = "type", comment = "The entity protocol type that should be used for the entity.") + private var entityProtocolType: EntityProtocolType<*> = EntityProtocolTypes.CHICKEN // Chickens by default? Why not... + + @Setting(value = "tracking-range", comment = "The tracking range that the entity is visible to players.") + var trackingRange = 64.0 + + @Setting(value = "tracking-update-rate", comment = "The tracking update rate is the amount of ticks between each protocol update.") + var trackingUpdateRate = 4 + + private var entityProtocol: AbstractEntityProtocol<*>? = null + + constructor() + + constructor(entityProtocolType: EntityProtocolType<*>, trackingRange: Int, trackingUpdateRate: Int) { + this.entityProtocolType = entityProtocolType + this.trackingUpdateRate = trackingUpdateRate + this.trackingRange = trackingRange.toDouble() + } + + /** + * Gets the [EntityProtocolType]. + * + * @return The entity protocol type + */ + fun getEntityProtocolType(): EntityProtocolType<*> { + return this.entityProtocolType + } + + /** + * Sets the [EntityProtocolType]. Will cause + * the [LanternEntity] to respawn on the client. + * + * @param entityProtocolType The entity protocol type + */ + fun setEntityProtocolType(entityProtocolType: EntityProtocolType<*>) { + this.entityProtocolType = entityProtocolType + // The entity was already present in a world when the component got attached + if (this.entity.existsInWorld()) { + // Respawn the entity on the client, the old entry will be cleaned up + this.entityProtocol = this.entity.world.entityProtocolManager.add(this.entity, this) + } + } + + @OnAttach + private fun onAttach() { + val shardeventBus = this.entity.shardeventBus + // Delegate the events that are used in protocol + delegatedEvents.forEach { (shardeventClass, shardeventType) -> + shardeventBus.register(shardeventClass.java) { event -> + if (this.entityProtocol != null) { + this.entityProtocol!!.addEvent(event, shardeventType) + } + } + } + // The entity was already present in a world when the component got attached + if (this.entity.existsInWorld()) { + // Spawn the entity on the client + this.entityProtocol = this.entity.world.entityProtocolManager.add(this.entity, this) + } + } + + @OnDetach + private fun onDetach() { + // The entity was already present in a world when the component got detached + if (this.entity.existsInWorld()) { + // Remove the entity from the client + this.entity.world.entityProtocolManager.remove(this.entity) + // The entity is no more + this.entityProtocol = null + } + } + + @ShardeventListener + private fun onJoinWorld(event: EntityWorldShardevent.Join) { + // Spawn the entity on the client + this.entityProtocol = event.world.entityProtocolManager.add(this.entity, this) + } + + @ShardeventListener + private fun onLeaveWorld(event: EntityWorldShardevent.Leave) { + // Remove the entity on the client + event.world.entityProtocolManager.remove(this.entity) + // The entity is no more + this.entityProtocol = null + } + + companion object { + + /** + * A list with all the [Shardevent] that should be delegated to the [AbstractEntityProtocol]. + */ + private val delegatedEvents: List, EntityProtocolShardeventType>> = listOf( + // Used to play damage animations + Pair(DamageEntityAnimation::class, EntityProtocolShardeventType.ALIVE), + // Used to request player ability updates + Pair(RequestPlayerAbilitiesRefreshShardevent::class, EntityProtocolShardeventType.DEATH_OR_ALIVE), + // Used to make the entity being collected (picked up) + Pair(CollectEntityAnimation::class, EntityProtocolShardeventType.DEATH_OR_ALIVE), + // Used when a player spectates a specific entity or stops spectating + Pair(SpectateEntityShardevent::class, EntityProtocolShardeventType.ALIVE), + // Used to play arm swing animations + Pair(SwingHandAnimation::class, EntityProtocolShardeventType.ALIVE), + // Used to play love particles + Pair(EntityLoveModeAnimation::class, EntityProtocolShardeventType.ALIVE) + ) + } +} diff --git a/src/main/kotlin/org/lanternpowered/server/inject/LanternInjectionPoint.kt b/src/main/kotlin/org/lanternpowered/server/inject/LanternInjectionPoint.kt index 979993599..8dabbcac6 100644 --- a/src/main/kotlin/org/lanternpowered/server/inject/LanternInjectionPoint.kt +++ b/src/main/kotlin/org/lanternpowered/server/inject/LanternInjectionPoint.kt @@ -30,7 +30,6 @@ import org.lanternpowered.api.ext.* import org.lanternpowered.api.inject.InjectionPoint import org.lanternpowered.server.util.ToStringHelper import java.lang.reflect.Executable -import java.lang.reflect.Field import java.util.Arrays internal abstract class LanternInjectionPoint( diff --git a/src/main/kotlin/org/lanternpowered/server/shard/LanternShardRegistry.kt b/src/main/kotlin/org/lanternpowered/server/shard/LanternShardRegistry.kt new file mode 100644 index 000000000..b5b356535 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/shard/LanternShardRegistry.kt @@ -0,0 +1,77 @@ +package org.lanternpowered.server.shard + +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.shard.RequiredHolderOfTypeProperty +import org.lanternpowered.api.shard.Shard +import org.lanternpowered.api.shard.ShardRegistry +import org.lanternpowered.api.shard.ShardType +import java.lang.reflect.TypeVariable +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.jvm.javaField +import kotlin.reflect.jvm.javaType + +object LanternShardRegistry : ShardRegistry { + + /** + * [ShardType]s mapped by their [Shard] type. + */ + private val typeCache = ConcurrentHashMap, ShardType<*>>() + + /** + * [ShardType]s mapped by all the [Shard] subclasses, includes type classes and implementations. + */ + private val resolvedTypeCache = ConcurrentHashMap, ShardType<*>>() + + private val requirementsCache = ConcurrentHashMap, ShardRequirements>() + + private val shardTypeVariable = Shard::class.java.typeParameters[0] + + override fun > getType(shardClass: Class): ShardType { + return this.resolvedTypeCache.computeIfAbsent(shardClass) { _ -> + // Extract the shard class that represents the type from the target + val type = shardClass.typeToken.resolveType(this.shardTypeVariable) + // Must be resolved, so not a type variable + check(type.type !is TypeVariable<*>) { "The shard type of ${shardClass.name} must be resolved, and not $type." } + // Create the shard type + this.typeCache.computeIfAbsent(type.rawType.uncheckedCast>()) { LanternShardType(it.uncheckedCast()) } + }.uncheckedCast() + } + + private fun buildRequirements(shardClass: Class>): ShardRequirements { + // Extract all the holder requirements from the shard class + val requiredHolderTypes = mutableListOf>() + + var target: Class<*> = shardClass + while (target != Shard::class.java) { + try { + target.kotlin.declaredMemberProperties.forEach { property -> + val field = property.javaField + if (field != null && RequiredHolderOfTypeProperty::class.java.isAssignableFrom(field.type)) { + // Extract the holder type from the property + requiredHolderTypes += property.returnType.javaType.typeToken.rawType + } + } + } catch (ignored: UnsupportedOperationException) { + } + target = target.superclass + } + + // Validate the required holder types, to check whether there + // are multiple non interface requirements, which is impossible + // to exist. + + val nonInterfaceHolders = requiredHolderTypes.filter { !it.isInterface } + check(nonInterfaceHolders.size <= 1) { + """A impossible required holder structure is found, the following holder classes in ${target.name} are causing conflicts: + ${nonInterfaceHolders.joinToString("\n") { it.name }} + """ + } + + return ShardRequirements(shardClass, requiredHolderTypes) + } + + override fun , I : T> registerDefault(shardType: ShardType, defaultImpl: Class) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} diff --git a/src/main/kotlin/org/lanternpowered/server/shard/LanternShardType.kt b/src/main/kotlin/org/lanternpowered/server/shard/LanternShardType.kt new file mode 100644 index 000000000..712b35a9d --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/shard/LanternShardType.kt @@ -0,0 +1,6 @@ +package org.lanternpowered.server.shard + +import org.lanternpowered.api.shard.Shard +import org.lanternpowered.api.shard.ShardType + +class LanternShardType>(override val type: Class) : ShardType diff --git a/src/main/kotlin/org/lanternpowered/server/shard/ShardRequirements.kt b/src/main/kotlin/org/lanternpowered/server/shard/ShardRequirements.kt new file mode 100644 index 000000000..3df7c00aa --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/server/shard/ShardRequirements.kt @@ -0,0 +1,41 @@ +package org.lanternpowered.server.shard + +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.shard.Shard +import org.lanternpowered.api.shard.ShardHolder +import org.lanternpowered.api.shard.ShardType + +/** + * Represents the requirements of a specific + * [Shard] before it can be attached. + */ +class ShardRequirements( + private val target: Class>, + requiredHolderTypes: Collection>) { + + /** + * The shard type of this requirements, if known. + */ + var shardType: ShardType<*>? = null + internal set + + /** + * A list with all the holder classes that + * should be implemented before the holder + * is considered applicable. + */ + private val requiredHolderTypes: Collection> = requiredHolderTypes.toImmutableList() + + /** + * Tests whether the shard can be attached to the given [ShardHolder] + * based on the [ShardHolder] requirements. + */ + fun testHolder(holder: ShardHolder): Boolean { + this.requiredHolderTypes.forEach { + if (!it.isInstance(holder)) { + return false + } + } + return true + } +} diff --git a/src/test/java/org/lanternpowered/server/ShardeventBusTest.java b/src/test/java/org/lanternpowered/server/ShardeventBusTest.java new file mode 100644 index 000000000..83bbfb68a --- /dev/null +++ b/src/test/java/org/lanternpowered/server/ShardeventBusTest.java @@ -0,0 +1,81 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * 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; + +import org.junit.Test; +import org.lanternpowered.api.shard.event.Shardevent; +import org.lanternpowered.api.shard.event.ShardeventListener; +import org.lanternpowered.server.shards.internal.event.LanternShardeventBus; +import org.lanternpowered.server.util.FieldAccessFactory; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class ShardeventBusTest { + + @Test + public void test() throws NoSuchFieldException { + final LanternShardeventBus bus = new LanternShardeventBus(); + bus.register(this); + bus.post(new TestShardevent()); + + final TestShardevent event = new TestShardevent(); + System.out.println("myValue: " + event.myValue); + + final Field field = TestShardevent.class.getDeclaredField("myValue"); + field.setAccessible(true); + try { + final Field mField = Field.class.getDeclaredField("modifiers"); + mField.setAccessible(true); + mField.set(field, field.getModifiers() & ~Modifier.FINAL); + } catch (Exception e) { + throw new IllegalStateException(e); + } + + final Function getterFunction = FieldAccessFactory.createGetter(field); + final BiConsumer setterFunction = FieldAccessFactory.createSetter(field); + + System.out.println("myValue: " + getterFunction.apply(event)); + setterFunction.accept(event, 5); + System.out.println("myValue: " + getterFunction.apply(event)); + setterFunction.accept(event, 10); + System.out.println("myValue: " + getterFunction.apply(event)); + setterFunction.accept(event, 50); + System.out.println("myValue: " + getterFunction.apply(event)); + } + + @ShardeventListener + private void onTest(TestShardevent event) { + System.out.println("DEBUG A!"); + } + + public final class TestShardevent implements Shardevent { + + private int myValue = 1; + } +} diff --git a/src/main/java/org/lanternpowered/server/entity/weather/AbstractWeatherEffect.java b/src/test/java/org/lanternpowered/server/util/ObjectSupplierGeneratorTest.java similarity index 75% rename from src/main/java/org/lanternpowered/server/entity/weather/AbstractWeatherEffect.java rename to src/test/java/org/lanternpowered/server/util/ObjectSupplierGeneratorTest.java index 3973575ae..2b9748c98 100644 --- a/src/main/java/org/lanternpowered/server/entity/weather/AbstractWeatherEffect.java +++ b/src/test/java/org/lanternpowered/server/util/ObjectSupplierGeneratorTest.java @@ -23,20 +23,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.weather; +package org.lanternpowered.server.util; -import org.lanternpowered.server.data.key.LanternKeys; -import org.spongepowered.api.entity.weather.WeatherEffect; +import org.junit.Test; +import org.lanternpowered.server.util.supplier.ObjectSupplierGenerator; -public interface AbstractWeatherEffect extends WeatherEffect { +import java.util.function.Supplier; - @Override - default boolean isEffect() { - return get(LanternKeys.IS_EFFECT).get(); - } +public class ObjectSupplierGeneratorTest { - @Override - default void setEffect(boolean effect) { - offer(LanternKeys.IS_EFFECT, effect); + @Test + public void test() { + final Supplier supplier = ObjectSupplierGenerator.getSupplier(TestObject.class); + System.out.println(supplier.get()); + System.out.println(supplier.get()); } } diff --git a/src/main/java/org/lanternpowered/server/entity/event/DamagedEntityEvent.java b/src/test/java/org/lanternpowered/server/util/TestObject.java similarity index 78% rename from src/main/java/org/lanternpowered/server/entity/event/DamagedEntityEvent.java rename to src/test/java/org/lanternpowered/server/util/TestObject.java index eb865bb86..35119ad2c 100644 --- a/src/main/java/org/lanternpowered/server/entity/event/DamagedEntityEvent.java +++ b/src/test/java/org/lanternpowered/server/util/TestObject.java @@ -23,21 +23,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.event; +package org.lanternpowered.server.util; -public final class DamagedEntityEvent implements EntityEvent { +public final class TestObject { - public static DamagedEntityEvent of() { - return INSTANCE; - } + private static int counter; - private static final DamagedEntityEvent INSTANCE = new DamagedEntityEvent(); + private final int index = counter++; - private DamagedEntityEvent() { + private TestObject() { } @Override - public EntityEventType type() { - return EntityEventType.ALIVE; + public String toString() { + return getClass().getName() + '[' + this.index + ']'; } } diff --git a/src/test/kotlin/org/lanternpowered/server/inject/LazyInjectionTest.kt b/src/test/kotlin/org/lanternpowered/server/inject/LazyInjectionTest.kt index 741463d50..c222f3841 100644 --- a/src/test/kotlin/org/lanternpowered/server/inject/LazyInjectionTest.kt +++ b/src/test/kotlin/org/lanternpowered/server/inject/LazyInjectionTest.kt @@ -28,7 +28,6 @@ package org.lanternpowered.server.inject import com.google.inject.AbstractModule import com.google.inject.Guice import com.google.inject.Provider -import com.google.inject.name.Named import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test diff --git a/src/main/java/org/lanternpowered/server/entity/event/SpectateEntityEvent.java b/src/test/kotlin/org/lanternpowered/server/shard/YouWishItWasAMerchantShard.kt similarity index 66% rename from src/main/java/org/lanternpowered/server/entity/event/SpectateEntityEvent.java rename to src/test/kotlin/org/lanternpowered/server/shard/YouWishItWasAMerchantShard.kt index 086c56eb0..727d6d491 100644 --- a/src/main/java/org/lanternpowered/server/entity/event/SpectateEntityEvent.java +++ b/src/test/kotlin/org/lanternpowered/server/shard/YouWishItWasAMerchantShard.kt @@ -23,28 +23,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.lanternpowered.server.entity.event; - -import org.spongepowered.api.entity.Entity; - -import java.util.Optional; - -import javax.annotation.Nullable; - -public final class SpectateEntityEvent implements EntityEvent { - - @Nullable private final Entity spectatedEntity; - - public SpectateEntityEvent(@Nullable Entity spectatedEntity) { - this.spectatedEntity = spectatedEntity; - } - - public Optional getSpectatedEntity() { - return Optional.ofNullable(this.spectatedEntity); - } - - @Override - public EntityEventType type() { - return EntityEventType.ALIVE; - } +package org.lanternpowered.server.shard + +import org.lanternpowered.api.entity.shard.ExplosiveShard +import org.lanternpowered.api.entity.shard.MerchantShard +import org.spongepowered.api.entity.living.Humanoid +import org.spongepowered.api.entity.living.player.Player +import org.spongepowered.api.text.Text + +class YouWishItWasAMerchantShard() : MerchantShard() { + + private val explosiveShard by optionalShard() + + override var customer: Humanoid? + set(value) { + this.explosiveShard?.detonate() + if (value is Player) { + value.sendMessage(Text.of("Boom!")) + } + } + get() = null }