Skip to content

Commit bbcc0e9

Browse files
committed
Fix Waypoints Bug
1 parent bbf6698 commit bbcc0e9

2 files changed

Lines changed: 140 additions & 4 deletions

File tree

build.gradle

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ def cleanClientWithMods = tasks.register("cleanClientWithMods", Delete) {
133133
delete "run/mods"
134134
}
135135

136+
// Work around NAS/Windows directory-handle issues where deleting the build
137+
// root folder itself can fail with "directory is not empty" even after files
138+
// are gone. Clean only the contents of build/.
139+
tasks.named("clean", Delete).configure {
140+
setDelete([])
141+
delete providers.provider {
142+
def dir = layout.buildDirectory.get().asFile
143+
return dir.exists() ? (dir.listFiles() ?: [] as File[]) : [] as File[]
144+
}
145+
}
146+
136147
def prepareClientWithMods = tasks.register("prepareClientWithMods", Sync) {
137148
from configurations.testMods
138149
into "run/mods"

src/main/java/net/wurstclient/hacks/WaypointsHack.java

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import net.wurstclient.settings.SliderSetting;
4444
import net.wurstclient.settings.ColorSetting;
4545
import net.wurstclient.settings.SliderSetting.ValueDisplay;
46+
import net.wurstclient.util.BlockUtils;
4647
import net.wurstclient.util.RenderUtils;
4748
import net.wurstclient.waypoints.Waypoint;
4849
import net.wurstclient.waypoints.WaypointDimension;
@@ -66,6 +67,7 @@ public final class WaypointsHack extends Hack
6667
private static final Pattern END_PORTAL_NAME_PATTERN =
6768
Pattern.compile("^End Portal (\\d+)$", Pattern.CASE_INSENSITIVE);
6869
private static final long PORTAL_RECORD_COOLDOWN_MS = 2000L;
70+
private static final long OCCLUSION_CACHE_MS = 250L;
6971
private static final String[] DEATH_MESSAGE_PHRASES =
7072
{"was slain", "was shot", "was blown", "was killed", "was fireballed",
7173
"was roasted", "was doomed", "was squashed", "was pummeled",
@@ -92,6 +94,7 @@ public final class WaypointsHack extends Hack
9294
private final Set<UUID> knownDead = new HashSet<>();
9395
// Guard to avoid handling our own injected chat messages
9496
private boolean sendingOwnChat = false;
97+
private final Map<UUID, OcclusionSample> occlusionCache = new HashMap<>();
9598

9699
private final SliderSetting waypointRenderDistance = new SliderSetting(
97100
"Waypoint render distance", 127, 0, DISTANCE_SLIDER_INFINITE, 1,
@@ -141,6 +144,14 @@ public void update()
141144
new CheckboxSetting("Compass text outline", true);
142145
private final SliderSetting compassOutlineOpacity = new SliderSetting(
143146
"Compass outline opacity", 100, 0, 100, 1, ValueDisplay.INTEGER);
147+
private final CheckboxSetting dimObscuredOnScreenWaypoints =
148+
new CheckboxSetting("Dim obscured on-screen waypoints",
149+
"When a waypoint is hidden behind blocks, greatly dim its world label unless you're looking directly at it.",
150+
false);
151+
private final CheckboxSetting iconOnlyForObscuredOnScreenWaypoints =
152+
new CheckboxSetting("Icon-only when obscured",
153+
"When a waypoint is hidden behind blocks, only show its icon unless you're looking directly at it.",
154+
false);
144155

145156
// Waypoint world-label outline settings
146157
private final CheckboxSetting waypointOutline =
@@ -196,6 +207,8 @@ public WaypointsHack()
196207
addSetting(compassOpacity);
197208
addSetting(compassOutline);
198209
addSetting(compassOutlineOpacity);
210+
addSetting(dimObscuredOnScreenWaypoints);
211+
addSetting(iconOnlyForObscuredOnScreenWaypoints);
199212
addSetting(waypointOutline);
200213
addSetting(waypointOutlineOpacity);
201214
addSetting(compassBackgroundOpacity);
@@ -221,6 +234,7 @@ protected void onEnable()
221234
EVENTS.add(GUIRenderListener.class, this);
222235
otherDeathCooldown.clear();
223236
knownDead.clear();
237+
occlusionCache.clear();
224238
}
225239

226240
@Override
@@ -234,6 +248,7 @@ protected void onDisable()
234248
EVENTS.remove(GUIRenderListener.class, this);
235249
otherDeathCooldown.clear();
236250
knownDead.clear();
251+
occlusionCache.clear();
237252
}
238253

239254
// Add a temporary waypoint that should not be persisted. Returns the
@@ -766,6 +781,13 @@ public void onRender(PoseStack matrices, float partialTicks)
766781
return;
767782

768783
var list = new ArrayList<>(manager.all());
784+
long nowMs = System.currentTimeMillis();
785+
double configuredLabelRange = waypointRenderDistance.getValue();
786+
boolean configuredInfiniteLabels =
787+
configuredLabelRange >= DISTANCE_SLIDER_INFINITE;
788+
double occlusionMaxDistSq =
789+
configuredInfiniteLabels ? Double.POSITIVE_INFINITY
790+
: Math.max(0.0, configuredLabelRange * configuredLabelRange);
769791
double beaconRange = beaconRenderDistance.getValue();
770792
boolean beaconInfinite = beaconRange >= DISTANCE_SLIDER_INFINITE;
771793
boolean beaconsEnabled = beaconInfinite || beaconRange > 0.0;
@@ -783,6 +805,7 @@ public void onRender(PoseStack matrices, float partialTicks)
783805

784806
double distSq = MC.player.distanceToSqr(wp.getX() + 0.5,
785807
wp.getY() + 0.5, wp.getZ() + 0.5);
808+
int waypointColor = applyFade(w.getColor(), distSq);
786809
double dist = Math.sqrt(distSq);
787810
double trd = waypointRenderDistance.getValue();
788811
boolean infiniteLabels = trd >= DISTANCE_SLIDER_INFINITE;
@@ -836,6 +859,24 @@ public void onRender(PoseStack matrices, float partialTicks)
836859

837860
if(renderLabel)
838861
{
862+
boolean applyObscuredRules =
863+
dimObscuredOnScreenWaypoints.isChecked()
864+
|| iconOnlyForObscuredOnScreenWaypoints.isChecked();
865+
int labelColor = waypointColor;
866+
boolean suppressDetails = false;
867+
if(applyObscuredRules)
868+
{
869+
boolean directlyLooked =
870+
isDirectlyLookingAtWaypoint(wp, 5.0);
871+
boolean obscured =
872+
!directlyLooked && isWaypointObscuredCached(w, wp,
873+
distSq, occlusionMaxDistSq, nowMs);
874+
suppressDetails = obscured
875+
&& iconOnlyForObscuredOnScreenWaypoints.isChecked();
876+
if(obscured && dimObscuredOnScreenWaypoints.isChecked())
877+
labelColor = dimForObscured(labelColor);
878+
}
879+
839880
String title = w.getName() == null ? "" : w.getName();
840881
String icon = iconChar(w.getIcon());
841882
if(!icon.isEmpty())
@@ -919,10 +960,11 @@ public void onRender(PoseStack matrices, float partialTicks)
919960
}
920961
// Keep a constant 10px separation using local pixel offset
921962
float sepPx = 10.0f;
922-
drawWorldLabel(matrices, title, lx, ly, lz,
923-
applyFade(w.getColor(), distSq), scale, -sepPx);
924-
drawWorldLabel(matrices, distanceText, lx, ly, lz,
925-
applyFade(w.getColor(), distSq), (float)(scale * 0.9f), 0f);
963+
drawWorldLabel(matrices, title, lx, ly, lz, labelColor, scale,
964+
-sepPx);
965+
if(!suppressDetails)
966+
drawWorldLabel(matrices, distanceText, lx, ly, lz,
967+
labelColor, (float)(scale * 0.9f), 0f);
926968
}
927969
}
928970
}
@@ -1618,6 +1660,75 @@ private static String cardinalFromYaw(double yaw)
16181660
}
16191661
}
16201662

