Mudlet icon indicating copy to clipboard operation
Mudlet copied to clipboard

Add: GMCP sharing namespace

Open mpconley opened this issue 1 year ago • 5 comments

Brief overview of PR changes/additions

Create a GMCP dialog between the client, game server, and user, that streamlines the process of identifying, requesting, consenting, delivering, and updating access to 17 client variables. Specification and implementation: GMCP Client Variables

This PR also moves a lot of NEW-ENVIRON code from ctelnet.cpp to ClientVariables.cpp.

Motivation for adding to Mudlet

Today, client variables may be transferred via the NEW-ENVIRON protocol referenced in PR #7058 as documented here.

Four variables were previously identified that add value to the player experience where player consent needs gathered prior to transfer (as suggested by the community):

  • LANGUAGE - Adjust game and user interface content to match the language set in the client.
  • SCREEN_READER - Adjust game and user interface content with accessible features where identified in the client.
  • SYSTEMTYPE - Adjust user interface elements based upon the operating system detected.
  • USER - Incorporate the account:character name from the client to the game server.
Approach
Create a GMCP specification for sharing client variables
  • Overlay with GMCP a way to prompt user action for consent.
  • Enable NEW_ENVIRON to receive more client variables that require consent.
  • Enable consumers who only use GMCP to access the client variables.
Add support to update certain client variables
  • CHARSET - Adjust the encoding as advised by the game.
Improve code readability
  • For Mudlet, this will box up interaction with client variables in one place and clean up the ctelnet.cpp class.
Task

Create a specification that reserves a GMCP namespace Client.Variables that facilitates the process of gathering variables from the client and incorporates user action where consent is required to gain access.

  1. Client publishes to the server that it supports the Client.Variables namespace.
  2. Server requests an enumeration of client variables from the client.
  3. Client responds with the list of client variables that are available, given consent or not, and updatable.
  4. Server requests client variables desired.
  5. Client responds with the available client variables and prompts the user for consent to share (where applicable).
graph TD
  A[Client Advertises Support] -->|Core.Supports.Set| B[Server Acknowledges Support]
  B -->|Client.Variables.Default| C[Client Sends Available Variables List]
  C -->|Client.Variables.List| D[Server Requests Specific Variables]
  D -->|Client.Variables.Request| E[Client Sends Updates for Requested Variables]
  E -->|Client.Variables.Update| F[Process Completes]

subgraph "Client"
    A
    C
    E
end

subgraph "Server"
    B
    D
    F
end
Request Consent

For client variables that were requested by the server but not available yet:

  1. Client prompts user in main window to share variables that are not blocked
  2. The --> Control sharing ... <-- message opens up the Data tab in Settings: Screenshot 2024-08-24 at 11 15 49 PM

These actions may happen ad-hoc thereafter:

  • User does nothing, blocks future consent requests for a variable, or provides consent. Screenshot 2024-08-25 at 11 14 08 AM

  • Client publishes to the server a changed client variable where consent was established.

graph TD
  A[Client Sends Updates for Variable Change] -->|Client.Variables.Update| B[Process Completes]
subgraph "Client"
    A
end

subgraph "Server"
    B
end
  • Server may request to update select client variables like BOLD_IS_BRIGHT and CHARSET.
graph TD
  A[Server Sets Updatable Client Variables] -->|Client.Variables.Set| B[Client Sends Updates for Set Variables]
  B -->|Client.Variables.Update| C[Process Completes]
subgraph "Server"
    A
    C
end

subgraph "Client"
    B
end
  • Server may request the available client variable(s) via another protocol (i.e., NEW-ENVIRON) if it desires.
Example Flow
// [1] Client
Core.Supports.Set { Client.Variables 1, ... }

// [2] Server acknowledges
Client.Variables.Default {}

// [3] Client (less variables if MNES mode or variables blocked by client settings)
Client.Variables.List [
    {"name": "256_COLORS", "available": true, "updatable": false},
    {"name": "ANSI", "available": true, "updatable": false},
    {"name": "BOLD_IS_BRIGHT", "available": true, "updatable": true},
    {"name": "CHARSET", "available": true, "updatable": true},
    {"name": "CLIENT_NAME", "available": true, "updatable": false},
    {"name": "CLIENT_VERSION", "available": true, "updatable": false},
    {"name": "LANGUAGE", "available": false, "updatable": false},
    {"name": "MTTS", "available": true, "updatable": false},
    {"name": "OSC_COLOR_PALETTE", "available": true, "updatable": false},
    {"name": "SCREEN_READER", "available": false, "updatable": false},
    {"name": "SYSTEMTYPE", "available": false, "updatable": false},
    {"name": "TERMINAL_TYPE", "available": true, "updatable": false},
    {"name": "TLS", "available": true, "updatable": false},
    {"name": "TRUECOLOR", "available": true, "updatable": false},
    {"name": "USER", "available": false, "updatable": false},
    {"name": "UTF-8", "available": true, "updatable": false},
    {"name": "VT100", "available": true, "updatable": false},
    {"name": "WORD_WRAP", "available": true, "updatable": false}
]

