ID:2864652
 
Resolved
Internal view() flags didn't reset correctly in some cases, resulting in incorrect view/hearers/etc. lists.
BYOND Version:515.1603
Operating System:Windows 11 Pro 64-bit
Web Browser:Firefox 113.0
Applies to:Dream Daemon
Status: Resolved (515.1604)

This issue has been resolved.
Descriptive Problem Summary:

If hearers() is used in a nested for loop, with "in hearers()", it will return results through opaque atoms.

Numbered Steps to Reproduce Problem:

1. Atom with a proc to list hearers inside another for loop
2. Another atom hidden behind opaque turfs.
3. The atom will be listed as a hearer despite behind hidden.

Code Snippet (if applicable) to Reproduce Problem:
Testcase ZIP: https://www.dropbox.com/s/zaet5do431nrbyg/hearers-bug.zip

/obj/proc/print_hearers()
var/list/H = hearers(7, loc) // nothing printed
for(var/i in 1 to H.len)
var/mob/A2 = H[i]
world.log << "[A2]"

var/list/valid_turfs = list(loc) // atom printed!
for(var/turf_z as() in valid_turfs)
var/turf/T = valid_turfs[turf_z]
for(var/mob/A as() in hearers(7, T))
world.log << "[A]"

var/list/H2 = hearers(7, loc) // nothing printed
for(var/i2 in 1 to H2.len)
var/mob/A3 = H2[i2]
world.log << "[A3]"


Expected Results:

The atom should not be in the hearers list

Actual Results:

The atom IS in the hearers list

Does the problem occur:
Every time? Or how often? Always
In other games? In SS13 and a BYOND testcase
In other user accounts? N/A
On other computers? N/A

When does the problem NOT occur? When hearers() is used in other contexts

Did the problem NOT occur in any earlier versions? If so, what was the last version that worked?

Works in 514.1589

Workarounds:

Not using "in hearers()"
I'm not getting the same results as you in 514.1589. I compiled in 514, and the output is consistent between 514 and 515.

[edit]
Actually your test project and example code don't show a bug at all, unless hearers() from a null loc including anything at all is a bug. I added more output to make sense of the proc:

/obj/proc/print_hearers()
world.log << "[src] hearers (non-nested):"
var/list/H = hearers(7, loc)
world.log << "Hearers from \ref[loc]"
for(var/i in 1 to H.len)
var/mob/A2 = H[i]
world.log << "[A2]"

world.log << "[src] hearers (nested):"
var/list/valid_turfs = list(loc)
for(var/turf_z as() in valid_turfs)
var/turf/T = valid_turfs[turf_z]
world.log << "Hearers from \ref[T]"
for(var/mob/A as() in hearers(7, T))
world.log << "[A]"

world.log << "[src] hearers (2nd non-nested):"
var/list/H2 = hearers(7, loc)
world.log << "Hearers from \ref[loc]"
for(var/i2 in 1 to H2.len)
var/mob/A3 = H2[i2]
world.log << "[A3]"

The resulting output makes it clear why the nested loop behaves differently, which I actually spotted as I was adding the debugging. You're looping through the valid_turfs list but then you're trying to grab T as an associated value that doesn't exist. Instead of using turf_z as the center for hearers(), you're using T which in that particular case is null.
https://www.dropbox.com/s/felnmgngpfnq26b/hearers-bug-2.zip

Modified version with working test case
Lummox JR resolved issue with message:
Internal view() flags didn't reset correctly in some cases, resulting in incorrect view/hearers/etc. lists.