A lexicon for game activity sharing

In Discord’s linux desktop app, I see the game they’re playing on the sidebar, and can get the full card by clicking on their profile.

It would not surprise me to learn that it’s updated elsewhere and they’ve just neglected linux.

They easily could have some safeguard in place to prevent it changing too frequently.

2 Likes

Huh. Guess I was wrong, then. My bad.

In any case, I think it could be neat to show multiple things. No need for parity imo

1 Like

I think we’re conflating things here. I’m down with making it easy to adopt, but we need to be realistic about adoption. Bluesky hasn’t shown any direct interest in this, while Colibri (@lou.gg) is not just open to integrating, it’s the impetus to this conversation. For now, I’m more inclined to support those that are engaging with the conversation rather than the theoretical “others.”

I have pinged the Roomy team as well, as we had a brief conversation about this months ago and they expressed interest.

Yeah, I believe they did support stacks not too long ago. We may also be conflating a stack of different activity types, e.g. game + spotify. However, I just verified that Discord currently only shows 1 activity at a time. I opened Aseprite via Steam and daw the Discord activity update. Then I opened Big Walk and the Discord activity updated again, replacing Aseprite with Big Walk.

Either way, I agree that supporting multiple activities at a time would be awesome. It’s not uncommon for me to play something like Elite: Dangerous, then open something like Balatro to play during long, mostly autonomous flights.

2 Likes

Yeah, I’m strongly in favor of each activity having its own rkey. I think this should be a valid MLF for the record, though the playground is throwing a minor tantrum over the use statemets:

use games.gamesgamesgamesgames.platform;
use app.bsky.embed.external;

/// A live "now playing a game" status, one per account.
/// Consider this record abandoned if staleAt >> now.
/// Be mindful of players' privacy preferences. Do not create this record
/// without consent from the owner to publish their live activity, and
/// consider what 'covert play' looks like in your app.
/// Record key should be static for your game - likely a games.gamesgamesgamesgames.game record key.
@key("any")
record status {
    /// The game being played (link to a games.gamesgamesgamesgames.game record)
    game!: AtUri,

    /// The storefront/platform being used
    platform: platform, // games.gamesgamesgamesgames.platform open union

    /// The kind of session this is (eg. "Daily Challenge", "4-player game")
    state: string constrained {
        maxGraphemes: 128,
        maxLength: 1280
    },

    /// The task being handled right now in the game
    /// eg. "Level 5-2", "Ranked - Diamond II", "Turn 14 of 20"
    details: string constrained {
        maxGraphemes: 128,
        maxLength: 1280
    },

    /// Details only relevant if you're playing.
    /// It's presence implies you're playing, not watching.
    playing: {
        /// An ID for this gaming session which will be the same for all players playing together
        id: string constrained { maxLength: 128 },
        
        /// Who's playing in this session (only for 'playing' activity)
        party: {
            /// Count of players currently in the party
            current!: integer constrained { minimum: 1 },
            /// Who they are, including yourself, may be less than current count
            /// eg. if some players don't have atproto accounts.
            /// Be mindful of players' privacy preferences.
            /// Ignore DIDs of players who don't also have
            /// an equivalent record (use `id` to match)
            dids: Did[] constrained { minLength: 1 },
            /// Capacity, if there's a hard limit
            max: integer constrained { minimum: 1 }
        },

        // Space for lobby details & other playing-specific metadata

        /// Timestamp components to display in the activity - for single matches or other events.
        timestamps: {
            /// The time a specific event started, counting upwards.
            startedAt: Datetime,
            /// The time a specific event will end, counting downwards.
            endsAt: Datetime
        }
    },

    /// A card representing the game/session
    embed: embed, // app.bsky.embed.external

    /// When this session began
    createdAt!: Datetime,

    /// When this record should be considered abandoned.
    /// Active sessions should update this regularly (eg. every 5 mins)
    /// to a few minutes in the future.
    staleAt!: Datetime
}

If there’s any more additions to data we should add, let me know! also @rpg.actor do you think it would be possible to host this schema at games.atmosphere.status or games.atprotocol.status? If not, I can also look into grabbing another authority domain for GWG schema.

2 Likes

Yeh, for the record we would readily adopt this in Roomy, and generally trust the direction we see you, @lou.gg and @mfzx.net are taking it in.

Personally I’ve never used this kind of feature so I don’t have an informed opinion on the particulars here.

Skimming through this very active (love to see it!) conversation, I’m just wondering whether there’s any tangible difference between game activity sharing and live(-app) activity sharing, since a common use of discord’s rich presence also seems to be “listening to this song [on this platform]” or “drawing in this app”.

2 Likes

The main things we’ve discussed are scope and metadata - we’re designing from the perspective of the GWG so we’re considering keeping it in the scope of games, and generally music and watch-togethers and the like need much less information for rich presence than games do. We have enough fields in the activity lexicon that extending it to other features in the future is possible (and out-of-atmosphere people have been doing this for years with discord), but we’re keeping games first and foremost due to where it’s being discussed.

3 Likes

Probably worth handling this part of the discussion in the namespace thread.

2 Likes

@lemmaeof.gay - Yeah, happy to offer games.atmosphere.status if that’s a desirable name for this one. Agree with its simplicity, and prefer it over atprotocol.games as it fits better with the rest of the Atmosphere Account concepts.

As @trezy.codes mentions, there’s probably a larger conversation to have about further use or standardizing more lexicons under atmosphere.games as you’ve suggested, but being a new lexicon and with nothing else to convert, this is easy enough to implement from our end.

We’ve set up Tangled: atmosphere.games/lexicons as a public source for the existing games.atmosphere.define schema for interoperability declarations.

If folks decide they want to use atmosphere.games for the .status lexicon, they’re welcome to open a pull request and contribute. We can review everything openly as a workgroup, and try it out on some rpg.actor experiences whenever it’s ready.

2 Likes

Understandable, that’s a fair approach. That said, the feature’s utility for Colibri and others is directly related to how many games implement it. While I wish this wasn’t the case, Bluesky remains the biggest userbase in the atmosphere by a mile. The size of the potential audience is key to getting game developers to adopt this. I’d probably be ideologically motivated enough to adopt this even if Bluesky doesn’t, but I don’t imagine many others would. I would love to see enormous success for Colibri that changes the calculus, but for now, this is obviously worth implementing if Bluesky adopts it and a min-priority backlog item otherwise.

My conclusion is that I prefer a dead-simple lexicon that’s trivial to adopt for Bluesky and future successful apps. I’m not convinced that supporting multiple concurrent games is worth the added complexity. It’s possible that small bit of added complexity is immaterial and won’t impact adoption, but I suspect otherwise.

2 Likes

Hi folks! Apologies for being away from the conversation yesterday! (I’m also loving the very active discussion @erlend.sh :grinning_face_with_smiling_eyes:)

@trezy.codes you make great points on swapping self out for a per-game rkey (and accepting that “bad” apps might abuse that).

I think a smart move here (someone may have mentioned it already, I lost track :sweat_smile:) would be to use the “sidecar” pattern, and reuse the g.gggg.game rkey for the games.atmosphere.status (love this!) record, so answering “is {person} playing {game} now?” is a trivial lookup.

The things that change I can spot:

  • We lose the ability to order records that a tid normally gives you, but given we’re expecting these records to be deleted when not active, I can’t see there being many to iterate through, even for folks playing many games at once!
  • We lose the ability for someone to play the same game twice (and have eg. the party details be different).
  • Apps spanning multiple games need to hold some state (what the last game they sync’d was), so they can remove it when you switch games.

I think these are all totally fine :slight_smile:

@erlend.sh you asked about non-game presence — given there’s an actively maintained index of games already on the atmostphere (games.gamesgamesgamesgames.game), and there’s game-specific metadata we’d like to include, I’d also think it’s best this initiative remains game specific.

But I can see it being useful more broadly, and I think an app-specific lexicon could use a similar/compatible structure to allow adopters to pull both lexicons and parse them identically in all but game-specific contexts.

@lemmaeof.gay I like your additions to the lexicon! timestamps and the `@key("any") right?

You reference “counting upwards” and “counting downwards” in the descriptions, I’m not quite following what they reference (I’m likely being dumb here, but I don’t follow how a timestamp can count?)

I’m also wondering if this would benefit from attaching those timestamps to something already in the record — “The time a specific event started/will end” — what event? Something referenced in the details? The state? Something separate? (if it’s separate then should we have instead an event key, with a startedAt, endedAt and description?) — I’m not familiar with how this could be used, so please tell me if I’m misunderstanding what this would be used for!

@rpg.actor thanks for the offer of games.atmoshpere.status! I’ll be happy to open a PR for us all to review — I think it’s just the question above on what the timestamps/event should be before we have something ready to ship!


Oh… and I have a demo (oh so very alpha) app up for you all to play with the records in your own dev servers :slight_smile:

sync.atplay.games will let you sync your Steam account’s now playing (powered by keytrace.dev) and your Discord account’s now playing (Keytrace for the link and you need to be in a specific Discord server for me to pull your data). It should give us all a good feel for how these records can look — syncing with Discord gives you richer data (as Steam doesn’t expose lots we’d otherwise need).

2 Likes

Yes, those are my additions! “counting up” and “counting down” are display instructions for relative time - startedAt would be displayed as time since the timestamp, and endsAt would be displayed as time until the timestamp.

2 Likes

Thanks! Do you think there’s value in allowing a description of the event? (If not, I wonder what’s different, for us, between the createdAt and timestamps.startedAt timestamp?)

1 Like

This is generally what the existing details field is for - it may be best to move timestamps out of playing and just have it be a top-level field instead.

2 Likes

Just a heads up that we should resolve the namespace conversation before we publish anything “official.” We can create lexicons under whatever namespace for testing, but I don’t think the WG can “bless” any lexicon until we resolve that.

2 Likes

Thanks Trezy, I wasn’t aware of that; I’ll keep any PR I make “draft” for now to avoid giving the wrong impression.

2 Likes

Sounds good; how do you feel about startsAt, staleAt and predictedEndsAt, all next to each other on the root?

1 Like

Yeah, I don’t mean to slow progress! I just want to figure out what namespace the WG will bless so we can be consistent. If we have 2 separate versions we want to try out, we could throw one in games.atmosphere and another in G5 or whatever, then converge on a final version in the blessed namespace at some point in the future.

We can just build things (and make them better later with the understanding that they’re not blessed until we sort out some other shit :rofl:).

2 Likes

What’s the difference between staleAt and predictedEndsAt? Feels like we’d only need one of those, but maybe I skipped over some part of the conversation. :thinking:

1 Like

staleAt is under-the-hood lexicon state, endsAt/predictedEndsAt is rich presence data that is actively displayed to on the client

2 Likes

I think that startedAt and endsAt should still be under a timestamps object to mark themselves as distinct from staleAt and the like, but they should be outside of playing since that one is mainly for multiplayer lobby info

1 Like