Skip to content

fix looking at issues#52

Merged
Tofaa2 merged 7 commits intoTofaa2:masterfrom
InstantlyMoist:fix/look-at-path
Apr 15, 2026
Merged

fix looking at issues#52
Tofaa2 merged 7 commits intoTofaa2:masterfrom
InstantlyMoist:fix/look-at-path

Conversation

@InstantlyMoist
Copy link
Copy Markdown
Contributor

@InstantlyMoist InstantlyMoist commented Apr 9, 2026

should fix look at path behaviour by using the cached value in NPCPath computed on waypoint change.

Summary by CodeRabbit

  • New Features

    • Enhanced NPC movement with physics-based vertical motion, including jump mechanics and ground detection.
    • Improved hologram attachment that now follows NPCs more naturally.
    • Better head rotation and facing direction during path following.
  • Changes

    • Spawned player NPCs no longer appear in the server tab list.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e4d72d70-db88-49f4-844c-3f3dbe2ca081

📥 Commits

Reviewing files that changed from the base of the PR and between eec4d85 and 1bd6248.

📒 Files selected for processing (2)
  • spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPC.java
  • spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java

📝 Walkthrough

Walkthrough

The pull request refactors NPC movement and rotation mechanics. Changes include physics-based vertical movement (gravity, jumping, ground detection) in path following, persistent yaw tracking in paths, improved waypoint detection using horizontal distance with bounded Y delta, and a shift from packet-based head rotation to entity-based rotation. Hologram positioning now uses entity parenting instead of manual Y-offset teleportation.

Changes

Cohort / File(s) Summary
Path Following Physics
spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java
Refactored vertical movement with gravity (GRAVITY, JUMP_VELOCITY), verticalVelocity state, and new methods (jump(), applyVerticalPhysics(), land(), isOnGround(), setOnGround()). Waypoint advancement now uses horizontal distance squared with bounded Y delta. Added findGroundLevel() helper for obstacle detection and ground resolution.
Head Rotation and Hologram Updates
spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java
Changed head rotation from per-viewer WrapperPlayServerEntityHeadLook packets to entity-based rotateHead() calls. For isLookAtPath() mode, skips packet dispatch entirely. Hologram positioning now uses setParent() instead of manual Y-offset teleportation.
Path Yaw Tracking
spaceNPC/src/main/java/me/tofaa/entitylib/npc/path/NPCPath.java
Added persistent yaw field with new recomputeYaw() helper using atan2 on waypoint X/Z deltas. Yaw recalculated during index transitions: advanceToNext(), setIndex(), reset(), and removeWaypoint(). New public getYaw() accessor.
NPC Tab List
spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPC.java
Changed spawned NPC player tab list inclusion from true to false.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • fix looking at issues #52 — Directly modifies the same NPCMovement logic, PathFollowing inner state, and NPCPath yaw handling, indicating this is a related or continuation effort.

Poem

🐰 Hop along the path so true,
Physics guides each move we do,
Gravity flows, the yaw turns bright,
No packets needed—rotation's right!
Hologram parents dance with grace,
NPC feet find their place.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix looking at issues' accurately reflects the main objective: fixing look-at path behavior by using cached yaw values in NPCPath, which is the primary change across NPCMovement.java and NPCPath.java.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java`:
- Around line 250-256: The head rotation currently always uses
npc.getPath().getYaw() which ignores npc.getOptions().isLookAtPath(); update the
call to entity.rotateHead(...) in NPCMovement to use the same conditional as the
body teleport (i.e. choose npc.getPath().getYaw() when
npc.getOptions().isLookAtPath() is true, otherwise use current.getYaw()) so the
head honors the isLookAtPath() option; keep the same second argument (0) as
before.

In `@spaceNPC/src/main/java/me/tofaa/entitylib/npc/path/NPCPath.java`:
- Around line 72-85: The cached yaw is only recomputed in advanceToNext(),
causing processPathFollowing() to read a stale or default yaw after reset() or
setIndex(); update the code so that whenever the active waypoint index changes
(including in reset(), setIndex(), and any method that directly sets
currentIndex) you recompute this.yaw using the same calculation currently in
advanceToNext() (use currentIndex, previousIndex/currentWaypoint,
waypoints.get(currentIndex) and Math.atan2 to compute degrees) so getYaw()
returns the correct heading before the first advance; keep the existing
advanceToNext() logic but extract or duplicate the yaw-compute step into a
shared helper invoked whenever the active waypoint is set.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f96dd48-7382-4f74-bcc9-73993f592054

