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.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.component.Store;
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.RefSystem;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.reference.InvalidatablePersistentRef;
import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.flock.StoredFlock;
import com.hypixel.hytale.server.npc.components.SpawnBeaconReference;
import com.hypixel.hytale.server.npc.components.SpawnMarkerReference;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.spawning.SpawningPlugin;
import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker;
import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity;
import com.hypixel.hytale.server.spawning.controllers.BeaconSpawnController;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity;
import java.time.Duration;
import java.time.Instant;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import javax.annotation.Nonnull;
public class SpawnReferenceSystems {
public SpawnReferenceSystems() {
}
public static class MarkerAddRemoveSystem extends RefSystem<EntityStore> {
@Nonnull
private final ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType;
@Nonnull
private final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType;
@Nonnull
private final ComponentType<EntityStore, NPCEntity> npcComponentType;
@Nonnull
private final ComponentType<EntityStore, WorldGenId> worldGenIdComponentType;
@Nonnull
private final ComponentType<EntityStore, UUIDComponent> uuidComponentComponentType;
@Nonnull
private final ResourceType<EntityStore, WorldTimeResource> worldTimeResourceResourceType;
@Nonnull
private final Query<EntityStore> query;
public MarkerAddRemoveSystem(@Nonnull ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType, @Nonnull ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType) {
this.spawnReferenceComponentType = spawnReferenceComponentType;
this.spawnMarkerEntityComponentType = spawnMarkerEntityComponentType;
this.npcComponentType = NPCEntity.getComponentType();
this.worldGenIdComponentType = WorldGenId.getComponentType();
this.uuidComponentComponentType = UUIDComponent.getComponentType();
this.worldTimeResourceResourceType = WorldTimeResource.getResourceType();
this.query = Archetype.of(spawnReferenceComponentType, this.npcComponentType, this.uuidComponentComponentType);
}
@Nonnull
public Query<EntityStore> getQuery() {
return this.query;
}
public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
switch (reason) {
case LOAD:
SpawnMarkerReference spawnReferenceComponent = (SpawnMarkerReference)store.getComponent(ref, this.spawnReferenceComponentType);
assert spawnReferenceComponent != null;
Ref<EntityStore> markerReference = spawnReferenceComponent.getReference().getEntity(store);
if (markerReference == null) {
return;
} else {
SpawnMarkerEntity markerTypeComponent = (SpawnMarkerEntity)store.getComponent(markerReference, this.spawnMarkerEntityComponentType);
assert markerTypeComponent != null;
NPCEntity npcComponent = (NPCEntity)store.getComponent(ref, this.npcComponentType);
assert npcComponent != null;
spawnReferenceComponent.getReference().setEntity(markerReference, store);
spawnReferenceComponent.refreshTimeoutCounter();
markerTypeComponent.refreshTimeout();
WorldGenId worldGenIdComponent = (WorldGenId)commandBuffer.getComponent(markerReference, this.worldGenIdComponentType);
int worldGenId = worldGenIdComponent != null ? worldGenIdComponent.getWorldGenId() : 0;
commandBuffer.putComponent(markerReference, WorldGenId.getComponentType(), new WorldGenId(worldGenId));
HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.FINE);
if (context.isEnabled()) {
UUIDComponent uuidComponent = (UUIDComponent)commandBuffer.getComponent(markerReference, this.uuidComponentComponentType);
assert uuidComponent != null;
UUID uuid = uuidComponent.getUuid();
context.log("%s synced up with marker %s", npcComponent.getRoleName(), uuid);
}
}
case SPAWN:
default:
}
}
public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
switch (reason) {
case REMOVE:
SpawnMarkerReference spawnReferenceComponent = (SpawnMarkerReference)store.getComponent(ref, this.spawnReferenceComponentType);
if (spawnReferenceComponent == null) {
return;
} else {
Ref<EntityStore> spawnMarkerRef = spawnReferenceComponent.getReference().getEntity(store);
if (spawnMarkerRef == null) {
return;
} else {
SpawnMarkerEntity spawnMarkerComponent = (SpawnMarkerEntity)store.getComponent(spawnMarkerRef, this.spawnMarkerEntityComponentType);
assert spawnMarkerComponent != null;
UUIDComponent uuidComponent = (UUIDComponent)store.getComponent(ref, this.uuidComponentComponentType);
assert uuidComponent != null;
UUID uuid = uuidComponent.getUuid();
int spawnCount = spawnMarkerComponent.decrementAndGetSpawnCount();
SpawnMarker cachedMarker = spawnMarkerComponent.getCachedMarker();
if (spawnCount > 0 && cachedMarker.getDeactivationDistance() > 0.0) {
InvalidatablePersistentRef[] newReferences = new InvalidatablePersistentRef[spawnCount];
int pos = 0;
InvalidatablePersistentRef[] npcReferences = spawnMarkerComponent.getNpcReferences();
for(InvalidatablePersistentRef npcRef : npcReferences) {
if (!npcRef.getUuid().equals(uuid)) {
newReferences[pos++] = npcRef;
}
}
spawnMarkerComponent.setNpcReferences(newReferences);
}
if (spawnCount <= 0 && !cachedMarker.isRealtimeRespawn()) {
Instant instant = ((WorldTimeResource)store.getResource(this.worldTimeResourceResourceType)).getGameTime();
Duration gameTimeRespawn = spawnMarkerComponent.pollGameTimeRespawn();
if (gameTimeRespawn != null) {
instant = instant.plus(gameTimeRespawn);
}
spawnMarkerComponent.setSpawnAfter(instant);
spawnMarkerComponent.setNpcReferences((InvalidatablePersistentRef[])null);
StoredFlock storedFlock = spawnMarkerComponent.getStoredFlock();
if (storedFlock != null) {
storedFlock.clear();
}
}
}
}
case UNLOAD:
default:
}
}
}
public static class BeaconAddRemoveSystem extends RefSystem<EntityStore> {
@Nonnull
private final ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType;
@Nonnull
private final ComponentType<EntityStore, LegacySpawnBeaconEntity> legacySpawnBeaconComponent;
@Nonnull
private final ComponentType<EntityStore, NPCEntity> npcComponentType;
@Nonnull
private final Query<EntityStore> query;
public BeaconAddRemoveSystem(@Nonnull ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType, @Nonnull ComponentType<EntityStore, LegacySpawnBeaconEntity> legacySpawnBeaconComponent) {
this.spawnReferenceComponentType = spawnReferenceComponentType;
this.legacySpawnBeaconComponent = legacySpawnBeaconComponent;
this.npcComponentType = NPCEntity.getComponentType();
this.query = Archetype.of(spawnReferenceComponentType, this.npcComponentType);
}
@Nonnull
public Query<EntityStore> getQuery() {
return this.query;
}
public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
switch (reason) {
case LOAD:
SpawnBeaconReference spawnReferenceComponent = (SpawnBeaconReference)store.getComponent(ref, this.spawnReferenceComponentType);
assert spawnReferenceComponent != null;
Ref<EntityStore> markerReference = spawnReferenceComponent.getReference().getEntity(store);
if (markerReference == null) {
return;
} else {
LegacySpawnBeaconEntity legacySpawnBeaconComponent = (LegacySpawnBeaconEntity)store.getComponent(markerReference, this.legacySpawnBeaconComponent);
assert legacySpawnBeaconComponent != null;
NPCEntity npcComponent = (NPCEntity)store.getComponent(ref, this.npcComponentType);
assert npcComponent != null;
spawnReferenceComponent.getReference().setEntity(markerReference, store);
spawnReferenceComponent.refreshTimeoutCounter();
BeaconSpawnController spawnController = legacySpawnBeaconComponent.getSpawnController();
if (!spawnController.hasSlots()) {
npcComponent.setToDespawn();
return;
} else {
spawnController.notifySpawnedEntityExists(markerReference, commandBuffer);
}
}
case SPAWN:
default:
}
}
public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
switch (reason) {
case REMOVE:
SpawnBeaconReference spawnReference = (SpawnBeaconReference)store.getComponent(ref, this.spawnReferenceComponentType);
if (spawnReference == null) {
return;
} else {
Ref<EntityStore> spawnBeaconRef = spawnReference.getReference().getEntity(store);
if (spawnBeaconRef == null) {
return;
} else {
LegacySpawnBeaconEntity legacySpawnBeaconComponent = (LegacySpawnBeaconEntity)store.getComponent(spawnBeaconRef, this.legacySpawnBeaconComponent);
if (legacySpawnBeaconComponent == null) {
return;
} else {
legacySpawnBeaconComponent.getSpawnController().notifyNPCRemoval(ref, store);
}
}
}
case UNLOAD:
default:
}
}
}
public static class TickingSpawnMarkerSystem extends EntityTickingSystem<EntityStore> {
@Nonnull
private static final Set<Dependency<EntityStore>> DEPENDENCIES;
@Nonnull
private final ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType;
@Nonnull
private final ComponentType<EntityStore, SpawnMarkerEntity> markerTypeComponentType;
@Nonnull
private final ComponentType<EntityStore, NPCEntity> npcEntityComponentType;
@Nonnull
private final Query<EntityStore> query;
public TickingSpawnMarkerSystem(@Nonnull ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType, @Nonnull ComponentType<EntityStore, SpawnMarkerEntity> markerTypeComponentType) {
this.spawnReferenceComponentType = spawnReferenceComponentType;
this.markerTypeComponentType = markerTypeComponentType;
this.npcEntityComponentType = NPCEntity.getComponentType();
this.query = Archetype.of(spawnReferenceComponentType, this.npcEntityComponentType);
}
@Nonnull
public Set<Dependency<EntityStore>> getDependencies() {
return DEPENDENCIES;
}
@Nonnull
public Query<EntityStore> getQuery() {
return this.query;
}
public boolean isParallel(int archetypeChunkSize, int taskCount) {
return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
}
public void tick(float dt, int index, @Nonnull ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
NPCEntity npcComponent = (NPCEntity)archetypeChunk.getComponent(index, this.npcEntityComponentType);
assert npcComponent != null;
if (!npcComponent.isDespawning() && !npcComponent.isPlayingDespawnAnim()) {
SpawnMarkerReference spawnReferenceComponent = (SpawnMarkerReference)archetypeChunk.getComponent(index, this.spawnReferenceComponentType);
assert spawnReferenceComponent != null;
if (spawnReferenceComponent.tickMarkerLostTimeoutCounter(dt)) {
Ref<EntityStore> spawnMarkerRef = spawnReferenceComponent.getReference().getEntity(commandBuffer);
if (spawnMarkerRef != null) {
SpawnMarkerEntity spawnMarkerComponent = (SpawnMarkerEntity)commandBuffer.getComponent(spawnMarkerRef, this.markerTypeComponentType);
assert spawnMarkerComponent != null;
spawnReferenceComponent.refreshTimeoutCounter();
spawnMarkerComponent.refreshTimeout();
} else if (npcComponent.getRole().getStateSupport().isInBusyState()) {
spawnReferenceComponent.refreshTimeoutCounter();
} else {
npcComponent.setToDespawn();
HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.WARNING);
if (context.isEnabled()) {
context.log("NPCEntity despawning due to lost marker: %s", archetypeChunk.getReferenceTo(index));
}
}
}
}
}
static {
DEPENDENCIES = Set.of(new SystemDependency(Order.AFTER, NPCPreTickSystem.class), new SystemDependency(Order.BEFORE, DeathSystems.CorpseRemoval.class));
}
}
public static class TickingSpawnBeaconSystem extends EntityTickingSystem<EntityStore> {
@Nonnull
private static final Set<Dependency<EntityStore>> DEPENDENCIES;
@Nonnull
private final ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType;
@Nonnull
private final ComponentType<EntityStore, NPCEntity> npcEntityComponentType;
@Nonnull
private final Query<EntityStore> query;
public TickingSpawnBeaconSystem(@Nonnull ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType) {
this.spawnReferenceComponentType = spawnReferenceComponentType;
this.npcEntityComponentType = NPCEntity.getComponentType();
this.query = Archetype.of(spawnReferenceComponentType, this.npcEntityComponentType);
}
@Nonnull
public Set<Dependency<EntityStore>> getDependencies() {
return DEPENDENCIES;
}
@Nonnull
public Query<EntityStore> getQuery() {
return this.query;
}
public boolean isParallel(int archetypeChunkSize, int taskCount) {
return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
}
public void tick(float dt, int index, @Nonnull ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
NPCEntity npcComponent = (NPCEntity)archetypeChunk.getComponent(index, this.npcEntityComponentType);
assert npcComponent != null;
if (!npcComponent.isDespawning() && !npcComponent.isPlayingDespawnAnim()) {
SpawnBeaconReference spawnReferenceComponent = (SpawnBeaconReference)archetypeChunk.getComponent(index, this.spawnReferenceComponentType);
assert spawnReferenceComponent != null;
if (spawnReferenceComponent.tickMarkerLostTimeoutCounter(dt)) {
Ref<EntityStore> spawnBeaconRef = spawnReferenceComponent.getReference().getEntity(commandBuffer);
if (spawnBeaconRef != null) {
spawnReferenceComponent.refreshTimeoutCounter();
} else if (npcComponent.getRole().getStateSupport().isInBusyState()) {
spawnReferenceComponent.refreshTimeoutCounter();
} else {
npcComponent.setToDespawn();
HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.WARNING);
if (context.isEnabled()) {
context.log("NPCEntity despawning due to lost marker: %s", archetypeChunk.getReferenceTo(index));
}
}
}
}
}
static {
DEPENDENCIES = Set.of(new SystemDependency(Order.AFTER, NPCPreTickSystem.class), new SystemDependency(Order.BEFORE, DeathSystems.CorpseRemoval.class));
}
}
}