package com.hypixel.hytale.server.npc.systems; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Archetype; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.SystemGroup; import com.hypixel.hytale.component.dependency.Dependency; import com.hypixel.hytale.component.dependency.Order; import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.EntityEventSystem; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.WorldEventSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.entity.Frozen; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent; import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab; import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.MovementAudioComponent; import com.hypixel.hytale.server.core.modules.entity.component.NewSpawnComponent; import com.hypixel.hytale.server.core.modules.entity.component.PositionDataComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId; import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems; import com.hypixel.hytale.server.core.modules.entity.damage.DeferredCorpseRemoval; import com.hypixel.hytale.server.core.modules.entity.damage.event.KillFeedEvent; import com.hypixel.hytale.server.core.modules.entity.system.ModelSystems; import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport; import com.hypixel.hytale.server.core.modules.entity.teleport.TeleportSystems; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent; import com.hypixel.hytale.server.core.prefab.event.PrefabPlaceEntityEvent; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.flock.FlockMembership; import com.hypixel.hytale.server.flock.FlockPlugin; import com.hypixel.hytale.server.npc.NPCPlugin; import com.hypixel.hytale.server.npc.blackboard.view.blocktype.BlockTypeView; import com.hypixel.hytale.server.npc.config.balancing.BalanceAsset; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import java.util.Set; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class NPCSystems { public static class AddSpawnEntityEffectSystem extends RefSystem { @Nonnull private final ComponentType npcComponentType; public AddSpawnEntityEffectSystem(@Nonnull ComponentType npcComponentType) { this.npcComponentType = npcComponentType; } @Override public void onEntityAdded( @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType); assert npcComponent != null; EffectControllerComponent effectController = store.getComponent(ref, EffectControllerComponent.getComponentType()); assert effectController != null; Role role = npcComponent.getRole(); if (role == null) { ((HytaleLogger.Api)((HytaleLogger.Api)NPCPlugin.get().getLogger().atSevere()) .withCause(new IllegalStateException("NPC has no role or role index in onLoad!"))) .log(); commandBuffer.removeEntity(ref, RemoveReason.REMOVE); } else { String balanceAssetId = role.getBalanceAsset(); if (balanceAssetId != null) { BalanceAsset balanceAsset = BalanceAsset.getAssetMap().getAsset(balanceAssetId); String entityEffectId = balanceAsset.getEntityEffect(); if (entityEffectId != null) { EntityEffect entityEffect = EntityEffect.getAssetMap().getAsset(entityEffectId); effectController.addEffect(ref, entityEffect, commandBuffer); } } } } @Override public void onEntityRemove( @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { } @Nonnull @Override public Query getQuery() { return Query.and(this.npcComponentType, EffectControllerComponent.getComponentType()); } } public static class AddedFromExternalSystem extends RefSystem { @Nonnull private final ComponentType npcComponentType; @Nonnull private final ComponentType transformComponentType; @Nonnull private final Set> dependencies; @Nonnull private final Query query; public AddedFromExternalSystem(@Nonnull ComponentType npcComponentType) { this.npcComponentType = npcComponentType; this.transformComponentType = TransformComponent.getComponentType(); this.dependencies = Set.of(new SystemDependency<>(Order.AFTER, NPCSystems.AddedSystem.class)); this.query = Query.and(npcComponentType, Query.or(FromWorldGen.getComponentType(), FromPrefab.getComponentType())); } @Override public void onEntityAdded( @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType()); NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType); assert npcComponent != null; Archetype archetype = store.getArchetype(ref); boolean fromWorldGen = archetype.contains(FromWorldGen.getComponentType()); TransformComponent transformComponent = store.getComponent(ref, this.transformComponentType); assert transformComponent != null; npcComponent.getLeashPoint().assign(transformComponent.getPosition()); Vector3f bodyRotation = transformComponent.getRotation(); npcComponent.setLeashHeading(bodyRotation.getYaw()); npcComponent.setLeashPitch(bodyRotation.getPitch()); npcComponent.setSpawnInstant(worldTimeResource.getGameTime()); npcComponent.getRole().onLoadFromWorldGenOrPrefab(ref, npcComponent, commandBuffer); if (fromWorldGen) { commandBuffer.tryRemoveComponent(ref, Frozen.getComponentType()); } } @Override public void onEntityRemove( @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { } @Nonnull @Override public Query getQuery() { return this.query; } @Nonnull @Override public Set> getDependencies() { return this.dependencies; } @Nullable @Override public SystemGroup getGroup() { return EntityModule.get().getPreClearMarkersGroup(); } } public static class AddedFromWorldGenSystem extends HolderSystem { @Nonnull private final ComponentType npcComponentType = NPCEntity.getComponentType(); @Nonnull private final ComponentType worldGenIdComponentType = WorldGenId.getComponentType(); @Nonnull private final ComponentType fromWorldGenComponentType = FromWorldGen.getComponentType(); @Nonnull private final Query query = Query.and(this.npcComponentType, this.fromWorldGenComponentType); @Nonnull @Override public Query getQuery() { return this.query; } @Nullable @Override public SystemGroup getGroup() { return EntityModule.get().getPreClearMarkersGroup(); } @Override public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { FromWorldGen fromWorldGenComponent = holder.getComponent(this.fromWorldGenComponentType); assert fromWorldGenComponent != null; holder.putComponent(this.worldGenIdComponentType, new WorldGenId(fromWorldGenComponent.getWorldGenId())); } @Override public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { } } public static class AddedSystem extends RefSystem { @Nonnull private final ComponentType npcComponentType; public AddedSystem(@Nonnull ComponentType npcComponentType) { this.npcComponentType = npcComponentType; } @Override public void onEntityAdded( @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType); assert npcComponent != null; Role role = npcComponent.getRole(); if (role == null) { ((HytaleLogger.Api)((HytaleLogger.Api)NPCPlugin.get().getLogger().atSevere()) .withCause(new IllegalStateException("NPC has no role or role index in onLoad!"))) .log(); commandBuffer.removeEntity(ref, RemoveReason.REMOVE); } else { npcComponent.initBlockChangeBlackboardView(ref, commandBuffer); role.loaded(); commandBuffer.ensureComponent(ref, PrefabCopyableComponent.getComponentType()); commandBuffer.ensureComponent(ref, PositionDataComponent.getComponentType()); commandBuffer.ensureComponent(ref, MovementAudioComponent.getComponentType()); if (reason == AddReason.SPAWN) { NewSpawnComponent newSpawnComponent = new NewSpawnComponent(role.getSpawnLockTime()); commandBuffer.addComponent(ref, NewSpawnComponent.getComponentType(), newSpawnComponent); } } } @Override public void onEntityRemove( @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType); assert npcComponent != null; BlockTypeView blockTypeView = npcComponent.removeBlockTypeBlackboardView(); if (blockTypeView != null) { blockTypeView.removeSearchedBlockSets(ref, npcComponent, npcComponent.getBlackboardBlockTypeSets()); } switch (reason) { case REMOVE: npcComponent.getRole().removed(); break; case UNLOAD: npcComponent.getRole().unloaded(); } } @Nonnull @Override public Query getQuery() { return this.npcComponentType; } } public static class KillFeedDecedentEventSystem extends EntityEventSystem { @Nonnull private final ComponentType npcComponentType = NPCEntity.getComponentType(); public KillFeedDecedentEventSystem() { super(KillFeedEvent.DecedentMessage.class); } public void handle( int index, @NonNullDecl ArchetypeChunk archetypeChunk, @NonNullDecl Store store, @NonNullDecl CommandBuffer commandBuffer, @NonNullDecl KillFeedEvent.DecedentMessage event ) { event.setCancelled(true); } @Nonnull @Override public Query getQuery() { return this.npcComponentType; } } public static class KillFeedKillerEventSystem extends EntityEventSystem { @Nonnull private final ComponentType npcComponentType = NPCEntity.getComponentType(); @Nonnull private final ComponentType playerComponentType = Player.getComponentType(); public KillFeedKillerEventSystem() { super(KillFeedEvent.KillerMessage.class); } public void handle( int index, @NonNullDecl ArchetypeChunk archetypeChunk, @NonNullDecl Store store, @NonNullDecl CommandBuffer commandBuffer, @NonNullDecl KillFeedEvent.KillerMessage event ) { Ref targetRef = event.getTargetRef(); if (targetRef.isValid()) { Player playerComponent = store.getComponent(targetRef, this.playerComponentType); if (playerComponent == null) { event.setCancelled(true); } else { DisplayNameComponent displayNameComponent = archetypeChunk.getComponent(index, DisplayNameComponent.getComponentType()); Message displayName; if (displayNameComponent != null) { displayName = displayNameComponent.getDisplayName(); } else { NPCEntity npcComponent = archetypeChunk.getComponent(index, this.npcComponentType); assert npcComponent != null; displayName = Message.raw(npcComponent.getRoleName()); } event.setMessage(displayName); } } } @Nonnull @Override public Query getQuery() { return this.npcComponentType; } } @Deprecated(forRemoval = true) public static class LegacyWorldGenId extends HolderSystem { @Nonnull private final ComponentType npcComponentType = NPCEntity.getComponentType(); @Nonnull private final ComponentType worldGenIdComponentType = WorldGenId.getComponentType(); @Nonnull private final Query query = Query.and( this.npcComponentType, Query.not(this.worldGenIdComponentType), Query.not(FromWorldGen.getComponentType()) ); @Override public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { NPCEntity npcComponent = holder.getComponent(this.npcComponentType); assert npcComponent != null; int worldGenId = npcComponent.getLegacyWorldgenId(); if (worldGenId != 0) { holder.addComponent(this.worldGenIdComponentType, new WorldGenId(worldGenId)); } } @Override public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { } @Nonnull @Override public Query getQuery() { return this.query; } } public static class ModelChangeSystem extends RefChangeSystem { @Nonnull private final ComponentType modelComponentType = ModelComponent.getComponentType(); @Nullable private final ComponentType npcComponentType = NPCEntity.getComponentType(); @Nonnull private final Set> dependencies = Set.of(new SystemDependency<>(Order.AFTER, ModelSystems.UpdateBoundingBox.class)); @Nullable @Override public Query getQuery() { return this.npcComponentType; } @Nonnull @Override public Set> getDependencies() { return this.dependencies; } @Nonnull @Override public ComponentType componentType() { return this.modelComponentType; } public void onComponentAdded( @Nonnull Ref ref, @Nonnull ModelComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { Model model = component.getModel(); NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType); assert npcComponent != null; npcComponent.getRole().updateMotionControllers(ref, model, model.getBoundingBox(), commandBuffer); } public void onComponentSet( @Nonnull Ref ref, ModelComponent oldComponent, @Nonnull ModelComponent newComponent, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { Model model = newComponent.getModel(); NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType); assert npcComponent != null; npcComponent.getRole().updateMotionControllers(ref, model, model.getBoundingBox(), commandBuffer); } public void onComponentRemoved( @Nonnull Ref ref, @Nonnull ModelComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType); assert npcComponent != null; npcComponent.getRole().updateMotionControllers(ref, null, null, commandBuffer); } } public static class OnDeathSystem extends DeathSystems.OnDeathSystem { @Nonnull private final ComponentType npcComponentType = NPCEntity.getComponentType(); @Nonnull private final ComponentType deferredCorpseRemovalComponentType = DeferredCorpseRemoval.getComponentType(); @Nonnull @Override public Query getQuery() { return this.npcComponentType; } public void onComponentAdded( @Nonnull Ref ref, @Nonnull DeathComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { NPCEntity npcComponent = commandBuffer.getComponent(ref, this.npcComponentType); assert npcComponent != null; Role role = npcComponent.getRole(); double deathAnimationTime = role.getDeathAnimationTime(); if (deathAnimationTime > 0.0) { commandBuffer.addComponent(ref, this.deferredCorpseRemovalComponentType, new DeferredCorpseRemoval(deathAnimationTime)); } } } public static class OnTeleportSystem extends RefChangeSystem { @Nonnull private final ComponentType npcComponentType = NPCEntity.getComponentType(); @Nonnull private final ComponentType teleportComponentType = Teleport.getComponentType(); @Nonnull private final Set> dependencies = Set.of(new SystemDependency<>(Order.AFTER, TeleportSystems.MoveSystem.class)); @Nonnull @Override public Set> getDependencies() { return this.dependencies; } @Nonnull @Override public Query getQuery() { return this.npcComponentType; } @Nonnull @Override public ComponentType componentType() { return this.teleportComponentType; } public void onComponentAdded( @Nonnull Ref ref, @Nonnull Teleport component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { NPCEntity npcComponent = commandBuffer.getComponent(ref, this.npcComponentType); assert npcComponent != null; World world = store.getExternalData().getWorld(); World worldTo = component.getWorld(); Role role = npcComponent.getRole(); assert role != null; role.teleported(world, worldTo == null ? world : worldTo); } public void onComponentSet( @Nonnull Ref ref, Teleport oldComponent, @Nonnull Teleport newComponent, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { } public void onComponentRemoved( @Nonnull Ref ref, @Nonnull Teleport component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { } } public static class PrefabPlaceEntityEventSystem extends WorldEventSystem { public PrefabPlaceEntityEventSystem() { super(PrefabPlaceEntityEvent.class); } public void handle( @NonNullDecl Store store, @NonNullDecl CommandBuffer commandBuffer, @NonNullDecl PrefabPlaceEntityEvent event ) { Holder holder = event.getHolder(); FlockMembership flockMembershipComponent = holder.getComponent(FlockMembership.getComponentType()); if (flockMembershipComponent != null) { UUID flockId = FlockPlugin.get().getPrefabRemappedFlockReference(event.getPrefabId(), flockMembershipComponent.getFlockId()); flockMembershipComponent.setFlockId(flockId); NPCEntity npcComponent = holder.getComponent(NPCEntity.getComponentType()); assert npcComponent != null; npcComponent.markNeedsSave(); } } } }