📥 Commits

Reviewing files that changed from the base of the PR and between 8fdf1ec and 41097d6.

📒 Files selected for processing (2)
  • spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java
  • spaceNPC/src/main/java/me/tofaa/entitylib/npc/path/NPCPath.java

Comment thread spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java Outdated
Comment thread spaceNPC/src/main/java/me/tofaa/entitylib/npc/path/NPCPath.java
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java`:
- Around line 94-96: The hologram sync currently calls npc.getEntity().get()
again inside the npc.getHologram().ifPresent(...) lambda, risking a
NoSuchElementException if the Optional changes; capture the entity Optional once
(e.g., via a local variable like entityOpt or entity) before the
hologram.ifPresent and use that local reference when calling
hologram.setParent(...) so the code uses the previously read entity instead of
re-fetching npc.getEntity().
- Around line 215-262: The async path processing is calling thread-unsafe Bukkit
APIs (world.getBlockAt, entity.teleport, entity.rotateHead,
Bukkit.getOnlinePlayers, player.getLocation) from methods like
processPathFollowing, processAllNPCHeadRotation, and processViewerSync; change
the code to perform only thread-safe computations asynchronously and move any
Bukkit API interactions to the main server thread by scheduling synchronous
tasks (e.g., using the BukkitScheduler runTask/submit to main) for: block/world
queries (replace direct world.getBlockAt calls with a sync-block that reads
needed block/passable data), entity operations (teleport, rotateHead,
follower.jump/land/setOnGround and verticalVelocity writes), and viewer
iteration (Bukkit.getOnlinePlayers and player.getLocation). Identify and wrap
the sections around world.getBlockAt,
follower.jump/applyVerticalPhysics/land/setOnGround/verticalVelocity,
entity.teleport, entity.rotateHead, and any player location reads so they
execute on the main thread while keeping path math async.
- Around line 264-269: The vertical (Y) interpolation uses fullLen that was
computed after dx/dz were normalized, so Y stepping is no longer true 3D
interpolation; in NPCMovement fix this by computing the 3D distance using the
original deltas (capture origDx = target.getX() - current.getX() and origDz =
target.getZ() - current.getZ(), keep dy = target.getY() - current.getY()), then
compute fullLen3D = Math.sqrt(origDx*origDx + dy*dy + origDz*origDz) and use
fullLen3D when calculating newY (newY = current.getY() + (dy / fullLen3D) *
speed), leaving the X/Z normalization and movement logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59791095-4409-43af-a31c-51832c4d515f

📥 Commits

Reviewing files that changed from the base of the PR and between 77f4de3 and adccaf7.

📒 Files selected for processing (3)
  • spaceNPC/build.gradle.kts
  • spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPC.java
  • spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java

Comment thread spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java
Comment on lines +215 to +262
if (npc.getOptions().isClampToGround()) {
var world = npc.getWorld();
if (world != null) {
int bx = (int) Math.floor(newX);
int bz = (int) Math.floor(newZ);
int feetY = (int) Math.floor(current.getY());

// Check if there's a solid block ahead at feet level (obstacle)
var blockAtFeet = world.getBlockAt(bx, feetY, bz);
var blockAboveFeet = world.getBlockAt(bx, feetY + 1, bz);

boolean obstacleAhead = !blockAtFeet.isPassable()
&& blockAboveFeet.isPassable();

// If we hit a 1-block obstacle and we're on the ground, jump
if (obstacleAhead && follower.isOnGround()) {
follower.jump();
}
}

Location newLoc = new Location(
newX,
newY,
newZ,
yaw,
0
);
// Apply vertical physics (gravity + velocity)
newY = follower.applyVerticalPhysics(current.getY());

// Resolve ground collision: find solid ground below new position
int newFeetY = (int) Math.floor(newY);
double groundLevel = findGroundLevel(world, bx, newFeetY, bz, feetY + 2);

entity.teleport(newLoc);
if (newY <= groundLevel) {
// Landed on ground
newY = groundLevel;
follower.land(groundLevel);
} else {
// Still airborne
follower.setOnGround(false);
}

entity.rotateHead(yaw, 0);
// If jumping into a ceiling (block above head), stop upward velocity
int headY = (int) Math.floor(newY + 1.8);
var blockAtHead = world.getBlockAt(bx, headY, bz);
if (!blockAtHead.isPassable() && follower.verticalVelocity > 0) {
follower.verticalVelocity = 0;
}

// If obstacle is 2+ blocks tall, don't move horizontally into it
if (!blockAtFeet.isPassable() && !blockAboveFeet.isPassable()) {
newX = current.getX();
newZ = current.getZ();
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Show async scheduler setup and affected processing methods"
rg -n -C4 'runTaskTimerAsynchronously|processPathFollowing|processViewerSync|processAllNPCHeadRotation' \
  spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java

echo
echo "Show Bukkit world/entity API usage inside these methods"
rg -n -C3 'getBlockAt|Bukkit.getOnlinePlayers|player.getLocation|entity.teleport|rotateHead' \
  spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java

Repository: Tofaa2/EntityLib

Length of output: 5668


Thread-unsafe Bukkit operations in async scheduler context.

The processPathFollowing() method (invoked from runTaskTimerAsynchronously) executes multiple non-thread-safe Bukkit API calls: world.getBlockAt() (lines 223–224, 252), entity.teleport() (line 281), and entity.rotateHead() (line 282). Additionally, processAllNPCHeadRotation() and processViewerSync() also call thread-unsafe operations (entity.rotateHead(), Bukkit.getOnlinePlayers(), player.getLocation()) from async tasks. These operations must run on the main thread only and will cause crashes or data corruption.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java` around lines
215 - 262, The async path processing is calling thread-unsafe Bukkit APIs
(world.getBlockAt, entity.teleport, entity.rotateHead, Bukkit.getOnlinePlayers,
player.getLocation) from methods like processPathFollowing,
processAllNPCHeadRotation, and processViewerSync; change the code to perform
only thread-safe computations asynchronously and move any Bukkit API
interactions to the main server thread by scheduling synchronous tasks (e.g.,
using the BukkitScheduler runTask/submit to main) for: block/world queries
(replace direct world.getBlockAt calls with a sync-block that reads needed
block/passable data), entity operations (teleport, rotateHead,
follower.jump/land/setOnGround and verticalVelocity writes), and viewer
iteration (Bukkit.getOnlinePlayers and player.getLocation). Identify and wrap
the sections around world.getBlockAt,
follower.jump/applyVerticalPhysics/land/setOnGround/verticalVelocity,
entity.teleport, entity.rotateHead, and any player location reads so they
execute on the main thread while keeping path math async.

