Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pls do not squash :3] Allow limiting object observers #13987

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

appgurueu
Copy link
Contributor

@appgurueu appgurueu commented Nov 11, 2023

Closes #8045 straightforwardly: By managing an opt-in set of observers per-entity; in the process I also made players use a proper std::string for their name rather than a char*.

This feature should work with newer servers and older clients as well.

To do

  • API design: I am considering incremental methods add_observers / remove_observers in addition to the current set_observers / get_observers. Opinions?
    • Considered, not really necessary for a minimal feature yet. Unless someone asks for them, I won't add them; note that you can always easily implement them yourselves.
  • What about static_save? The easiest solution would probably be to disallow static_save for entities with managed observers.
  • What else did I fail to take into account?
    • Well well, let's find out! This PR is suspiciously simple, isn't it?

Feedback welcome!

How to test

Join a server with two clients. Spawn a testentities:observable. Punch the observable on one client: It should disappear for you and appear for the other client.

@appgurueu appgurueu marked this pull request as draft November 11, 2023 23:45
@MisterE123
Copy link
Contributor

RE: static_save: Why not clear the table of players allowed to view the entity when the entity is saved, and let the entity set its viewable players when it comes back from being saved?

@appgurueu
Copy link
Contributor Author

appgurueu commented Nov 12, 2023

RE: static_save: Why not clear the table of players allowed to view the entity when the entity is saved, and let the entity set its viewable players when it comes back from being saved?

I considered this, but I believe it's problematic as it could lead to some kind of "polling" / "spurious" wake ups: Suppose a player who's not supposed to be an observer comes near an entity. Since we don't know whether this player will be set as an observer, we have to wake the entity up. The entity then sets its observers, excluding the player. We should put the entity back to sleep, since there is no nearby observer. But if we do that, this cycle repeats ad infinitum while the player is in proximity - the entity gets woken up, put back to sleep, ...

An alternative is to always load entities if any player is in proximity - be that an observer or not - and only unload them if no player is in proximity - again regardless of the observer status of the players. I'm not sure whether this is cleaner, though. (Given that I have not touched this code, I believe this should be the current state of affairs.)

Yet another alternative would be letting the engine persist observers. That way it could check the observers before waking an object up.

Copy link
Member

@sfan5 sfan5 left a comment

Choose a reason for hiding this comment

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

gave the code a quick look, not exhaustive

src/client/localplayer.h Outdated Show resolved Hide resolved
doc/lua_api.md Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
src/server/serveractiveobject.cpp Outdated Show resolved Hide resolved
@MisterE123
Copy link
Contributor

MisterE123 commented Nov 12, 2023

RE: static_save: Why not clear the table of players allowed to view the entity when the entity is saved, and let the entity set its viewable players when it comes back from being saved?

I considered this, but I believe it's problematic as it could lead to some kind of "polling" / "spurious" wake ups: Suppose a player who's not supposed to be an observer comes near an entity. Since we don't know whether this player will be set as an observer, we have to wake the entity up. The entity then sets its observers, excluding the player. We should put the entity back to sleep, since there is no nearby observer. But if we do that, this cycle repeats ad infinitum while the player is in proximity - the entity gets woken up, put back to sleep, ...

An alternative is to always load entities if any player is in proximity - be that an observer or not - and only unload them if no player is in proximity - again regardless of the observer status of the players. I'm not sure whether this is cleaner, though. (Given that I have not touched this code, I believe this should be the current state of affairs.)

Yet another alternative would be letting the engine persist observers. That way it could check the observers before waking an object up.

I thought statically saved entites get woken up when the mapblock they are in loads, and put to sleep by the engine when the mapblock they are in unloads. Given that, the entity should be woken up regardless of who comes near. Are you telling me you will make custom wake/save logic that takes players who can view it into account? Does that mean that you cannot have an entity that is un-viewable by any player exist in an active state for a time?

I disagree that you should put the entity back to sleep if it is not viewable by players; that would be a hardcoded engine "feature" that would take control away from mods. Mods cnn easily use entity logic to delete or save entities if they really need to, but mods would not be able to easily work around an engine feature that automatically statically saved an unviewable entity if it was unviewable.

Usecase: an entity that is invisible to all players but which runs game logic at a particular location.

Usecase: an entity which is invisible to all or some players for a time, and at a specific time determined in on_step, shows itself to some players

Neither of these would be possible if you automatically statically saved entites when they did not have any viewers.

Your second alternative is the way entities currently work, and should be kept that way.

@appgurueu
Copy link
Contributor Author

I thought statically saved entites get woken up when the mapblock they are in loads, and put to sleep by the engine when the mapblock they are in unloads. Given that, the entity should be woken up regardless of who comes near.

Yes, that should be the current state of this PR. I'm not sure whether that's clean though, hence why I'm discussing it.

Are you telling me you will make custom wake/save logic that takes players who can view it into account? Does that mean that you cannot have an entity that is un-viewable by any player exist in an active state for a time?

This would be the third option, yes. I don't know if I will implement this though, it's still up for discussion.