// [4] Server
Client.Variables.Request [
    {"name": "BOLD_IS_BRIGHT"},
    {"name": "CHARSET"},
    {"name": "LANGUAGE", "purpose": "Match client language to optimize game and UI content"},
    {"name": "SCREEN_READER", "purpose": "Enable accessibility features for game and UI"},
    {"name": "SYSTEMTYPE", "purpose": "Adjust game and UI elements based on operating system"},
    {"name": "USER", "purpose": "Identify client account and character to the server"}
]

// [5] Client ("requested" is false when no message is or needs sent to user or if blocked by client settings)
Client.Variables.Update [
    {"name": "BOLD_IS_BRIGHT", "available": true, "updatable": true, "requested": false, "source": "request", "timestamp": 1721495879, "value": 2},
    {"name": "CHARSET", "available": true, "updatable": true, "requested": false, "source": "request", "timestamp": 1721495879, "value": "UTF-8"},
    {"name": "LANGUAGE", "available": false, "updatable": false, "requested": true, "source": "request", "timestamp": 1721495879},
    {"name": "SCREEN_READER", "available": false, "updatable": false, "requested": true, "source": "request", "timestamp": 1721495879},
    {"name": "SYSTEMTYPE", "available": false, "updatable": false, "requested": true, "source": "request", "timestamp": 1721495879},
    {"name": "USER", "available": false, "updatable": false, "requested": true, "source": "request", "timestamp": 1721495879}
]

Client prompts user in main window to share variables that are not blocked, and the --> Control sharing ... <-- message opens up the Data tab in Settings Screenshot 2024-08-25 at 11 04 33 AM

User selects to share variable or block future requests to share variable Screenshot 2024-08-25 at 11 14 08 AM

// Client (will share "LANGUAGE", now, on the next request and for future connections)
Client.Variables.Update [
    {"name": "LANGUAGE", "available": true,  "updatable": false, "value": "en_US", "source": "client", "timestamp": 1721499991}
]

// Server may request "LANGUAGE" variable with NEW-ENVIRON (it may have taken it from GMCP already).

// Server could adjust value(s)
Client.Variables.Set [
    {"name": "BOLD_IS_BRIGHT", "value": 1}
]

// Client responds with updated value
Client.Variables.Update [
    {"name": "BOLD_IS_BRIGHT", "available": true, "updatable": true, "success": true, "value": 1, "source": "server", "timestamp": 1721523333}
]

Upon the next connection, the variables previously shared or blocked will not be requested: Screenshot 2024-08-25 at 11 16 53 AM

Other info (issues closed, discussion etc)

Test with StickMUD, listed on the default games in Mudlet.

  1. Create a character.
  2. Use the Data tab to share Screen Reader Use.
  3. If you are not already advertising screen reader use from the Accessibility tab, you'll see an [INFO] message guiding you to perform step 4.
  4. Use the Accessibility tab to set advertise screen reader use to checked, which will trigger step 5.
  5. You should see a StickMUD message that states Screen reader in use is identified as yes. in the Main Window of Mudlet.
  6. Use the Accessibility tab to set advertise screen reader use to unchecked, which will trigger step 7.
  7. You should see a StickMUD message that states Screen reader in use is identified as no. in the Main Window of Mudlet.
  8. Reconnect to StickMUD, and you should not see Screen Reader Use requested since it is still Shared on the Data tab in Mudlet and StickMUD is updated that Mudlet is not advertising screen reader use.

mpconley avatar Mar 24 '24 02:03 mpconley

Warnings
:warning: PR makes changes to 12 source files. Double check the scope hasn't gotten out of hand

Generated by :no_entry_sign: dangerJS against 152e0d22ec59242b9c2d01208b7925bde8f59a55

github-actions[bot] avatar Mar 24 '24 02:03 github-actions[bot]

/refresh links

mpconley avatar Aug 03 '24 12:08 mpconley

/create links

vadi2 avatar Aug 03 '24 13:08 vadi2