Comment on lines +264 to +269
// No ground clamping: use linear Y interpolation (original behavior)
double dy = target.getY() - current.getY();
double fullLen = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (fullLen > 0.01) {
newY = current.getY() + (dy / fullLen) * speed;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Non-clamp vertical interpolation currently uses normalized X/Z components.

fullLen is computed after dx/dz may have been normalized, so Y stepping is no longer true 3D interpolation toward the waypoint.

Proposed fix
-                    double dx = target.getX() - current.getX();
-                    double dz = target.getZ() - current.getZ();
+                    double rawDx = target.getX() - current.getX();
+                    double rawDz = target.getZ() - current.getZ();
+                    double dx = rawDx;
+                    double dz = rawDz;
                     double hLen = Math.sqrt(dx * dx + dz * dz);
@@
                     } else {
                         // No ground clamping: use linear Y interpolation (original behavior)
                         double dy = target.getY() - current.getY();
-                        double fullLen = Math.sqrt(dx * dx + dy * dy + dz * dz);
+                        double fullLen = Math.sqrt(rawDx * rawDx + dy * dy + rawDz * rawDz);
                         if (fullLen > 0.01) {
                             newY = current.getY() + (dy / fullLen) * speed;
                         }
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spaceNPC/src/main/java/me/tofaa/entitylib/npc/NPCMovement.java` around lines
264 - 269, The vertical (Y) interpolation uses fullLen that was computed after
dx/dz were normalized, so Y stepping is no longer true 3D interpolation; in
NPCMovement fix this by computing the 3D distance using the original deltas
(capture origDx = target.getX() - current.getX() and origDz = target.getZ() -
current.getZ(), keep dy = target.getY() - current.getY()), then compute
fullLen3D = Math.sqrt(origDx*origDx + dy*dy + origDz*origDz) and use fullLen3D
when calculating newY (newY = current.getY() + (dy / fullLen3D) * speed),
leaving the X/Z normalization and movement logic unchanged.

@Tofaa2 Tofaa2 merged commit 0a3dc10 into Tofaa2:master Apr 15, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants