Help figuring the RES internals

Mostly a thread for @Accipiter, I guess, someone else might still find it helpful.

Got a couple questions after staring at the code for too long.

  1. Does making a request not guarantee that there will be a response?

The C# code seems to drop the ResRpc on the floor if WS connection is lost. JS code doesn’t seem to clean up this.requests at any point other than receiving a response. What exactly the client does if there is a request in flight and the connection dropped? To me it looks like the callback will never be called.

  1. How does a model or collection transition into an error and back?

When parsing the models/collections/errors there’s a strict type checking so in the JS code if some model errored it’s stored as an error in the cache. How does it unerror given the server side doesn’t seem to have any knowledge of whether the client has the error cached and the client won’t accept the legit model/collection as long as the error is in the cache?

Is that error mechanism used at all? I can’t see any reference to it in the C# code.

1 Like

Excellent questions, and impressive analysis of the code :smiley:

Question 1

All pending requests should instantly complete with a system.disconnect error code if the WebSocket connection is disconnected. Admittedly, neither the C# library, nor the Javascript does this! I need to fix that! :sweat_smile: Thanks for pointing it out!
But yes, a response should be guaranteed, usually within the default timeout duration of 3 seconds. However, a response may exceed this timeout duration by having the service extend it, or if the response contains nested data/resources that requires more time to fetch.

Question 2

The protocol currently does not support transitioning of resources into different types, including transitioning an error to a model/collection, or the other way around.
This is usually a non-issue, but the most common case when it can cause trouble is during service restarts. The scenario could looke like this:

  1. Service A shuts down for restart
  2. Client fetches collection from service B. Collection contains references to resources on service A.
  3. Resgate fetches collection from B, but when following references to service A, it gets no response and generates notFound errors.
  4. Resgate returns collection to client, including notFound errors.
  5. Service A is restarted.

In the above steps, the client would end up with errored resources, and would need to reload in order to fetch them. Reload is possible since Resgate, unlike the client, does not cache errors.

As said, this has never been much of an issue; if it would become one, it could be solved by updating Resgate and the RES protocol with one additional event. But this would also add to client complexity.

Or did you see some other scenario where transitioning from errors might be needed?

1 Like

By reload there-- what exactly do you mean? There’s no specific mechanism I found to purge an object from cache so any call to server that returns a non-errored response won’t update it.

It’s a bit of a theoretical issue for now, I haven’t stumbled on any errored resources yet and I couldn’t find any specific handling of those in the mucklet’s source code.

1 Like

And 3. Is there any protocol-level ping or anything that’d work like one? (I want to make sure there’s some keepalive traffic going over the WS).

1 Like

never mind #3, I just found $character.ping call :slight_smile:

1 Like

The docs example for Change event object seems wrong as it’s missing the values within data.

1 Like

Oh!! You are perfectly right. I will update the docs!
Thanks for pointing it out! :slight_smile:

The character ping is no keepalive for the WebSocket, but for keeping characters awake.
However, the RES protocol relies on the underlying protocols (WebSocket/TCP) to keep the connection alive.

1 Like

Yeah no, can’t quite trust that. My reasoning is that TCP is at most a keepalive to the LB and WS’s ping is at most reaching the gateway. I guess keeping the characters awake is a reasonable side-effect for what I want to have (i.e. I want to hear from that specific backend).

1 Like

Actually the ping doesn’t seem to prevent idling. What’s the best thing to counter that? I guess I can make the bot go “hmm” every once in a while but other than that?

1 Like

The way I did it with my bot was sending a look me after every ping.

1 Like

sounds like smoke and mirrors solution!

1 Like

This works perfectly fine and has no overhead to speak of.
But I guess we should have something to set active and idle state as well.
Maybe just a single setActiveState command?

1 Like

I’d help me a lot if you could provide me with a schema (fields, their types and optionality) for the out event, it looks like I’m missing a couple fields here and there and I’d prefer to deserialize it proper instead of guesstimating.

1 Like

Sure!
I will include all event types emitted to characters. I have excluded player events (such as broadcast) as they could be defined separately.

If the type is written within [brackets], it may be omitted. An omitted boolean will default to false.

General definitions

Event base

Properties used by all events.

Property Type Desc
id string Unique event ID
type string Event type
time number Unix timestamp in milliseconds
sig string Digital signature

Character object

Properties for character objects nested in events.

Property Type Desc
id string Character ID
name string Character name
surname string Character surname

Room object

Properties for room objects nested in events.

Property Type Desc
id string Room ID
name string Room name

Events

Message events

Event types: info

Additional property Type Desc
msg string Message text

Character message events

Events types: say, pose, wakeup, sleep, leave, arrive, describe, action

Additional property Type Desc
char character object Character sending the message
puppeteer [character object] Puppeteer controlling character
msg string Message text

Character poseable message events

Events types: ooc

Additional property Type Desc
char character object Character sending the message
puppeteer [character object] Puppeteer controlling character
msg string Message text
pose [boolean] Flag if message should be formatted as a pose

Targeted character message events

Events types: whisper, message, warn, mail, address, controlRequest

Additional property Type Desc
char character object Character sending the message
puppeteer [character object] Puppeteer controlling character
target character object Targeted character
msg string Message text
pose [boolean] Flag if message should be formatted as a pose
ooc [boolean] Flag if message should be formatted as OOC

Target room message events

Events types: travel

Additional property Type Desc
char character object Character sending the message
puppeteer [character object] Puppeteer controlling character
targetRoom room object Target room
msg string Message text

Targeted char events

Events types: summon, join, leadRequest, followRequest, follow, stopFollow, stopLead

Additional property Type Desc
char character object Character sending the message
puppeteer [character object] Puppeteer controlling character
target character object Targeted character

I think I’ve got them all. :sweat_smile:

1 Like

There’s quite more than I discovered, thanks! Could you add me a puppet on the test server (or convert my second char into one) for experiments?

1 Like

The test server is a replicate of Wolfery, and it is restored from Wolfery’s backups every 24 hours; anything not on Wolfery will be wiped, including any puppet I create. Instead, you can go to Miranda (Miranda’s Café), and type:

register puppet Miranda

Once I’ve accepted that request, you will have a puppet to control.
To try out the controlRequest, you could register Miranda twice using two different characters of yours. Then you can try take control between them.

Once done registering puppets, you have to wait for the backup restore (00:15 UTC time… I think. Or was it CET?).

1 Like

2 posts were split to a new topic: [Bug] Client tried to subscribe to deleted exit

Sometimes core.tooActive returns {seconds: xx} but other times there are no seconds in the data. What’s in there? Every time it failed on that side I had the traffic tracing off :frowning:

1 Like

If core.tooActive contains no seconds, it is <= 1 second.
The data property returned in error messages is meant only for text formatting and localization purpose, so it isn’t really intended to be used as a reliable source of info.

The flood detection feature determines if a player is allowed to issue a command right away, of if they will get the core.tooActive error.

Which commands are you interested in knowing the cost of?

1 Like

I don’t think I care about cost accounting client-side much, at this point, I’d rather try, fail, and wait for however long the server tells me.

The commands I do are limited to teleporting and room/exit management, though:

2 Likes