I disagree that you should put the entity back to sleep if it is not viewable by players; that would be a hardcoded engine "feature" that would take control away from mods. Mods cnn easily use entity logic to delete or save entities if they really need to [...]

Good point on the deletion, though I'm not sure on the saving (unless you mean saving entities in something like a mod-managed file rather than the map database).

[...] but mods would not be able to easily work around an engine feature that automatically statically saved an unviewable entity if it was unviewable.

When is it useful to keep an entity that can't be observed around? I feel this is wasteful. Consider something like a statically saved ghost which is supposed to haunt a specific player: You'll wake this ghost up and keep it in memory if any player comes nearby, loading the mapblock (and thus the ghost).

That said, since entity static save is tied to mapblocks, I'm not sure how the ghost could efficiently be left asleep, and I don't want this PR to get out of scope, so leaving it "as is" is indeed probably the easier option here. I suppose this should be documented, though.

(There could be a workaround for this, actually: Have an invisible parent entity with unmanaged observers; make the child have static save = false, and let the parent (re)spawn the child if necessary. But I agree that it's dirty.)

Your second alternative is the way entities currently work, and should be kept that way.

Yes, it's also what I believe this PR currently implements (I haven't tested this yet). Keeping it this way and documenting it properly is probably be the cleanest & simplest solution. What do core devs think?

@appgurueu appgurueu changed the title Proof-of-concept of limiting object observers [pls do not squash :3] Proof-of-concept of limiting object observers Nov 12, 2023
@appgurueu appgurueu marked this pull request as ready for review November 14, 2023 12:57
@MisterE123
Copy link
Contributor

MisterE123 commented Nov 14, 2023

Oh, wow, this allows actual player invisibility

@MisterE123
Copy link
Contributor

I'm curious why you have managed players as:
{pl_name1=true, pl_name2=true}
Instead of:
{pl_name1,pl_name2}
Where only the players in the table can view the object, and an empty table makes no one able to view it, and setting observers to nil (not even an empty table) makes them unmanaged.

Given your system, I am a bit confused as to how you would set an entity to have no observers without constantly using the on_step to add player names to the table and set them to false.

@grorp
Copy link
Member

grorp commented Nov 14, 2023

I couldn't resist: I made a quick-and-dirty per-player hallucination mod with the set_observers API added by this PR.
https://github.com/grorp/hallucinations

Seems to work great!

Given your system, I am a bit confused as to how you would set an entity to have no observers without constantly using the on_step to add player names to the table and set them to false.

Wouldn't that be set_observers({})?

@appgurueu
Copy link
Contributor Author

Oh, wow, this allows actual player invisibility

Yeah, from the perspective of the client players are pretty much just CAOs, so I saw no reason to disallow player invisibility - with the exception that you can't hide a player from themselves this way, since that would break various assumptions the engine makes.

I'm curious why you have managed players as: {pl_name1=true, pl_name2=true}

Because this is a set of player names: Player names are either in the set, or they aren't; they may not appear twice. A table from player names to true captures this well. It also makes tests for inclusion convenient (on the Lua side of things): You can simply do if observers[name] then ... end. I think {[element] = true, ...} is simply the most natural way to model a set in Lua most of the time.

Other engine functions (e.g. set privs) represent sets this way too.

Instead of: {pl_name1,pl_name2}

Lists are another way to represent sets (which the engine also uses), but it doesn't make much of a difference, and I don't see any significant advantages of lists (except maybe for being slightly less typing work); duplicates would also need to be dealt with (error or silently ignore?).

Where only the players in the table can view the object and an empty table makes no one able to view it

Currently the case (using player names as keys, though)

and setting observers to nil (not even an empty table) makes them unmanaged.

Yeah, that's currently the case: Doing obj:set_observers() or obj:set_observers(nil) makes them unmanaged. I suppose I should make this clearer in the docs?

Given your system, I am a bit confused as to how you would set an entity to have no observers

Same as in your proposed list format: obj:set_observers({}), as grorp said.

without constantly using the on_step to add player names to the table and set them to false.

Oh no, don't do that! It's a set, not a map from player name to whether they are an observer 😄

This is interestingly the same confusion as for setting privs ({interact = false} will set the player privileges to exactly interact, rather than revoking interact or similar). I had hoped for my docs to be less ambiguous, but oh well... I should make the code error if false is used as a value, and expand the docs.

@appgurueu appgurueu changed the title [pls do not squash :3] Proof-of-concept of limiting object observers [pls do not squash :3] Allow limiting object observers Nov 14, 2023
@LoneWolfHT
Copy link
Contributor

I'm running this PR on the CaptureTheFlag server, as you can see if you look at teammate nametags. I'll let OP know if there are any issues

@jordan4ibanez
Copy link
Contributor

:3

@hlqkj
Copy link

hlqkj commented Nov 29, 2023

Is the TAB autocomplete in the game chat also benefiting from, or affected by this?

@sfan5 sfan5 self-requested a review November 29, 2023 10:12
@Zughy Zughy added the Concept approved Approved by a core dev: PRs welcomed! label Nov 29, 2023
@appgurueu
Copy link
Contributor Author

Is the TAB autocomplete in the game chat also benefiting from, or affected by this?

