package com.hypixel.hytale.server.spawning.beacons; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.group.EntityGroup; import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; 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.HiddenFromAdventurePlayers; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; 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.npc.components.SpawnBeaconReference; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.spawning.SpawningContext; import com.hypixel.hytale.server.spawning.SpawningPlugin; import com.hypixel.hytale.server.spawning.assets.spawns.config.BeaconNPCSpawn; import com.hypixel.hytale.server.spawning.controllers.BeaconSpawnController; import com.hypixel.hytale.server.spawning.suppression.component.SpawnSuppressionComponent; import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector; import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper; import it.unimi.dsi.fastutil.Pair; import java.time.Duration; import java.time.Instant; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class LegacySpawnBeaconEntity extends Entity { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( LegacySpawnBeaconEntity.class, LegacySpawnBeaconEntity::new, Entity.CODEC ) .append(new KeyedCodec<>("SpawnConfiguration", Codec.STRING), (spawnBeacon, s) -> spawnBeacon.spawnConfigId = s, spawnBeacon -> spawnBeacon.spawnConfigId) .add() .append( new KeyedCodec<>("NextSpawnAfter", Codec.INSTANT), (spawnBeacon, instant) -> spawnBeacon.nextSpawnAfter = instant, spawnBeacon -> spawnBeacon.nextSpawnAfter ) .add() .append( new KeyedCodec<>("NextSpawnAfterRealtime", Codec.BOOLEAN), (spawnBeacon, s) -> spawnBeacon.nextSpawnAfterRealtime = s, spawnBeacon -> spawnBeacon.nextSpawnAfterRealtime ) .add() .append( new KeyedCodec<>("DespawnSelfAfter", Codec.INSTANT), (spawnBeacon, instant) -> spawnBeacon.despawnSelfAfter = instant, spawnBeacon -> spawnBeacon.despawnSelfAfter ) .add() .append( new KeyedCodec<>("LastPlayerCount", Codec.INTEGER), (spawnBeacon, i) -> spawnBeacon.lastPlayerCount = i, spawnBeacon -> spawnBeacon.lastPlayerCount ) .add() .append(new KeyedCodec<>("ObjectiveUUID", Codec.UUID_BINARY), (entity, uuid) -> entity.objectiveUUID = uuid, entity -> entity.objectiveUUID) .add() .build(); private BeaconSpawnController spawnController; @Nullable protected UUID objectiveUUID; private BeaconSpawnWrapper spawnWrapper; private String spawnConfigId; private Instant nextSpawnAfter; private boolean nextSpawnAfterRealtime; @Nullable private Instant despawnSelfAfter; private int spawnAttempts; private int lastPlayerCount; @Nullable public static ComponentType getComponentType() { return EntityModule.get().getComponentType(LegacySpawnBeaconEntity.class); } public LegacySpawnBeaconEntity(@Nullable World world) { super(world); } private LegacySpawnBeaconEntity() { } public String getSpawnConfigId() { return this.spawnConfigId; } public BeaconSpawnController getSpawnController() { return this.spawnController; } public void setSpawnController(@Nonnull BeaconSpawnController spawnController) { this.spawnController = spawnController; } public Instant getNextSpawnAfter() { return this.nextSpawnAfter; } public boolean isNextSpawnAfterRealtime() { return this.nextSpawnAfterRealtime; } @Nullable public Instant getDespawnSelfAfter() { return this.despawnSelfAfter; } public void setSpawnAttempts(int spawnAttempts) { this.spawnAttempts = spawnAttempts; } public BeaconSpawnWrapper getSpawnWrapper() { return this.spawnWrapper; } public void setSpawnWrapper(BeaconSpawnWrapper spawnWrapper) { this.spawnWrapper = spawnWrapper; } public int getSpawnAttempts() { return this.spawnAttempts; } public int getLastPlayerCount() { return this.lastPlayerCount; } public void setLastPlayerCount(int lastPlayerCount) { this.lastPlayerCount = lastPlayerCount; } private void setSpawnConfiguration(BeaconSpawnWrapper spawn) { this.spawnWrapper = spawn; } private void setSpawnConfigId(String spawnConfigId) { this.spawnConfigId = spawnConfigId; } @Nullable public UUID getObjectiveUUID() { return this.objectiveUUID; } public void setObjectiveUUID(@Nullable UUID objectiveUUID) { this.objectiveUUID = objectiveUUID; } @Override public boolean isHiddenFromLivingEntity( @Nonnull Ref ref, @Nonnull Ref targetRef, @Nonnull ComponentAccessor componentAccessor ) { Player targetPlayerComponent = componentAccessor.getComponent(targetRef, Player.getComponentType()); return targetPlayerComponent == null || targetPlayerComponent.getGameMode() != GameMode.Creative; } @Override public boolean isCollidable() { return false; } @Override public void moveTo(@Nonnull Ref ref, double locX, double locY, double locZ, @Nonnull ComponentAccessor componentAccessor) { super.moveTo(ref, locX, locY, locZ, componentAccessor); FloodFillPositionSelector floodFillPositionSelectorComponent = componentAccessor.getComponent(ref, FloodFillPositionSelector.getComponentType()); assert floodFillPositionSelectorComponent != null; floodFillPositionSelectorComponent.setCalculatePositionsAfter(SpawnBeaconSystems.POSITION_CALCULATION_DELAY_RANGE[1]); floodFillPositionSelectorComponent.forceRebuildCache(); this.spawnController.clearUnspawnableNPCs(); } public void notifyFailedSpawn() { this.spawnAttempts++; } public void notifySpawn(@Nonnull Player target, @Nonnull Ref spawnedEntity, @Nonnull Store store) { this.processSpawn(spawnedEntity, target, store); FlockMembership flockMembershipComponent = store.getComponent(spawnedEntity, FlockMembership.getComponentType()); Ref flockReference = flockMembershipComponent != null ? flockMembershipComponent.getFlockRef() : null; if (flockReference != null && flockReference.isValid()) { EntityGroup entityGroup = store.getComponent(flockReference, EntityGroup.getComponentType()); entityGroup.forEachMemberExcludingSelf((member, sender, beacon, player) -> { if (store.getArchetype(member).contains(NPCEntity.getComponentType())) { beacon.processSpawn(member, player, store); } }, spawnedEntity, this, target); } this.spawnController.onJobFinished(store); } public static void prepareNextSpawnTimer(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { LegacySpawnBeaconEntity legacySpawnBeaconComponent = componentAccessor.getComponent(ref, getComponentType()); assert legacySpawnBeaconComponent != null; BeaconNPCSpawn beaconSpawn = legacySpawnBeaconComponent.spawnWrapper.getSpawn(); boolean realtime = beaconSpawn.isRespawnRealtime(); if (realtime) { Duration[] spawnAfterRange = beaconSpawn.getSpawnAfterRealTimeRange(); Duration nextValue = RandomExtra.randomDuration(spawnAfterRange[0], spawnAfterRange[1]); legacySpawnBeaconComponent.nextSpawnAfter = Instant.now().plus(nextValue); legacySpawnBeaconComponent.nextSpawnAfterRealtime = true; } else { WorldTimeResource worldTimeResource = componentAccessor.getResource(WorldTimeResource.getResourceType()); Duration[] spawnAfterRange = beaconSpawn.getSpawnAfterGameTimeRange(); Duration nextValue = RandomExtra.randomDuration(spawnAfterRange[0], spawnAfterRange[1]); legacySpawnBeaconComponent.nextSpawnAfter = worldTimeResource.getGameTime().plus(nextValue); legacySpawnBeaconComponent.nextSpawnAfterRealtime = false; } TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; transformComponent.markChunkDirty(componentAccessor); } public static void clearDespawnTimer(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { LegacySpawnBeaconEntity legacySpawnBeaconComponent = componentAccessor.getComponent(ref, getComponentType()); assert legacySpawnBeaconComponent != null; if (legacySpawnBeaconComponent.despawnSelfAfter != null) { legacySpawnBeaconComponent.despawnSelfAfter = null; TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; transformComponent.markChunkDirty(componentAccessor); } } public static void setToDespawnAfter(@Nonnull Ref ref, @Nullable Duration duration, @Nonnull ComponentAccessor componentAccessor) { LegacySpawnBeaconEntity legacySpawnBeaconComponent = componentAccessor.getComponent(ref, getComponentType()); assert legacySpawnBeaconComponent != null; if (duration != null && legacySpawnBeaconComponent.despawnSelfAfter == null) { WorldTimeResource worldTimeResource = componentAccessor.getResource(WorldTimeResource.getResourceType()); legacySpawnBeaconComponent.despawnSelfAfter = worldTimeResource.getGameTime().plus(duration); TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; transformComponent.markChunkDirty(componentAccessor); } } public void markNPCUnspawnable(int roleIndex) { this.spawnController.markNPCUnspawnable(roleIndex); } public boolean prepareSpawnContext( @Nonnull Vector3d playerPosition, int spawnsThisRound, int roleIndex, @Nonnull SpawningContext spawningContext, @Nonnull CommandBuffer commandBuffer ) { FloodFillPositionSelector floodFillPositionSelectorComponent = commandBuffer.getComponent(this.reference, FloodFillPositionSelector.getComponentType()); assert floodFillPositionSelectorComponent != null; if (!floodFillPositionSelectorComponent.hasPositionsForRole(roleIndex)) { this.markNPCUnspawnable(roleIndex); return false; } else { return floodFillPositionSelectorComponent.prepareSpawnContext(playerPosition, spawnsThisRound, roleIndex, spawningContext, this.spawnWrapper); } } private void processSpawn(@Nonnull Ref ref, @Nonnull Player target, @Nonnull Store store) { SpawnBeaconReference spawnBeaconReference = store.ensureAndGetComponent(ref, SpawnBeaconReference.getComponentType()); spawnBeaconReference.getReference().setEntity(this.reference, store); spawnBeaconReference.refreshTimeoutCounter(); this.spawnController.notifySpawnedEntityExists(ref, store); NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType()); assert npcComponent != null; Role role = npcComponent.getRole(); BeaconNPCSpawn spawn = this.spawnWrapper.getSpawn(); role.getMarkedEntitySupport().setMarkedEntity(spawn.getTargetSlot(), target.getReference()); String spawnState = spawn.getNpcSpawnState(); if (spawnState != null) { role.getStateSupport().setState(ref, spawnState, spawn.getNpcSpawnSubState(), store); } } @Nonnull public static Pair, LegacySpawnBeaconEntity> create( @Nonnull BeaconSpawnWrapper spawnWrapper, @Nonnull Vector3d position, @Nonnull Vector3f rotation, @Nonnull ComponentAccessor componentAccessor ) { Holder holder = createHolder(spawnWrapper, position, rotation); Ref ref = componentAccessor.addEntity(holder, AddReason.SPAWN); LegacySpawnBeaconEntity legacySpawnBeaconComponent = holder.getComponent(getComponentType()); return Pair.of(ref, legacySpawnBeaconComponent); } public static Holder createHolder(@Nonnull BeaconSpawnWrapper spawnWrapper, @Nonnull Vector3d position, @Nonnull Vector3f rotation) { LegacySpawnBeaconEntity entity = new LegacySpawnBeaconEntity(); entity.setSpawnConfiguration(spawnWrapper); BeaconNPCSpawn spawn = spawnWrapper.getSpawn(); String spawnConfigId = spawn.getId(); entity.setSpawnConfigId(spawnConfigId); String modelName = spawn.getModel(); ModelAsset modelAsset = null; if (modelName != null && !modelName.isEmpty()) { modelAsset = ModelAsset.getAssetMap().getAsset(modelName); } Model model; if (modelAsset == null) { model = SpawningPlugin.get().getSpawnMarkerModel(); } else { model = Model.createUnitScaleModel(modelAsset); } Holder holder = EntityStore.REGISTRY.newHolder(); holder.addComponent(getComponentType(), entity); holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(position, rotation)); holder.ensureComponent(UUIDComponent.getComponentType()); holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model)); holder.addComponent(PersistentModel.getComponentType(), new PersistentModel(model.toReference())); DisplayNameComponent displayNameComponent = new DisplayNameComponent(Message.raw(spawnConfigId)); holder.addComponent(DisplayNameComponent.getComponentType(), displayNameComponent); holder.addComponent(Nameplate.getComponentType(), new Nameplate(spawnConfigId)); double[] initialSpawnDelay = spawn.getInitialSpawnDelay(); if (initialSpawnDelay != null) { InitialBeaconDelay delay = holder.ensureAndGetComponent(InitialBeaconDelay.getComponentType()); delay.setupInitialSpawnDelay(initialSpawnDelay); } String suppression = spawn.getSpawnSuppression(); if (suppression != null && !suppression.isEmpty()) { holder.addComponent(SpawnSuppressionComponent.getComponentType(), new SpawnSuppressionComponent(suppression)); } holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType()); return holder; } @Nonnull @Override public String toString() { return "LegacySpawnBeaconEntity{nextSpawnAfter=" + this.nextSpawnAfter + ", nextSpawnAfterRealtime=" + this.nextSpawnAfterRealtime + ", despawnSelfAfter=" + this.despawnSelfAfter + ", spawnAttempts=" + this.spawnAttempts + ", lastPlayerCount=" + this.lastPlayerCount + "}"; } }