classpublicPriority 3
PositionCache
com.hypixel.hytale.server.npc.role.support.PositionCache
33
Methods
33
Public Methods
12
Fields
1
Constructors
Constants
ComponentType<EntityStore, BoundingBox>BOUNDING_BOX_COMPONENT_TYPE= BoundingBox.getComponentType()
StringFUNCTION_CAN_BE_ONLY_CALLED_WHILE_CONFIGURING_POSITION_CACHE= "function can be only called while configuring PositionCache"
BiPredicate<Ref<EntityStore>, ComponentAccessor<EntityStore>>IS_VALID_NPC= (ref, accessor) -> accessor.getArchetype(ref)
.contains(NPCEntity.getComponentType())
BiPredicate<Ref<EntityStore>, ComponentAccessor<EntityStore>>IS_VALID_PLAYER= (ref, componentAccessor) -> {
Player playerComponent = componentAccessor.getComponent(ref, ...
ComponentType<EntityStore, ItemComponent>ITEM_COMPONENT_TYPE= ItemComponent.getComponentType()
floatLOS_CACHE_TTL_MAX_SECONDS= 0.11F
floatLOS_CACHE_TTL_MIN_SECONDS= 0.09F
doubleMIN_LOS_BLOCKING_DISTANCE_SQUARED= 1.0E-6
ComponentType<EntityStore, ModelComponent>MODEL_COMPONENT_TYPE= ModelComponent.getComponentType()
floatPOSITION_CACHE_TTL_SECONDS= 0.2F
ComponentType<EntityStore, TransformComponent>TRANSFORM_COMPONENT_TYPE= TransformComponent.getComponentType()
Constructors
public
PositionCache(Role role)Methods
Public Methods (33)
public
void addExternalPositionCacheRegistration(Consumer<Role> registration)public
void clear(double tickTime)public
void clearRefs()public
boolean couldBreatheCached()public
void finalizeConfiguration()public
Ref<EntityStore> getClosestPlayerInRange(double minRange, double maxRange, ComponentAccessor<EntityStore> componentAccessor)@Nullable
public
List<Ref<EntityStore>> getDroppedItemList()@Nonnull
public
List<Consumer<Role>> getExternalRegistrations()@Nonnull
public
double getMaxDroppedItemDistance()public
int getMaxSpawnBeaconDistance()public
double getMaxSpawnMarkerDistance()public
EntityList getNpcs()public
EntityList getPlayers()public
Role getRole()@Nonnull
public
List<Ref<EntityStore>> getSpawnBeaconList()@Nonnull
public
List<Ref<EntityStore>> getSpawnMarkerList()@Nonnull
public
boolean hasLineOfSight(Ref<EntityStore> ref, Ref<EntityStore> targetRef, ComponentAccessor<EntityStore> componentAccessor)public
boolean isBenchmarking()public
void requireDroppedItemDistance(double value)public
void requireEntityDistanceAvoidance(double v)public
void requireEntityDistanceSorted(double v)public
void requireEntityDistanceUnsorted(double v)public
void requirePlayerDistanceAvoidance(double v)public
void requirePlayerDistanceSorted(double v)public
void requirePlayerDistanceUnsorted(double v)public
void requireSpawnBeaconDistance(int value)public
void requireSpawnMarkerDistance(double value)public
void reset(boolean isConfiguring)public
void resetPositionCacheNextUpdate()public
void setBenchmarking(boolean benchmarking)public
void setCouldBreathe(boolean couldBreathe)public
void setOpaqueBlockSet(int blockSet)public
boolean tickPositionCacheNextUpdate(float dt)Fields
Protected Fields (2)
protected
EntityList npcsprotected
EntityList playersPrivate/Package Fields (10)
private
float cacheTTLprivate
boolean couldBreatheprivate
boolean isBenchmarkingprivate
boolean isConfiguringprivate
double maxDroppedItemDistanceprivate
int maxSpawnBeaconDistanceprivate
double maxSpawnMarkerDistanceprivate
int opaqueBlockSetprivate
float positionCacheNextUpdateprivate
Role roleRelated Classes
Used By
BlockTypeAssetMapCommandBufferComponentAccessorComponentTypeRefDoubleQuadObjectConsumerQuadConsumerTriConsumerQuadPredicateBlockIteratorRandomExtraBoxChunkUtilMathUtilVector2dVector3dGameModeOpacityBlockTypePlayerBlockSetModuleCollisionMathBoundingBoxModelComponentTransformComponentItemComponentPlayerSettingsWorldWorldChunkEntityStoreNPCPluginNPCEntityRoleNPCPhysicsMath
Source Code
package com.hypixel.hytale.server.npc.role.support;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.function.consumer.DoubleQuadObjectConsumer;
import com.hypixel.hytale.function.consumer.QuadConsumer;
import com.hypixel.hytale.function.consumer.TriConsumer;
import com.hypixel.hytale.function.predicate.QuadPredicate;
import com.hypixel.hytale.math.iterator.BlockIterator;
import com.hypixel.hytale.math.random.RandomExtra;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.vector.Vector2d;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.protocol.Opacity;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.blockset.BlockSetModule;
import com.hypixel.hytale.server.core.modules.collision.CollisionMath;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSettings;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2ByteMap;
import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PositionCache {
public static final BiPredicate<Ref<EntityStore>, ComponentAccessor<EntityStore>> IS_VALID_PLAYER = (ref, componentAccessor) -> {
Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
if (playerComponent != null && !playerComponent.isWaitingForClientReady()) {
if (playerComponent.getGameMode() == GameMode.Adventure) {
return true;
} else if (playerComponent.getGameMode() != GameMode.Creative) {
return false;
} else {
PlayerSettings playerSettingsComponent = componentAccessor.getComponent(ref, PlayerSettings.getComponentType());
return playerSettingsComponent != null && playerSettingsComponent.creativeSettings().allowNPCDetection();
}
} else {
return false;
}
};
public static final BiPredicate<Ref<EntityStore>, ComponentAccessor<EntityStore>> IS_VALID_NPC = (ref, accessor) -> accessor.getArchetype(ref)
.contains(NPCEntity.getComponentType());
public static final double MIN_LOS_BLOCKING_DISTANCE_SQUARED = 1.0E-6;
public static final String FUNCTION_CAN_BE_ONLY_CALLED_WHILE_CONFIGURING_POSITION_CACHE = "function can be only called while configuring PositionCache";
private static final float LOS_CACHE_TTL_MIN_SECONDS = 0.09F;
private static final float LOS_CACHE_TTL_MAX_SECONDS = 0.11F;
private static final float POSITION_CACHE_TTL_SECONDS = 0.2F;
private static final ComponentType<EntityStore, TransformComponent> TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType();
private static final ComponentType<EntityStore, ItemComponent> ITEM_COMPONENT_TYPE = ItemComponent.getComponentType();
private static final ComponentType<EntityStore, ModelComponent> MODEL_COMPONENT_TYPE = ModelComponent.getComponentType();
protected static final ComponentType<EntityStore, BoundingBox> BOUNDING_BOX_COMPONENT_TYPE = BoundingBox.getComponentType();
private double maxDroppedItemDistance;
private double maxSpawnMarkerDistance;
private int maxSpawnBeaconDistance;
@Nonnull
private final Role role;
private int opaqueBlockSet;
protected EntityList players;
protected EntityList npcs;
protected final List<Consumer<Role>> externalRegistrations = new ObjectArrayList();
private final List<Ref<EntityStore>> droppedItems = new ObjectArrayList();
private final List<Ref<EntityStore>> spawnMarkers = new ObjectArrayList();
private final List<Ref<EntityStore>> spawnBeacons = new ObjectArrayList();
private final Object2ByteMap<Ref<EntityStore>> lineOfSightCache = new Object2ByteOpenHashMap();
private final Object2ByteMap<Ref<EntityStore>> inverseLineOfSightCache = new Object2ByteOpenHashMap();
private final Object2ByteMap<Ref<EntityStore>> friendlyFireCache = new Object2ByteOpenHashMap();
protected final PositionCache.LineOfSightBuffer lineOfSightComputeBuffer = new PositionCache.LineOfSightBuffer();
protected final PositionCache.LineOfSightEntityBuffer lineOfSightEntityComputeBuffer = new PositionCache.LineOfSightEntityBuffer();
private float cacheTTL = 0.09F;
private float positionCacheNextUpdate;
private boolean isBenchmarking;
private boolean isConfiguring;
private boolean couldBreathe = true;
public PositionCache(@Nonnull Role role) {
this.role = role;
this.players = new EntityList(null, IS_VALID_PLAYER);
this.npcs = new EntityList(null, IS_VALID_NPC);
}
public boolean isBenchmarking() {
return this.isBenchmarking;
}
public void setBenchmarking(boolean benchmarking) {
this.isBenchmarking = benchmarking;
}
public void setCouldBreathe(boolean couldBreathe) {
this.couldBreathe = couldBreathe;
}
public EntityList getPlayers() {
return this.players;
}
public EntityList getNpcs() {
return this.npcs;
}
public boolean tickPositionCacheNextUpdate(float dt) {
return (this.positionCacheNextUpdate -= dt) <= 0.0F;
}
public void resetPositionCacheNextUpdate() {
this.positionCacheNextUpdate = 0.2F;
}
public double getMaxDroppedItemDistance() {
return this.maxDroppedItemDistance;
}
public double getMaxSpawnMarkerDistance() {
return this.maxSpawnMarkerDistance;
}
public int getMaxSpawnBeaconDistance() {
return this.maxSpawnBeaconDistance;
}
public void addExternalPositionCacheRegistration(Consumer<Role> registration) {
this.externalRegistrations.add(registration);
}
@Nonnull
public List<Consumer<Role>> getExternalRegistrations() {
return this.externalRegistrations;
}
public void reset(boolean isConfiguring) {
this.players.reset();
this.npcs.reset();
this.maxDroppedItemDistance = 0.0;
this.droppedItems.clear();
this.spawnMarkers.clear();
this.spawnBeacons.clear();
this.positionCacheNextUpdate = RandomExtra.randomRange(0.0F, 0.2F);
this.clearLineOfSightCache();
this.isConfiguring = isConfiguring;
}
public void finalizeConfiguration() {
this.isConfiguring = false;
this.npcs.finalizeConfiguration();
this.players.finalizeConfiguration();
RoleStats roleStats = this.role.getRoleStats();
if (roleStats != null) {
roleStats.trackBuckets(false, this.npcs.getBucketRanges());
roleStats.trackBuckets(true, this.players.getBucketRanges());
}
}
public void clear(double tickTime) {
this.clearLineOfSightCache(tickTime);
if (this.isBenchmarking) {
NPCPlugin.get().collectSensorSupportTickDone(this.role.getRoleIndex());
}
this.isBenchmarking = false;
}
public boolean couldBreatheCached() {
return this.couldBreathe;
}
public <T, U, V> void forEachPlayer(
@Nonnull DoubleQuadObjectConsumer<Ref<EntityStore>, T, U, V> consumer, T t, U u, V v, double d, ComponentAccessor<EntityStore> componentAccessor
) {
this.players.forEachEntity(consumer, t, u, v, d, componentAccessor);
}
@Nullable
public Ref<EntityStore> getClosestPlayerInRange(double minRange, double maxRange, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
return this.getClosestPlayerInRange(minRange, maxRange, p -> true, componentAccessor);
}
@Nullable
public Ref<EntityStore> getClosestPlayerInRange(
double minRange, double maxRange, @Nonnull Predicate<Ref<EntityStore>> filter, @Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
return this.players.getClosestEntityInRange(minRange, maxRange, filter, componentAccessor);
}
@Nullable
public Ref<EntityStore> getClosestNPCInRange(
double minRange, double maxRange, @Nonnull Predicate<Ref<EntityStore>> filter, @Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
return this.npcs.getClosestEntityInRange(minRange, maxRange, filter, componentAccessor);
}
public <S, T> void processNPCsInRange(
@Nonnull Ref<EntityStore> ref,
double minRange,
double maxRange,
boolean useProjectedDistance,
Ref<EntityStore> ignoredEntityReference,
@Nonnull Role role,
@Nonnull QuadPredicate<S, Ref<EntityStore>, Role, T> filter,
S s,
T t,
@Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
this.processEntitiesInRange(ref, this.npcs, minRange, maxRange, useProjectedDistance, ignoredEntityReference, role, filter, s, t, componentAccessor);
}
public <S, T> void processPlayersInRange(
@Nonnull Ref<EntityStore> ref,
double minRange,
double maxRange,
boolean useProjectedDistance,
Ref<EntityStore> ignoredEntityReference,
@Nonnull Role role,
@Nonnull QuadPredicate<S, Ref<EntityStore>, Role, T> filter,
S s,
T t,
@Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
this.processEntitiesInRange(ref, this.players, minRange, maxRange, useProjectedDistance, ignoredEntityReference, role, filter, s, t, componentAccessor);
}
public <S, T> void processEntitiesInRange(
@Nonnull Ref<EntityStore> ref,
@Nonnull EntityList entities,
double minRange,
double maxRange,
boolean useProjectedDistance,
Ref<EntityStore> ignoredEntityReference,
@Nonnull Role role,
@Nonnull QuadPredicate<S, Ref<EntityStore>, Role, T> filter,
S s,
T t,
@Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
if (useProjectedDistance) {
entities.getClosestEntityInRangeProjected(
ref, ignoredEntityReference, role.getActiveMotionController(), minRange, maxRange, filter, role, s, t, componentAccessor
);
} else {
entities.getClosestEntityInRange(ignoredEntityReference, minRange, maxRange, filter, role, s, t, componentAccessor);
}
}
@Nullable
public <S> Ref<EntityStore> getClosestDroppedItemInRange(
@Nonnull Ref<EntityStore> ref,
double minRange,
double maxRange,
@Nonnull QuadPredicate<S, Ref<EntityStore>, Role, ComponentAccessor<EntityStore>> filter,
Role role,
S s,
@Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
int droppedItemsSize = this.droppedItems.size();
if (droppedItemsSize == 0) {
return null;
} else {
TransformComponent transformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE);
assert transformComponent != null;
Vector3d position = transformComponent.getPosition();
minRange *= minRange;
maxRange *= maxRange;
for (int index = 0; index < droppedItemsSize; index++) {
Ref<EntityStore> itemEntityRef = this.droppedItems.get(index);
if (itemEntityRef.isValid()) {
ItemComponent itemComponent = componentAccessor.getComponent(itemEntityRef, ITEM_COMPONENT_TYPE);
if (itemComponent != null) {
TransformComponent itemEntityTransformComponent = componentAccessor.getComponent(itemEntityRef, TRANSFORM_COMPONENT_TYPE);
assert itemEntityTransformComponent != null;
double squaredDistance = itemEntityTransformComponent.getPosition().distanceSquaredTo(position);
if (!(squaredDistance < minRange)) {
if (squaredDistance >= maxRange) {
break;
}
if (filter.test(s, itemEntityRef, role, componentAccessor)) {
return itemEntityRef;
}
}
}
}
}
return null;
}
}
public <S> boolean isEntityCountInRange(
double minRange,
double maxRange,
int minCount,
int maxCount,
boolean findPlayers,
Role role,
@Nonnull QuadPredicate<S, Ref<EntityStore>, Role, ComponentAccessor<EntityStore>> filter,
S s,
ComponentAccessor<EntityStore> componentAccessor
) {
int count = 0;
if (findPlayers) {
count = this.players.countEntitiesInRange(minRange, maxRange, maxCount + 1, filter, s, role, componentAccessor);
if (count > maxCount) {
return false;
}
}
count += this.npcs.countEntitiesInRange(minRange, maxRange, maxCount - count + 1, filter, s, role, componentAccessor);
return count >= minCount && count <= maxCount;
}
public <S, T> int countEntitiesInRange(
double minRange,
double maxRange,
boolean findPlayers,
@Nonnull QuadPredicate<S, Ref<EntityStore>, T, ComponentAccessor<EntityStore>> filter,
S s,
T t,
ComponentAccessor<EntityStore> componentAccessor
) {
int count = 0;
if (findPlayers) {
count = this.players.countEntitiesInRange(minRange, maxRange, 2147483647, filter, s, t, componentAccessor);
}
return count + this.npcs.countEntitiesInRange(minRange, maxRange, 2147483647, filter, s, t, componentAccessor);
}
public void requirePlayerDistanceSorted(double v) {
int value = MathUtil.ceil(v);
if (!this.isConfiguring) {
throw new IllegalStateException("function can be only called while configuring PositionCache");
} else {
this.players.requireDistanceSorted(value);
RoleStats roleStats = this.role.getRoleStats();
if (roleStats != null) {
roleStats.trackRange(true, RoleStats.RangeType.SORTED, value);
}
}
}
public void requirePlayerDistanceUnsorted(double v) {
int value = MathUtil.ceil(v);
if (!this.isConfiguring) {
throw new IllegalStateException("function can be only called while configuring PositionCache");
} else {
this.players.requireDistanceUnsorted(value);
RoleStats roleStats = this.role.getRoleStats();
if (roleStats != null) {
roleStats.trackRange(true, RoleStats.RangeType.UNSORTED, value);
}
}
}
public void requirePlayerDistanceAvoidance(double v) {
int value = MathUtil.ceil(v);
if (!this.isConfiguring) {
throw new IllegalStateException("function can be only called while configuring PositionCache");
} else {
this.players.requireDistanceAvoidance(value);
RoleStats roleStats = this.role.getRoleStats();
if (roleStats != null) {
roleStats.trackRange(true, RoleStats.RangeType.AVOIDANCE, value);
}
}
}
public void requireEntityDistanceSorted(double v) {
int value = MathUtil.ceil(v);
if (!this.isConfiguring) {
throw new IllegalStateException("function can be only called while configuring PositionCache");
} else {
this.npcs.requireDistanceSorted(value);
RoleStats roleStats = this.role.getRoleStats();
if (roleStats != null) {
roleStats.trackRange(false, RoleStats.RangeType.SORTED, value);
}
}
}
public void requireEntityDistanceUnsorted(double v) {
int value = MathUtil.ceil(v);
if (!this.isConfiguring) {
throw new IllegalStateException("function can be only called while configuring PositionCache");
} else {
this.npcs.requireDistanceUnsorted(value);
RoleStats roleStats = this.role.getRoleStats();
if (roleStats != null) {
roleStats.trackRange(false, RoleStats.RangeType.UNSORTED, value);
}
}
}
public void requireEntityDistanceAvoidance(double v) {
int value = MathUtil.ceil(v);
if (!this.isConfiguring) {
throw new IllegalStateException("function can be only called while configuring PositionCache");
} else {
value = this.npcs.requireDistanceAvoidance(value);
RoleStats roleStats = this.role.getRoleStats();
if (roleStats != null) {
roleStats.trackRange(false, RoleStats.RangeType.AVOIDANCE, value);
}
}
}
public void requireDroppedItemDistance(double value) {
if (this.maxDroppedItemDistance < value) {
this.maxDroppedItemDistance = value;
}
}
public void requireSpawnMarkerDistance(double value) {
if (this.maxSpawnMarkerDistance < value) {
this.maxSpawnMarkerDistance = value;
}
}
public void requireSpawnBeaconDistance(int value) {
if (this.maxSpawnBeaconDistance < value) {
this.maxSpawnBeaconDistance = value;
}
}
@Nonnull
public Role getRole() {
return this.role;
}
public <T, U, V, R> void forEachNPCUnordered(
double maxDistance,
@Nonnull QuadPredicate<Ref<EntityStore>, T, U, ComponentAccessor<EntityStore>> predicate,
@Nonnull QuadConsumer<Ref<EntityStore>, T, V, R> consumer,
T t,
U u,
V v,
R r,
ComponentAccessor<EntityStore> componentAccessor
) {
this.npcs.forEachEntityUnordered(maxDistance, predicate, consumer, t, u, v, r, componentAccessor);
}
public <T> void forEachEntityInAvoidanceRange(
@Nonnull Set<Ref<EntityStore>> ignoredEntitiesForAvoidance,
@Nonnull TriConsumer<Ref<EntityStore>, T, CommandBuffer<EntityStore>> consumer,
T t,
CommandBuffer<EntityStore> commandBuffer
) {
this.npcs.forEachEntityAvoidance(ignoredEntitiesForAvoidance, consumer, t, commandBuffer);
this.players.forEachEntityAvoidance(ignoredEntitiesForAvoidance, consumer, t, commandBuffer);
}
public <T, U> void forEachEntityInAvoidanceRange(
@Nonnull Set<Ref<EntityStore>> ignoredEntitiesForAvoidance,
@Nonnull QuadConsumer<Ref<EntityStore>, T, U, CommandBuffer<EntityStore>> consumer,
T t,
U u,
CommandBuffer<EntityStore> commandBuffer
) {
this.npcs.forEachEntityAvoidance(ignoredEntitiesForAvoidance, consumer, t, u, commandBuffer);
this.players.forEachEntityAvoidance(ignoredEntitiesForAvoidance, consumer, t, u, commandBuffer);
}
public void setOpaqueBlockSet(int blockSet) {
this.opaqueBlockSet = blockSet;
}
private static <T> boolean testLineOfSightRays(
@Nonnull Ref<EntityStore> ref,
@Nonnull Ref<EntityStore> targetRef,
@Nonnull PositionCache.RayPredicate<T> predicate,
@Nonnull T t,
@Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
TransformComponent transformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE);
assert transformComponent != null;
Vector3d position = transformComponent.getPosition();
ModelComponent modelComponent = componentAccessor.getComponent(ref, MODEL_COMPONENT_TYPE);
float eyeHeight = modelComponent != null ? modelComponent.getModel().getEyeHeight() : 0.0F;
double sx = position.getX();
double sy = position.getY() + (double)eyeHeight;
double sz = position.getZ();
TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, TRANSFORM_COMPONENT_TYPE);
assert targetTransformComponent != null;
Vector3d targetPosition = targetTransformComponent.getPosition();
double tx = targetPosition.getX();
double ty = targetPosition.getY();
double tz = targetPosition.getZ();
ModelComponent targetModelComponent = componentAccessor.getComponent(targetRef, MODEL_COMPONENT_TYPE);
if (targetModelComponent != null) {
return predicate.test(sx, sy, sz, tx, ty + (double)targetModelComponent.getModel().getEyeHeight(), tz, t, componentAccessor);
} else {
double ox = 0.0;
double oy = 0.0;
double oz = 0.0;
BoundingBox boundingBoxComponent = componentAccessor.getComponent(targetRef, BOUNDING_BOX_COMPONENT_TYPE);
if (boundingBoxComponent != null) {
Box boundingBox = boundingBoxComponent.getBoundingBox();
ox = (boundingBox.getMax().getX() + boundingBox.getMin().getX()) / 2.0;
oy = (boundingBox.getMax().getY() + boundingBox.getMin().getY()) / 2.0;
oz = (boundingBox.getMax().getZ() + boundingBox.getMin().getZ()) / 2.0;
}
return predicate.test(sx, sy, sz, tx + ox, ty + oy, tz + oz, t, componentAccessor);
}
}
private boolean hasLineOfSightInternal(
@Nonnull Ref<EntityStore> ref, @Nonnull Ref<EntityStore> targetRef, @Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
if (ref.equals(targetRef)) {
return false;
} else {
TransformComponent transformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE);
assert transformComponent != null;
TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, TRANSFORM_COMPONENT_TYPE);
assert targetTransformComponent != null;
if (transformComponent.getPosition().distanceSquaredTo(targetTransformComponent.getPosition()) <= 1.0E-12) {
return true;
} else {
World world = componentAccessor.getExternalData().getWorld();
Objects.requireNonNull(world, "World can't be null in isLOS");
Int2ObjectMap<IntSet> blockSets = BlockSetModule.getInstance().getBlockSets();
IntSet opaqueSet = this.opaqueBlockSet >= 0 && blockSets != null ? (IntSet)blockSets.get(this.opaqueBlockSet) : null;
boolean var9;
try {
this.lineOfSightComputeBuffer.result = true;
this.lineOfSightComputeBuffer.assetMap = BlockType.getAssetMap();
this.lineOfSightComputeBuffer.opaqueSet = opaqueSet;
this.lineOfSightComputeBuffer.world = world;
var9 = testLineOfSightRays(
ref,
targetRef,
(sx, sy, sz, tx, ty, tz, buffer, accessor) -> {
buffer.chunk = buffer.world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(sx, sz));
if (buffer.chunk == null) {
return false;
} else {
BlockIterator.iterateFromTo(
sx,
sy,
sz,
tx,
ty,
tz,
(x, y, z, px, py, pz, qx, qy, qz, iBuffer) -> {
if (!ChunkUtil.isInsideChunk(iBuffer.chunk.getX(), iBuffer.chunk.getZ(), x, z)) {
iBuffer.chunk = iBuffer.world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(x, z));
if (iBuffer.chunk == null) {
iBuffer.result = false;
return false;
}
}
int blockId = iBuffer.chunk.getBlock(x, y, z);
if (blockId == 0) {
return true;
} else {
BlockType blockType = iBuffer.assetMap.getAsset(blockId);
if (blockType != BlockType.UNKNOWN
&& blockType.getOpacity() != null
&& blockType.getOpacity() == Opacity.Transparent
&& (iBuffer.opaqueSet == null || !iBuffer.opaqueSet.contains(blockId))) {
return true;
} else {
iBuffer.result = false;
return false;
}
}
},
buffer
);
return buffer.result;
}
},
this.lineOfSightComputeBuffer,
componentAccessor
);
} finally {
this.lineOfSightComputeBuffer.clearRefs();
}
return var9;
}
}
}
public boolean hasLineOfSight(@Nonnull Ref<EntityStore> ref, @Nonnull Ref<EntityStore> targetRef, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
boolean cached = this.lineOfSightCache.containsKey(targetRef);
if (cached) {
if (this.isBenchmarking) {
NPCPlugin.get().collectSensorSupportLosTest(this.role.getRoleIndex(), true, 0L);
}
return this.lineOfSightCache.getByte(targetRef) != 0;
} else {
boolean hasLineOfSight;
if (this.isBenchmarking) {
long start = System.nanoTime();
hasLineOfSight = this.hasLineOfSightInternal(ref, targetRef, componentAccessor);
NPCPlugin.get().collectSensorSupportLosTest(this.role.getRoleIndex(), false, System.nanoTime() - start);
} else {
hasLineOfSight = this.hasLineOfSightInternal(ref, targetRef, componentAccessor);
}
this.lineOfSightCache.put(targetRef, (byte)(hasLineOfSight ? 1 : 0));
return hasLineOfSight;
}
}
public boolean hasInverseLineOfSight(
@Nonnull Ref<EntityStore> ref, @Nonnull Ref<EntityStore> targetRef, @Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
boolean cached = this.inverseLineOfSightCache.containsKey(targetRef);
if (this.isBenchmarking) {
NPCPlugin.get().collectSensorSupportInverseLosTest(this.role.getRoleIndex(), cached);
}
if (cached) {
return this.inverseLineOfSightCache.getByte(targetRef) != 0;
} else {
boolean hasLineOfSight = this.hasLineOfSightInternal(targetRef, ref, componentAccessor);
this.inverseLineOfSightCache.put(targetRef, (byte)(hasLineOfSight ? 1 : 0));
return hasLineOfSight;
}
}
public boolean isFriendlyBlockingLineOfSight(
@Nonnull Ref<EntityStore> ref, @Nonnull Ref<EntityStore> targetRef, @Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
boolean cached = this.friendlyFireCache.containsKey(targetRef);
if (this.isBenchmarking) {
NPCPlugin.get().collectSensorSupportFriendlyBlockingTest(this.role.getRoleIndex(), cached);
}
if (cached) {
return this.friendlyFireCache.getByte(targetRef) != 0;
} else {
boolean blocking = testLineOfSightRays(
ref,
targetRef,
(sx, sy, sz, tx, ty, tz, _this, accessor) -> {
PositionCache.LineOfSightEntityBuffer buffer = _this.lineOfSightEntityComputeBuffer;
buffer.pos.assign(sx, sy, sz);
buffer.dir.assign(tx - sx, ty - sy, tz - sz);
double squaredLength = buffer.dir.squaredLength();
return squaredLength < 1.0E-6
? false
: _this.players
.testAnyEntityDistanceSquared(
squaredLength,
(positionCache, targetRef1, buffer1, componentAccessor1, length2) -> positionCache.testLineOfSightEntity(
ref, targetRef1, buffer1, componentAccessor1, length2
),
_this,
buffer,
accessor
)
|| _this.npcs
.testAnyEntityDistanceSquared(
squaredLength,
(positionCache1, targetRef2, buffer2, componentAccessor2, length3) -> positionCache1.testLineOfSightEntity(
ref, targetRef2, buffer2, componentAccessor2, length3
),
_this,
buffer,
accessor
);
},
this,
componentAccessor
);
this.friendlyFireCache.put(targetRef, (byte)(blocking ? 1 : 0));
return blocking;
}
}
private boolean testLineOfSightEntity(
@Nonnull Ref<EntityStore> ref,
@Nonnull Ref<EntityStore> targetRef,
@Nonnull PositionCache.LineOfSightEntityBuffer buffer,
@Nonnull ComponentAccessor<EntityStore> componentAccessor,
double length2
) {
return !targetRef.equals(ref)
&& this.role.isFriendly(targetRef, componentAccessor)
&& rayIsIntersectingEntity(targetRef, buffer.pos, buffer.dir, buffer.minMax, length2, componentAccessor);
}
private void clearLineOfSightCache(double tickTime) {
this.cacheTTL = (float)((double)this.cacheTTL - tickTime);
if (this.cacheTTL <= 0.0F) {
this.clearLineOfSightCache();
}
}
private void clearLineOfSightCache() {
this.cacheTTL = RandomExtra.randomRange(0.09F, 0.11F);
this.lineOfSightCache.clear();
this.inverseLineOfSightCache.clear();
this.friendlyFireCache.clear();
}
protected static boolean rayIsIntersectingEntity(
@Nonnull Ref<EntityStore> ref,
@Nonnull Vector3d pos,
@Nonnull Vector3d dir,
@Nonnull Vector2d minMax,
double length2,
@Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
BoundingBox boundingBoxComponent = componentAccessor.getComponent(ref, BOUNDING_BOX_COMPONENT_TYPE);
if (boundingBoxComponent == null) {
return false;
} else {
TransformComponent transformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE);
assert transformComponent != null;
Vector3d position = transformComponent.getPosition();
double px = position.getX();
double py = position.getY();
double pz = position.getZ();
double dx = px - pos.x;
double dy = py - pos.y;
double dz = pz - pos.z;
double dotProduct = NPCPhysicsMath.dotProduct(dir.x, dir.y, dir.z, dx, dy, dz);
if (dotProduct <= 0.0) {
return false;
} else {
double dist2 = NPCPhysicsMath.dotProduct(dx, dy, dz);
return dotProduct * dotProduct >= dist2 * length2
? false
: CollisionMath.intersectRayAABB(pos, dir, px, py, pz, boundingBoxComponent.getBoundingBox(), minMax);
}
}
}
@Nonnull
public List<Ref<EntityStore>> getDroppedItemList() {
return this.droppedItems;
}
@Nonnull
public List<Ref<EntityStore>> getSpawnMarkerList() {
return this.spawnMarkers;
}
@Nonnull
public List<Ref<EntityStore>> getSpawnBeaconList() {
return this.spawnBeacons;
}
private static class LineOfSightBuffer {
@Nullable
public World world;
@Nullable
public WorldChunk chunk;
@Nullable
public IntSet opaqueSet;
@Nullable
public BlockTypeAssetMap<String, BlockType> assetMap;
public boolean result;
private LineOfSightBuffer() {
}
public void clearRefs() {
this.world = null;
this.chunk = null;
this.opaqueSet = null;
this.assetMap = null;
}
}
private static class LineOfSightEntityBuffer {
public final Vector3d pos = new Vector3d();
public final Vector3d dir = new Vector3d();
public final Vector2d minMax = new Vector2d();
private LineOfSightEntityBuffer() {
}
}
@FunctionalInterface
public interface RayPredicate<T> {
boolean test(double var1, double var3, double var5, double var7, double var9, double var11, T var13, @Nonnull ComponentAccessor<EntityStore> var14);
}
}