In its current form, this doesn't affect TAB autocompletion or the list of names sent to the serverlist at all. Depending on the use case, this could be desirable: Imagine something like an "invisibility" feature in a CTF server. You don't want to pretend that invisible players have left to the serverlist, or disable TAB autocomplete for them. On the other hand, a "cloaking" mod intended to conceal the presence of staff will want to disable both TAB autocomplete and sending the name to the server list; not sending names to the serverlist is also a demanded feature: Servers might have bot players (like NodeCore's spectator which streams to Twitch) which they wish to hide, or players may be asking for slightly more privacy than having their online status on a Minetest server readily available in some public JSON.

In conclusion, I believe it's probably best to leave these features up to a separate PR, to keep this PR simple and focused. Should it be considered necessary to "complete" this feature however, I already have a commit prepared which adapts the player list sent to clients based on who it is sent to, but I'm not very fond of it since it conflates "can see player in-game" and "knows that player is in-game" (e.g. available for a chat), which aren't exactly the same (refer to the example use cases above). The server list is yet another issue, which deserves its own API.

TL;DR: Controlling

  • Tab completion (player list sent to other clients) and
  • Serverlist visibility

deserve to be their own features in follow-up PRs. They are out of scope for this PR (which is only concerned with limiting who the active objects are sent to).

@hlqkj
Copy link

hlqkj commented Nov 30, 2023

Thanks for your answer! I think you are totally right. I was indeed thinking about cloak, but didn't consider the other use case...

doc/lua_api.md Outdated Show resolved Hide resolved
doc/lua_api.md Outdated Show resolved Hide resolved
doc/lua_api.md Outdated
* If players are managed, they always need to have themselves as observers.
* Attachments:
* If an object's observers are managed, the observers of all children need to be managed too.
* The observers of children need to be a subset of the observers of parents.
Copy link
Member

Choose a reason for hiding this comment

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

unanswered question: object (un)loading is not touched by observers, right?

Copy link
Member

Choose a reason for hiding this comment

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

^ don't see this answered yet

src/client/localplayer.cpp Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
src/script/lua_api/l_object.cpp Outdated Show resolved Hide resolved
@sfan5
Copy link
Member

sfan5 commented Dec 12, 2023

Thought: In the unmanaged state the engine should automatically "propagate" the observers to children objects.

Otherwise this feature is quite intrusive because if you want to attach something to the player (*) you suddenly have to change your code to care about observers (to not violate the conditions).

(*) or any other objects whose observer state you don't get to decide

@sfan5 sfan5 added the Action / change needed Code still needs changes (PR) / more information requested (Issues) label Dec 12, 2023
@appgurueu appgurueu force-pushed the entity-observers branch 3 times, most recently from fc4c828 to e3a4bda Compare March 8, 2024 19:48
@appgurueu
Copy link
Contributor Author

appgurueu commented Mar 8, 2024

I've implemented intersecting observers along the parent chain (and rebased).

Glad to know that the segfault doesn't seem to be coming from this PR.

@appgurueu appgurueu removed the Action / change needed Code still needs changes (PR) / more information requested (Issues) label Mar 8, 2024
src/client/localplayer.cpp Outdated Show resolved Hide resolved
@Zughy
Copy link
Member

Zughy commented Mar 17, 2024

@appgurueu rebase needed (can't wait for this PR!)

@Zughy Zughy added the Rebase needed The PR needs to be rebased by its author. label Mar 17, 2024
@appgurueu appgurueu force-pushed the entity-observers branch 2 times, most recently from 3dd4606 to 30716d8 Compare March 18, 2024 11:55
@appgurueu appgurueu removed the Rebase needed The PR needs to be rebased by its author. label Mar 18, 2024
@Zughy Zughy added this to the 5.9.0 milestone Mar 24, 2024
@sfan5 sfan5 self-requested a review April 20, 2024 19:52
Copy link
Member

@sfan5 sfan5 left a comment

Choose a reason for hiding this comment

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

Code review only

doc/lua_api.md Outdated Show resolved Hide resolved
doc/lua_api.md Show resolved Hide resolved
doc/lua_api.md Outdated Show resolved Hide resolved
doc/lua_api.md Outdated Show resolved Hide resolved
src/client/localplayer.h Outdated Show resolved Hide resolved
src/server/serveractiveobject.cpp Outdated Show resolved Hide resolved
src/server/serveractiveobject.cpp Outdated Show resolved Hide resolved
src/server/serveractiveobject.cpp Outdated Show resolved Hide resolved
src/server/serveractiveobject.h Outdated Show resolved Hide resolved
src/serverenvironment.cpp Outdated Show resolved Hide resolved
@appgurueu appgurueu force-pushed the entity-observers branch 6 times, most recently from e9ddf57 to f2e1aa8 Compare April 28, 2024 20:08
Co-authored-by: sfan5 <sfan5@live.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature ✨ PRs that add or enhance a feature Roadmap The change matches an item on the current roadmap. @ Script API @ Server / Client / Env. Supported by core dev Not on the roadmap, yet some core dev decided to take care of this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow Changing Entity Visibility On A Per-Player Basis
10 participants