This week I fixed three 512 bugs related to visual contents, in two reports (all by the same person), and they were interesting enough that I thought this seemed like the placed to talk about them.
Bug #1: Changing client pixel offsets caused visual contents to get "stuck".
Ho boy was that one fun, and I'm hoping the fix doesn't come back to bite me in the butt. I don't think it will, but sometimes things happen in unexpected ways. The cause of this bug was code that I put in that I apparently shouldn't have, but what that means is maybe I had a good reason for that and I'll find out the hard way in a future bug report.
On the server, for each client BYOND keeps a list of objs the client knows about from the map, and a list of mobs. It also has a list of turfs that have visual contents the client knows about. (Also areas and some other things.) The way these lists work is that they have a flag saying if they've been sent, if they're in use, and flags for whether various properties have changed. The data structure in this list is MapObject, and it basically contains a copy of all the relevant info. (There's also a PublishedAtomList structure containing screen objects; this will be important later.)
In a typical map update, these lists get their in-use flag cleared on all MapObjects and important objects get re-added to the list, which basically means they're run through a quick comparison and if there've been changes, the flags are recorded and that's used to determine if the client even needs the update at all. If there are MapObjects with the in-use flag still off, the client needs to be told about no longer needing them.
Well, visual contents on turfs use their own MapObjects list. Whenever a player's client pixel offsets change, or they make a major change to the map, a number of turf-related structures get cleared so they can be sent fresh. Unfortunately, I was also clearing the list of turfs with known visual contents. This means that if any of those turfs had to have their visual contents info removed from the client later, that "forget me" message was not being sent.
Why did I do something so stupid as to clear that list? Well I must have had a reason, as I said, but darn if I can remember what it was. Whatever that code was in there for, either I put it in because it looked like it belonged with similar lists being cleared and didn't think through the consequences properly, or it was to fix a different issue which I have just re-broken. Time will tell! I'm hoping I was only stupid the one time.
Bug #2: Adding to client.screen and removing from map at the same time caused new visual contents to not appear.
This bug and #3 were in the same report. The user was adding a flashing exclamation point to a piece of equipment when clicked, moving it into usr.contents, and putting it in usr.client.screen. When clicked again, the opposite would happen: no visual contents, out of client.screen, back onto the map.
Adding to an object's visual contents and moving it to client.screen from the map in the same tick turned out to be a problem.
The server sends the screen update for PublishedAtomList first, and it was sending a separate message for the visual contents of these screen objects. Right after that, the MapObject update was sending a signal to the client that it no longer needed the same obj, and could it please clear out pixel offset info and visual contents. The timing of these two was a big issue.
I removed the client code clearing pixel offsets and visual contents when an obj is "deleted" from the client. This introduced two potential problems, one of which is a non-issue because of the fix for Bug #3.
Problem A: If an object is truly being removed from the map, what happens if its visual contents are reset to blank? Since that's the default for a MapObject it won't trigger any messages on a new MapObject. This is the one I'll talk about under Bug #3.
Problem B: What if a new object appears with the same ID due to recycling? Actually we have that covered, because MapObject includes a two-byte sequence number that all movables have, incremented whenever they're created. If the client sees a mismatch, it knows it's no longer dealing with the same obj and will reset everything.
Bug #3: Removing from client.screen and adding to map at the same time while clearing visual contents caused the visual contents to get stuck on.
This is of course the opposite of Bug #2 but it required a different solution. The server tells the client to forget about this screen object, which has visual contents. Then the MapObject update comes, but because this obj has now cleared its visual contents on the server, it knows having no visual contents this is the default behavior and doesn't say anything special.
The solution to this bug, and to problem A, was to simply make sure client.screen objects get added to the MapObject list every tick! The screen list has to be traversed anyway because HUD visual contents still need to be updated as needed, so this is really nothing.
While this does mean a tiny amount of extra work per map tick, I think it's so tiny it's not going to make any difference to server performance, even on HUD-heavy games.
So that was how my bug adventures went this week! Exciting stuff, and hopefully gives you an interesting insight into how BYOND handles updates.