mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2026-01-04 15:41:38 +00:00
完成第三人称光线追踪,修复眼睛位置
This commit is contained in:
@@ -8,21 +8,11 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.bukkit.parser.PlayerParser;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.standard.DoubleParser;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@ import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext;
|
||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||
import net.momirealms.craftengine.core.plugin.entityculling.EntityCullingThread;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||
import net.momirealms.craftengine.core.plugin.network.*;
|
||||
@@ -2036,7 +2035,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
boolean hasGlobalPalette = false;
|
||||
|
||||
// 创建客户端侧世界(只在开启实体情况下创建)
|
||||
ClientSection[] clientSections = Config.enableEntityCulling() ? new ClientSection[count] : null;
|
||||
ClientSection[] clientSections = Config.entityCullingRayTracing() ? new ClientSection[count] : null;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList);
|
||||
@@ -2215,7 +2214,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
|
||||
// 获取客户端侧区域
|
||||
ClientSection clientSection = null;
|
||||
if (Config.enableEntityCulling()) {
|
||||
if (Config.entityCullingRayTracing()) {
|
||||
SectionPos sectionPos = SectionPos.of(sPos);
|
||||
ClientChunk trackedChunk = user.getTrackedChunk(sectionPos.asChunkPos().longKey);
|
||||
clientSection = trackedChunk.sectionById(sectionPos.y);
|
||||
@@ -2261,7 +2260,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
BlockPos pos = buf.readBlockPos();
|
||||
int before = buf.readVarInt();
|
||||
if (Config.enableEntityCulling()) {
|
||||
if (Config.entityCullingRayTracing()) {
|
||||
ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(pos.x >> 4, pos.z >> 4));
|
||||
if (trackedChunk != null) {
|
||||
trackedChunk.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(before));
|
||||
@@ -2441,7 +2440,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
BlockPos blockPos = buf.readBlockPos();
|
||||
int state = buf.readInt();
|
||||
// 移除不透明设置
|
||||
if (Config.enableEntityCulling()) {
|
||||
if (Config.entityCullingRayTracing()) {
|
||||
ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(blockPos.x >> 4, blockPos.z >> 4));
|
||||
if (trackedChunk != null) {
|
||||
trackedChunk.setOccluding(blockPos.x, blockPos.y, blockPos.z, false);
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.bukkit.block.Block;
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.damage.DamageType;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
@@ -116,8 +117,6 @@ public class BukkitServerPlayer extends Player {
|
||||
private IntIdentityList blockList = new IntIdentityList(BlockStateUtils.vanillaBlockStateCount());
|
||||
// cache if player can break blocks
|
||||
private boolean clientSideCanBreak = true;
|
||||
// prevent AFK players from consuming too much CPU resource on predicting
|
||||
private Location previousEyeLocation;
|
||||
// a cooldown for better breaking experience
|
||||
private int lastSuccessfulBreak;
|
||||
// player's game tick
|
||||
@@ -145,6 +144,10 @@ public class BukkitServerPlayer extends Player {
|
||||
// 跟踪到的方块实体渲染器
|
||||
private final Map<BlockPos, VirtualCullableObject> trackedBlockEntityRenderers = new ConcurrentHashMap<>();
|
||||
private final EntityCulling culling;
|
||||
private Vec3d firstPersonCameraVec3;
|
||||
private Vec3d thirdPersonCameraVec3;
|
||||
// 玩家眼睛所在位置
|
||||
private Location eyeLocation;
|
||||
|
||||
public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) {
|
||||
this.channel = channel;
|
||||
@@ -512,10 +515,11 @@ public class BukkitServerPlayer extends Player {
|
||||
@Override
|
||||
public void tick() {
|
||||
// not fully online
|
||||
if (serverPlayer() == null) return;
|
||||
Object serverPlayer = serverPlayer();
|
||||
if (serverPlayer == null) return;
|
||||
org.bukkit.entity.Player bukkitPlayer = platformPlayer();
|
||||
if (VersionHelper.isFolia()) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = FastNMS.INSTANCE.field$ServerPlayer$gameMode(serverPlayer);
|
||||
this.gameTicks = (int) CoreReflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -527,12 +531,30 @@ public class BukkitServerPlayer extends Player {
|
||||
if (this.gameTicks % 20 == 0) {
|
||||
this.updateGUI();
|
||||
}
|
||||
|
||||
// 更新眼睛位置
|
||||
{
|
||||
Location unsureEyeLocation = bukkitPlayer.getEyeLocation();
|
||||
Entity vehicle = bukkitPlayer.getVehicle();
|
||||
if (vehicle != null) {
|
||||
Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer);
|
||||
unsureEyeLocation.set(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + bukkitPlayer.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos));
|
||||
}
|
||||
if (Config.predictBreaking() && !this.isDestroyingCustomBlock && !unsureEyeLocation.equals(this.eyeLocation)) {
|
||||
// if it's not destroying blocks, we do predict
|
||||
if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) {
|
||||
this.predictNextBlockToMine();
|
||||
}
|
||||
}
|
||||
this.eyeLocation = unsureEyeLocation;
|
||||
}
|
||||
|
||||
if (hasSwingHand()) {
|
||||
if (this.isDestroyingBlock) {
|
||||
this.tickBlockDestroy();
|
||||
} else if (this.lastStopMiningPos != null && this.gameTicks - this.lastStopMiningTick <= 5) {
|
||||
double range = getCachedInteractionRange();
|
||||
RayTraceResult result = platformPlayer().rayTraceBlocks(range, FluidCollisionMode.NEVER);
|
||||
RayTraceResult result = rayTrace(this.eyeLocation, range, FluidCollisionMode.NEVER);
|
||||
if (result != null) {
|
||||
Block hitBlock = result.getHitBlock();
|
||||
if (hitBlock != null) {
|
||||
@@ -555,36 +577,57 @@ public class BukkitServerPlayer extends Player {
|
||||
this.isHackedBreak = false;
|
||||
}
|
||||
}
|
||||
if (Config.predictBreaking() && !this.isDestroyingCustomBlock) {
|
||||
// if it's not destroying blocks, we do predict
|
||||
if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) {
|
||||
Location eyeLocation = platformPlayer().getEyeLocation();
|
||||
if (eyeLocation.equals(this.previousEyeLocation)) {
|
||||
return;
|
||||
|
||||
if (Config.entityCullingRayTracing()) {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
Location eyeLocation = this.eyeLocation.clone();
|
||||
this.firstPersonCameraVec3 = LocationUtils.toVec3d(eyeLocation);
|
||||
int distance = 4;
|
||||
if (VersionHelper.isOrAbove1_21_6()) {
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null && vehicle.getType() == EntityType.HAPPY_GHAST) {
|
||||
distance = 8;
|
||||
}
|
||||
this.previousEyeLocation = eyeLocation;
|
||||
this.predictNextBlockToMine();
|
||||
}
|
||||
this.thirdPersonCameraVec3 = LocationUtils.toVec3d(eyeLocation.subtract(eyeLocation.getDirection().multiply(distance)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entityCullingTick() {
|
||||
this.culling.restoreTokenOnTick();
|
||||
boolean useRayTracing = Config.entityCullingRayTracing();
|
||||
for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) {
|
||||
CullingData cullingData = cullableObject.cullable.cullingData();
|
||||
if (cullingData != null) {
|
||||
Vec3d vec3d = LocationUtils.toVec3d(platformPlayer().getEyeLocation());
|
||||
boolean visible = this.culling.isVisible(cullingData, vec3d);
|
||||
if (visible != cullableObject.isShown) {
|
||||
if (Config.enableEntityCullingRateLimiting() && visible) {
|
||||
if (this.culling.takeToken()) {
|
||||
cullableObject.setShown(this, true);
|
||||
}
|
||||
} else {
|
||||
cullableObject.setShown(this, visible);
|
||||
boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing);
|
||||
// 之前可见
|
||||
if (cullableObject.isShown) {
|
||||
boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing);
|
||||
if (!firstPersonVisible && !thirdPersonVisible) {
|
||||
cullableObject.setShown(this, false);
|
||||
}
|
||||
}
|
||||
// 之前不可见
|
||||
else {
|
||||
// 但是第一人称可见了
|
||||
if (firstPersonVisible) {
|
||||
// 下次再说
|
||||
if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) {
|
||||
continue;
|
||||
}
|
||||
cullableObject.setShown(this, true);
|
||||
continue;
|
||||
}
|
||||
if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) {
|
||||
// 下次再说
|
||||
if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) {
|
||||
continue;
|
||||
}
|
||||
cullableObject.setShown(this, true);
|
||||
}
|
||||
// 仍然不可见
|
||||
}
|
||||
} else {
|
||||
cullableObject.setShown(this, true);
|
||||
}
|
||||
@@ -607,17 +650,12 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
public boolean canInteractWithBlock(BlockPos pos, double distance) {
|
||||
double d = this.getCachedInteractionRange() + distance;
|
||||
return (new AABB(pos)).distanceToSqr(this.getEyePosition()) < d * d;
|
||||
return (new AABB(pos)).distanceToSqr(this.getEyePos()) < d * d;
|
||||
}
|
||||
|
||||
public boolean canInteractPoint(Vec3d pos, double distance) {
|
||||
double d = this.getCachedInteractionRange() + distance;
|
||||
return Vec3d.distanceToSqr(this.getEyePosition(), pos) < d * d;
|
||||
}
|
||||
|
||||
public final Vec3d getEyePosition() {
|
||||
Location eyeLocation = this.platformPlayer().getEyeLocation();
|
||||
return new Vec3d(eyeLocation.getX(), eyeLocation.getY(), eyeLocation.getZ());
|
||||
return Vec3d.distanceToSqr(this.getEyePos(), pos) < d * d;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -777,7 +815,7 @@ public class BukkitServerPlayer extends Player {
|
||||
try {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
double range = getCachedInteractionRange();
|
||||
RayTraceResult result = player.rayTraceBlocks(range, FluidCollisionMode.NEVER);
|
||||
RayTraceResult result = rayTrace(this.eyeLocation, range, FluidCollisionMode.NEVER);
|
||||
if (result == null) return;
|
||||
Block hitBlock = result.getHitBlock();
|
||||
if (hitBlock == null) return;
|
||||
@@ -1217,7 +1255,7 @@ public class BukkitServerPlayer extends Player {
|
||||
@Override
|
||||
public void removeTrackedChunk(long chunkPos) {
|
||||
this.trackedChunks.remove(chunkPos);
|
||||
if (Config.enableEntityCulling()) {
|
||||
if (Config.entityCullingRayTracing()) {
|
||||
this.culling.removeLastVisitChunkIfMatches((int) chunkPos, (int) (chunkPos >> 32));
|
||||
}
|
||||
}
|
||||
@@ -1367,4 +1405,36 @@ public class BukkitServerPlayer extends Player {
|
||||
public void clearTrackedBlockEntities() {
|
||||
this.trackedBlockEntityRenderers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldPosition eyePosition() {
|
||||
return LocationUtils.toWorldPosition(this.getEyeLocation());
|
||||
}
|
||||
|
||||
public Location getEyeLocation() {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
Location eyeLocation = player.getEyeLocation();
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null) {
|
||||
Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer());
|
||||
eyeLocation.set(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + player.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos));
|
||||
}
|
||||
return eyeLocation;
|
||||
}
|
||||
|
||||
public Vec3d getEyePos() {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null) {
|
||||
Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer());
|
||||
return new Vec3d(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + player.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos));
|
||||
} else {
|
||||
Location location = player.getLocation();
|
||||
return new Vec3d(location.getX(), location.getY() + player.getEyeHeight(), location.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
private RayTraceResult rayTrace(Location start, double range, FluidCollisionMode mode) {
|
||||
return start.getWorld().rayTraceBlocks(start, start.getDirection(), range, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,11 +554,13 @@ chunk-system:
|
||||
|
||||
# [Premium Exclusive]
|
||||
client-optimization:
|
||||
# Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure.
|
||||
# Requires a restart to fully apply.
|
||||
entity-culling:
|
||||
enable: true
|
||||
view-distance: 64
|
||||
# Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure.
|
||||
ray-tracing: true
|
||||
# Cull entities based on distance
|
||||
view-distance: 64 # -1 = no limit
|
||||
# Determining the number of threads to execute these raytrace operations
|
||||
threads: 1
|
||||
# Limit the maximum number of entities with visibility changes per tick for one player
|
||||
|
||||
@@ -715,13 +715,14 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
|
||||
return null;
|
||||
}
|
||||
if (!(arguments instanceof Map)) {
|
||||
return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5);
|
||||
return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true);
|
||||
}
|
||||
Map<String, Object> argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling");
|
||||
return new CullingData(
|
||||
ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", 1), "aabb"),
|
||||
ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"),
|
||||
ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion")
|
||||
ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"),
|
||||
ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -218,4 +218,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
|
||||
@Override
|
||||
public void remove() {
|
||||
}
|
||||
|
||||
public abstract WorldPosition eyePosition();
|
||||
}
|
||||
|
||||
@@ -208,6 +208,7 @@ public class Config {
|
||||
protected boolean client_optimization$entity_culling$enable;
|
||||
protected int client_optimization$entity_culling$view_distance;
|
||||
protected int client_optimization$entity_culling$threads;
|
||||
protected boolean client_optimization$entity_culling$ray_tracing;
|
||||
protected boolean client_optimization$entity_culling$rate_limiting$enable;
|
||||
protected int client_optimization$entity_culling$rate_limiting$bucket_size;
|
||||
protected int client_optimization$entity_culling$rate_limiting$restore_per_tick;
|
||||
@@ -579,6 +580,7 @@ public class Config {
|
||||
}
|
||||
client_optimization$entity_culling$view_distance = config.getInt("client-optimization.entity-culling.view-distance", 64);
|
||||
client_optimization$entity_culling$threads = config.getInt("client-optimization.entity-culling.threads", 1);
|
||||
client_optimization$entity_culling$ray_tracing = client_optimization$entity_culling$enable && config.getBoolean("client-optimization.entity-culling.ray-tracing", true);
|
||||
client_optimization$entity_culling$rate_limiting$enable = config.getBoolean("client-optimization.entity-culling.rate-limiting.enable", true);
|
||||
client_optimization$entity_culling$rate_limiting$bucket_size = config.getInt("client-optimization.entity-culling.rate-limiting.bucket-size", 300);
|
||||
client_optimization$entity_culling$rate_limiting$restore_per_tick = config.getInt("client-optimization.entity-culling.rate-limiting.restore-per-tick", 5);
|
||||
@@ -1197,6 +1199,10 @@ public class Config {
|
||||
return instance.client_optimization$entity_culling$rate_limiting$restore_per_tick;
|
||||
}
|
||||
|
||||
public static boolean entityCullingRayTracing() {
|
||||
return instance.client_optimization$entity_culling$ray_tracing;
|
||||
}
|
||||
|
||||
public YamlDocument loadOrCreateYamlData(String fileName) {
|
||||
Path path = this.plugin.dataFolderPath().resolve(fileName);
|
||||
if (!Files.exists(path)) {
|
||||
|
||||
@@ -6,22 +6,28 @@ public final class CullingData {
|
||||
public final AABB aabb;
|
||||
public final int maxDistance;
|
||||
public final double aabbExpansion;
|
||||
public final boolean rayTracing;
|
||||
|
||||
public CullingData(AABB aabb, int maxDistance, double aabbExpansion) {
|
||||
public CullingData(AABB aabb, int maxDistance, double aabbExpansion, boolean rayTracing) {
|
||||
this.aabb = aabb;
|
||||
this.maxDistance = maxDistance;
|
||||
this.aabbExpansion = aabbExpansion;
|
||||
this.rayTracing = rayTracing;
|
||||
}
|
||||
|
||||
public AABB aabb() {
|
||||
return aabb;
|
||||
return this.aabb;
|
||||
}
|
||||
|
||||
public int maxDistance() {
|
||||
return maxDistance;
|
||||
return this.maxDistance;
|
||||
}
|
||||
|
||||
public double aabbExpansion() {
|
||||
return aabbExpansion;
|
||||
return this.aabbExpansion;
|
||||
}
|
||||
|
||||
public boolean rayTracing() {
|
||||
return this.rayTracing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ public final class EntityCulling {
|
||||
private final Player player;
|
||||
private final boolean[] dotSelectors = new boolean[MAX_SAMPLES];
|
||||
private final MutableVec3d[] targetPoints = new MutableVec3d[MAX_SAMPLES];
|
||||
private final int[] lastHitBlock = new int[MAX_SAMPLES * 3];
|
||||
private final boolean[] canCheckLastHitBlock = new boolean[MAX_SAMPLES];
|
||||
private final int[] lastHitBlock = new int[3];
|
||||
private boolean canCheckLastHitBlock = false;
|
||||
private int hitBlockCount = 0;
|
||||
private int lastVisitChunkX = Integer.MAX_VALUE;
|
||||
private int lastVisitChunkZ = Integer.MAX_VALUE;
|
||||
@@ -52,9 +52,9 @@ public final class EntityCulling {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isVisible(CullingData cullable, Vec3d cameraPos) {
|
||||
public boolean isVisible(CullingData cullable, Vec3d cameraPos, boolean rayTracing) {
|
||||
// 情空标志位
|
||||
Arrays.fill(this.canCheckLastHitBlock, false);
|
||||
this.canCheckLastHitBlock = false;
|
||||
this.hitBlockCount = 0;
|
||||
AABB aabb = cullable.aabb;
|
||||
double aabbExpansion = cullable.aabbExpansion;
|
||||
@@ -97,6 +97,10 @@ public final class EntityCulling {
|
||||
}
|
||||
}
|
||||
|
||||
if (!rayTracing || !cullable.rayTracing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 清空之前的缓存
|
||||
Arrays.fill(this.dotSelectors, false);
|
||||
if (relX == Relative.POSITIVE) {
|
||||
@@ -190,7 +194,7 @@ public final class EntityCulling {
|
||||
int startBlockZ = MiscUtils.floor(start.z);
|
||||
|
||||
// 遍历所有目标点进行视线检测
|
||||
outer: for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) {
|
||||
for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) {
|
||||
MutableVec3d currentTarget = targets[targetIndex];
|
||||
|
||||
// 计算起点到目标的相对向量(世界坐标差)
|
||||
@@ -199,14 +203,9 @@ public final class EntityCulling {
|
||||
double deltaZ = start.z - currentTarget.z;
|
||||
|
||||
// 检查之前命中的方块,大概率还是命中
|
||||
for (int i = 0; i < MAX_SAMPLES; i++) {
|
||||
if (this.canCheckLastHitBlock[i]) {
|
||||
int offset = i * 3;
|
||||
if (rayIntersection(this.lastHitBlock[offset], this.lastHitBlock[offset + 1], this.lastHitBlock[offset + 2], start, new MutableVec3d(deltaX, deltaY, deltaZ).normalize())) {
|
||||
continue outer;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
if (this.canCheckLastHitBlock) {
|
||||
if (rayIntersection(this.lastHitBlock[0], this.lastHitBlock[1], this.lastHitBlock[2], start, new MutableVec3d(deltaX, deltaY, deltaZ).normalize())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +291,7 @@ public final class EntityCulling {
|
||||
if (isLineOfSightClear) {
|
||||
return true;
|
||||
} else {
|
||||
this.canCheckLastHitBlock[this.hitBlockCount++] = true;
|
||||
this.canCheckLastHitBlock = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.entityculling;
|
||||
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -14,6 +15,7 @@ public class EntityCullingThread {
|
||||
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
||||
private final int id;
|
||||
private final int threads;
|
||||
private int timer;
|
||||
|
||||
public EntityCullingThread(int id, int threads) {
|
||||
this.id = id;
|
||||
@@ -35,7 +37,7 @@ public class EntityCullingThread {
|
||||
this.scheduler.execute(() -> {
|
||||
try {
|
||||
int processed = 0;
|
||||
long startTime = System.currentTimeMillis();
|
||||
long startTime = System.nanoTime();
|
||||
|
||||
for (Player player : CraftEngine.instance().networkManager().onlineUsers()) {
|
||||
// 使用绝对值确保非负,使用 threads 而不是 threads-1 确保均匀分布
|
||||
@@ -45,10 +47,10 @@ public class EntityCullingThread {
|
||||
}
|
||||
}
|
||||
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
if (duration > 45) {
|
||||
String value = String.format("EntityCullingThread-%d processed %d players in %dms (over 45ms)",
|
||||
this.id, processed, duration);
|
||||
long duration = System.nanoTime() - startTime;
|
||||
if (Config.debugEntityCulling() && this.timer++ % 20 == 0) {
|
||||
String value = String.format("EntityCullingThread-%d processed %d players in %sms",
|
||||
this.id, processed, String.format("%.2f", duration / 1_000_000.0));
|
||||
Debugger.ENTITY_CULLING.debug(() -> value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -143,7 +143,7 @@ public class CEChunk {
|
||||
ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(
|
||||
elements,
|
||||
Optional.ofNullable(state.cullingData())
|
||||
.map(data -> new CullingData(data.aabb.move(pos), data.maxDistance, data.aabbExpansion))
|
||||
.map(data -> new CullingData(data.aabb.move(pos), data.maxDistance, data.aabbExpansion, data.rayTracing))
|
||||
.orElse(null)
|
||||
);
|
||||
World wrappedWorld = this.world.world();
|
||||
|
||||
@@ -48,7 +48,7 @@ byte_buddy_version=1.18.1
|
||||
ahocorasick_version=0.6.3
|
||||
snake_yaml_version=2.5
|
||||
anti_grief_version=1.0.5
|
||||
nms_helper_version=1.0.137
|
||||
nms_helper_version=1.0.138
|
||||
evalex_version=3.5.0
|
||||
reactive_streams_version=1.0.4
|
||||
amazon_awssdk_version=2.38.7
|
||||
|
||||
Reference in New Issue
Block a user