1663+
private boolean isWaypointObscured(BlockPos waypointPos)
1664+
{
1665+
if(MC.player == null || MC.level == null || waypointPos == null)
1666+
return false;
1667+
1668+
// If the waypoint's chunk isn't loaded, LOS is unknown. Treat it as
1669+
// obscured to avoid far waypoints popping back to full text.
1670+
if(!MC.level.hasChunkAt(waypointPos))
1671+
return true;
1672+
1673+
Vec3 eyes = MC.player.getEyePosition(1.0F);
1674+
Vec3 target = new Vec3(waypointPos.getX() + 0.5,
1675+
waypointPos.getY() + 1.2, waypointPos.getZ() + 0.5);
1676+
return !BlockUtils.hasLineOfSight(eyes, target);
1677+
}
1678+
1679+
private boolean isWaypointObscuredCached(Waypoint waypoint,
1680+
BlockPos waypointPos, double distSq, double maxOcclusionDistSq,
1681+
long nowMs)
1682+
{
1683+
if(waypoint == null || waypointPos == null)
1684+
return false;
1685+
1686+
// Respect user's waypoint render distance setting.
1687+
if(distSq > maxOcclusionDistSq)
1688+
return false;
1689+
1690+
OcclusionSample cached = occlusionCache.get(waypoint.getUuid());
1691+
if(cached != null && cached.pos.equals(waypointPos)
1692+
&& nowMs - cached.timestampMs <= OCCLUSION_CACHE_MS)
1693+
return cached.obscured;
1694+
1695+
boolean obscured = isWaypointObscured(waypointPos);
1696+
occlusionCache.put(waypoint.getUuid(),
1697+
new OcclusionSample(waypointPos.immutable(), obscured, nowMs));
1698+
return obscured;
1699+
}
1700+
1701+
private boolean isDirectlyLookingAtWaypoint(BlockPos waypointPos,
1702+
double maxAngleDeg)
1703+
{
1704+
if(MC.player == null || waypointPos == null)
1705+
return false;
1706+
1707+
Vec3 eyes = MC.player.getEyePosition(1.0F);
1708+
Vec3 target = new Vec3(waypointPos.getX() + 0.5,
1709+
waypointPos.getY() + 1.2, waypointPos.getZ() + 0.5);
1710+
Vec3 toWp = target.subtract(eyes);
1711+
double len = toWp.length();
1712+
if(len < 1.0E-6)
1713+
return true;
1714+
1715+
Vec3 dir = toWp.scale(1.0 / len);
1716+
Vec3 look = MC.player.getLookAngle();
1717+
double dot = look.dot(dir);
1718+
dot = Math.max(-1.0, Math.min(1.0, dot));
1719+
double angle = Math.toDegrees(Math.acos(dot));
1720+
return angle <= maxAngleDeg;
1721+
}
1722+
1723+
private int dimForObscured(int argb)
1724+
{
1725+
int a = (argb >>> 24) & 0xFF;
1726+
if(a <= 0)
1727+
a = 0xFF;
1728+
int dimmedAlpha = Math.max(14, (int)Math.round(a * 0.2));
1729+
return withAlpha(argb, dimmedAlpha);
1730+
}
1731+
16211732
private static final class WaypointEntry
16221733
{
16231734
final Waypoint w;
@@ -1632,6 +1743,20 @@ private static final class WaypointEntry
16321743
}
16331744
}
16341745

1746+
private static final class OcclusionSample
1747+
{
1748+
final BlockPos pos;
1749+
final boolean obscured;
1750+
final long timestampMs;
1751+
1752+
OcclusionSample(BlockPos pos, boolean obscured, long timestampMs)
1753+
{
1754+
this.pos = pos;
1755+
this.obscured = obscured;
1756+
this.timestampMs = timestampMs;
1757+
}
1758+
}
1759+
16351760
private enum PortalKind
16361761
{
16371762
NETHER,

0 commit comments

Comments
 (0)