Hey there! Thanks for helping Mudlet improve. :star2:

Test versions

You can directly test the changes here:

  • linux: https://make.mudlet.org/snapshots/7c028f/Mudlet-4.18.5-testing-pr7193-08cecf4c-linux-x64.AppImage.tar
  • osx: https://make.mudlet.org/snapshots/32e6c0/Mudlet-4.18.5-testing-pr7193-08cecf4c.dmg
  • windows 64 bit: https://make.mudlet.org/snapshots/50295a/Mudlet-4.18.5-testing-pr7193-08cecf4c-windows-64.zip
  • windows 32 bit: https://make.mudlet.org/snapshots/4943b1/Mudlet-4.18.5-testing-pr7193-08cecf4c-windows-32.zip

No need to install anything - just unzip and run. Let us know if it works well, and if it doesn't, please give details.

Work-in-progress: Adding "type" to Client.Variables.List. Added to the standard.

mpconley avatar Aug 25 '24 17:08 mpconley

Work-in-progress: Adding "type" to Client.Variables.List.

mpconley avatar Aug 31 '24 18:08 mpconley

Complete: Adding "type" to Client.Variables.List.

mpconley avatar Sep 01 '24 17:09 mpconley

Planned changes complete. Lots of testing performed. Requesting to merge and get this into PTBs.

This is low risk. The noticeable in-line change for existing functionality is that NEW-ENVIRON protocol will begin sending an EMPTY VAL (no value sent) for LANGUAGE, SCREEN_READER, SYSTEMTYPE, and USER, until those are marked as shared. The EMPTY VAL is an indicator the client has these and action is required to unlock access (Shared on the Data tab). Alternatively, that is also an indicator within a session that a user no longer wishes to consent to share those variables (Data tab: Do not share or Block Requests).

mpconley avatar Sep 02 '24 18:09 mpconley

[...] The noticeable in-line change for existing functionality is that NEW-ENVIRON protocol will begin sending an EMPTY VAL (no value sent) for LANGUAGE, SCREEN_READER, SYSTEMTYPE, and USER, until those are marked as shared.

By the looks of it, a server not knowing about this custom (empty not meaning "set to the empty string", here, but rather empty meaning "user hasn't consented to this") would "just" DWIM and proceed as if the value was set, but to an empty string.

So if I'm a server and I receive an IAC SB NEW-ENVIRON IS VAR LANGUAGE VAL IAC SE I may well proceed thinking "the client has set somehing like LANGUAGE="".

If the server is instead cognizant of this "empty might be not shared by the user"... what should it do? Request it via GMCP, as this PR effects?

mfontani avatar Sep 02 '24 20:09 mfontani

Tested the Linux and 64bit windows versions with the included instructions above and everything worked perfectly! (Also go play StickMUD its Great!!!)

kmart9001 avatar Sep 02 '24 20:09 kmart9001

If the server is instead cognizant of this "empty might be not shared by the user"... what should it do? Request it via GMCP, as this PR effects?

I am requesting it via GMCP, because I am writing this PR, but I am also doing everything I can for NEW-ENVIRON, since I wrote that PR too =)

  • Let's say that I love NEW-ENVIRON. Yes, I could use the GMCP Client.Variables workflow to induce the end-user to toggle that Data menu item. The framework is there.
  • Or, Let's say that I hate GMCP. Knowing this is a Mudlet client from another NEW-ENVIRON variable, I could use my game to ping the end-user instructions to find the tab and consent, GMCP-free.

Up until yesterday, I did not want to share this initially with NEW-ENVIRON until it went through the GMCP dance, but I thought about someone wanting to remove consent after they shared. That's easy with GMCP, as "available": false from Client.Variables.Update is a trigger, but in considering NEW-ENVIRON got access as well, it seemed best to give NEW-ENVIRON a way to signal the "undo" button for the game, and EMPTY VAL is a good path to signal it. Given that NEW-ENVIRON is all of 1 month in the MUD market, and MNES does not do anything with EMPTY VAL, this was the perfect time to use this approach. I will add guidance to the Mudlet page on NEW-ENVIRON once I get the signal this PR will be going into Mudlet and when.

Side note: On StickMUD, I store NEW-ENVIRON values as string and this EMPTY VAL is a 0 (not a string of "0").

mpconley avatar Sep 02 '24 21:09 mpconley

I've taken one look at this but ought/want to take another ...

SlySven avatar Sep 04 '24 14:09 SlySven

Will re-consider for the future

mpconley avatar Sep 07 '24 19:09 mpconley