#[WIP] LobbyCompatibility

1 messages · Page 1 of 1 (latest)

cyan flame
#

network compatibility for mods is not ideal at the moment! most lobbies will reject you with an unspecified error. CompatibilityChecker helps with this, but it doesn't solve the issue as-is

#

LethalAPI and LC_API both have some utilities for dealing with this, but they're far from ubiquitous right now.

in my opinion, the best solution is a single-purpose, fairly light network compatibility library. the benefits would be:

  • fairly easy to update upon game updates
  • likely won't break anything if you add it as a dependency
  • should be as easy as adding a single attribute to define how compatible your mod is
  • can be finished in a matter of days
#

I'm willing to work on this, i just want to make sure something is made that properly works for the entire community, and can be maintained long term

#

with the goals being:

  • an easy way to browse public modded lobbies, with an alert that tells you what mods to change if it fails
  • a centralized place to block server-side mods from doing challenge runs
  • a better way to block vanilla users from seeing lobbies that they won't be able to join
#

Attributes would probably look like this, a slightly modified version of what Jotunn uses:

    public enum CompatibilityLevel
    {
        /// <summary>
        ///     Mod is not checked at all, VersionStrictness does not apply.
        /// </summary>
        ClientSide = 0        
        /// <summary>
        ///     Mod is only required by the server. VersionStrictness does not apply.
        /// </summary>
        ServerSide = 1       
        /// <summary>
        ///     Mod must be loaded on server and client. Version checking depends on the VersionStrictness.
        /// </summary>
        EveryoneMustHaveMod = 2,
        /// <summary>
        ///     If mod is loaded on the client, it must be loaded on the server. Not every client needs to have the mod installed. Fairly niche and only applies if your mod works fine when a client doesn't have it. Version checking depends on the VersionStrictness.
        /// </summary>
        ServerMustHaveMod = 3
    }

    public enum VersionStrictness : int
    {
        /// <summary>
        ///     No version check is done
        /// </summary>
        None = 0,
        /// <summary>
        ///     Mod must have the same Major version
        /// </summary>
        Major = 1,
        /// <summary>
        ///     Mods must have the same Minor version
        /// </summary>
        Minor = 2,
        /// <summary>
        ///     Mods must have the same Patch version
        /// </summary>
        Patch = 3
    }

example usage would be [NetworkCompatibility(CompatibilityLevel.EveryoneMustHaveMod, VersionStrictness.Minor)]

#

the big questions I have are:

  • what should default network compatibility be? we could either have it be ClientSide or ServerSide. IMO ClientSide would be better, just to avoid EVERY client side mod having to label itself
  • do those attributes above cover every common usecase?
  • are people actually willing to adopt the attributes, or is it too late?
#

this could also be put into an existing library (CompatibilityChecker, LC_API, LethalAPI) but IMO starting fresh with a simple, easy-to-update mod would be the way to go
as far as I'm aware no mod has tried to implement proper plugin-defined lobby syncing like this yet

#

pinging semi-relevant people for comment @sharp ridge @lament perch @dense forum @humble elm @earnest pilot

humble elm
#

seems like how r2api handles it in risk of rain

lament perch
#

imo defaults should be
Compat: - ClientSide
Version: -Major

but this is definitely a necessity

cyan flame
#

Yeah, I've never modded ror but it seems like it used a similar system

#

Are mods server side by default in ror ooc? How's that handled
Is r2api just used everywhere so it doesn't really matter

lament perch
#

also there likely is a way to "enforce" this on all mods with the defaults

though whether that should be done or not is an important question

humble elm
#

it doesn't define serverside or clientside

humble elm
cyan flame
#

hmm, I still feel like having the difference between clientside and serverside could be useful somewhere but idk

#

mostly for the cheating in challenge runs bit, but I'm not sure the best way to handle it in general

#

ror just blocks any mods from prismatic trials right

humble elm
#

yeah

#

and quickplay

#

was by request of hopoo

#

lol

cyan flame
#

i do think being able to use clientside qol mods on challenges could be nice but that obviously comes with some problems

#

especially if mods are labeled clientside by default

lament perch
#

yeah, infinite sprint it client side

humble elm
#

the problem with having a serverside or clientside tag will always have those problems

#

if there is no defaults they can just not tag their mods

#

clientside default is bad tho

#

that'll for sure cause issues, because it is really hard to establish standards, i would assume most people won't add the attributes

cyan flame
#

idk, serverside default seems like the correct option

but EVERY client mod developed (likely thousands at this point) breaking lobby compatibility until they update seems like it's going to be a disaster

humble elm
#

most people will never add the attributes i can tell you that right now

#

lol

cyan flame
#

Yeah it's probably too late, I think even having the top 20 mods labeled would help massively though

humble elm
#

this modding community is too scattered to push any kinds of standards

cyan flame
#

idk, I think it could happen if:

  • we updated the wiki
  • ping announcements were made in both modding servers
  • some people made PRs for larger mods
humble elm
#

i think the best solution would be to just not worry about it and have default behaviour be noneedforsync

#

because that'll cause the least issues out of the box

#

sadly i don't think this'll be a solution for cheating or anything either way

#

because they could just not install this mod

#

:p

cyan flame
#

Oh yeah I'm not worried about stopping cheating, it's probably not possible, I just want to be able to find public lobbies lol

sharp ridge
humble elm
#

my point is, what you really should be doing is making it opt-in, and having it be as unintrusive as possible

lament perch
#

yeah

cyan flame
#

yeah, that's probably the best option

lament perch
#

#1194412635932856440 message

cyan flame
#

opt in with large mods that will break (biggerlobby, morecompany, lethallib) marked as needed syncing

humble elm
#

yeah

cyan flame
#

worst case it can just show your different mods on error

cyan flame
#

at least for now

#

@gleaming ice I think you were also talking about something similar earlier, do you have any opinions on what to do if a new system like this was being made

sharp ridge
cyan flame
#

Either those three, or the two values of no sync/sync

humble elm
#

the second option is what r2api does

#

and it makes sense because in reality, there is not a lot of situations where you need to know whether a mod is server or clientside

#

because either one is just no sync

sharp ridge
#

It's useful for other mods to know, I feel like?

humble elm
#

maybe, but i cannot actually think of a case where a mod needed to know it

sharp ridge
#

The system I was working on in LethalAPI was a simple attribute you add (akin to BepInEx attributes) to indicate whether a mod is required. The rest of the functionality surrounding it then added plugin info to the lobby (GUID + version). If any required mods are found, it was set to unjoinable / invisible for vanilla. Modded users of the API do a check with the lobby's plugin data and compare versions.

#

I felt like that was a relatively elegant solution.

#

I had some plans to add an on-hover list of required mods for lobbies

cyan flame
#

since whatever is decided on probably can't be changed

sharp ridge
#

I'll port over my stuff to a separate mod and toss it up as a repository?

cyan flame
#

sure, i'll probably have some input/changes once it's up

#

i need to make some mockups of some UI ideas i had

gleaming ice
#

at appropriate time just iterate through the chainloader loaded plugins list

#

the enums looks fine to me

sharp ridge
#

I'm not a fan of overcomplicating it tbh. The enums at the top seem a bit too much.

lament perch
#

easiest way to do so is the nosync/sync; default nosync, and have mods add an attribute to set it to sync

cyan flame
#

the fourth enum value is probably almost never going to be used tbh

lament perch
#

and even if it would, it really should be everyone must have/sync

gleaming ice
#

the last one seems a bit weird but the other 3 make sense, client side server side and both

sharp ridge
#

Doesn't it basically boil down to "This mod is required" and "This mod isn't required", in practice?

humble elm
#

yeah

lament perch
#

yeah

cyan flame
#

pretty much

sharp ridge
#

(Is that yeah @ me or HD)

cyan flame
#

you

humble elm
#

at you lol

sharp ridge
#

So, do we want to go with a simple "required" tag, then?

#

It's what I had for LethalAPI

#

But I don't mind cutting it out

#

And then have some "smart" logic to check versioning and the like

cyan flame
#

I think the slightly more detailed enums would probably do the trick, I could see the specific difference between Client side and Server side coming in handy
plus the ability to define version strictness

#

but bare minimum, the required tag would probably work

sharp ridge
#

Alright

#

Give me a minute to set some tea

humble elm
sharp ridge
#

Or, realistically, give me like 15 - 20

#

A mod that is flagged required should block a user that has it from joining a server that doesn't have it (which is how I did it in LethalAPI)

lament perch
#

which makes sense

sharp ridge
#

Not Required

  • User: Can join any server
  • Server: Anyone can join server

Required

  • User: Can join any server that also has it
  • Server: Anyone that has it can join
#

With the stipulation that versions also match

#

(E.g. major version matches)

humble elm
#

i think version strictness settings is something that would be nice

lament perch
#

def

sharp ridge
#

As in, perfect matching versions?

#

Ah

#

A setting

#

Yeah, that's definitely very doable

humble elm
#

major minor patch or any version

#

or something

cyan flame
#

yeah, that's what the version compatibility attribute was for

#

i'm not sure what should be default, probably minor?

sharp ridge
#

I can probably get it to work within the next hour or so. No UI stuff was written for the API yet though - if anyone wants to experiment with that.

cyan flame
#

major seems like a lot, 99% of mods are always going to be on major version 1

sharp ridge
#

Agreed

humble elm
cyan flame
#

forever in beta

humble elm
#

because i never rewrite my mod

lament perch
humble elm
#

lol

#

the only time i increase my major version is if i rewrite my code

sharp ridge
#

If they're using BepInEx, I'm pretty sure they're also automatically using System.Version, so no funky custom versions afaik

gleaming ice
#

definitly do minor version by default

humble elm
#

which also supports build numbers

#

0.10.4.2355 tf

#

it just parses the version string

gleaming ice
humble elm
#

yeah in reality i don't think it would be used much

cyan flame
#

maybe like 10 mods would use it, but it's still something to consider

humble elm
#

what actually happens when you run a client rpc if not everyone has a mod installed

#

this is not something i have tried

#

i would assume it wouldn't freak out

lament perch
#

depends what rpc

#

in-game one should work

humble elm
#

no like

#

a modded one

gleaming ice
#

it'll probably error

#

it's better if you handle the case

lament perch
#

custom one (in most cases) the object owning it wouldn't even be spawned in

gleaming ice
#

idk how mirror does it but worth checking if it doesnt keep resending it too

humble elm
#

oh actually the error would come way earlier

lament perch
humble elm
#

when the prefab was attempted to be spawned

cyan flame
#

if you try to join with desynced network prefabs it won't let you

humble elm
#

yeah, it wouldn't even let you join

#

so networkbehaviours couldn't even be used for messaging in the servermusthavemod type mods

#

lol

gleaming ice
#

that's like the only thing I'd ask the dev to put them in themselves, a customizable message box so that the server can send arbitrary text to client so that they know why they got disconnected

#

for vanilla clients

humble elm
#

definitely would be nice

cyan flame
#

there might be a way to do that already with lobby data, i'm not sure

humble elm
#

thing is

cyan flame
#

I think max's lobby thing for lethalAPI did that?

humble elm
#

to actually give an accurate message of missing mods or something, the server would have to receive a list of mods from the client, in which case the client would have to have the mod installed aswell in order to send that list

#

in which case you can just implement the custom message boxes yourself anyway

#

lol

sharp ridge
#

But it's simple enough to do

#

It'd be great if zeekerss added a simple "modded" field that he checks in vanilla tbh.

cyan flame
#

doesn't the new version have lobby tags?

sharp ridge
#

It has tags, but I generally don't use public lobbies, so I haven't used them yet

#

Not sure how they work?

cyan flame
#

definitely would be nice to have a modded field in vanilla

#

is there no other way to hide modded lobbies from vanilla as-is

sharp ridge
#

There is

#

iirc you can just set the joinable lobby data to false

#

Which is what I did with LethalAPI

#

And then had a custom modded_joinable field that I made users of the API check

#

If that was joinable, it then did the plugin data comparison

lament perch
#

honestly seems like all we need to do is tack on version strictness and it's mostly finish tbh

sharp ridge
#
Vanilla Joinable?
  - Yes -> Check if client has any required plugins
    - Yes -> Do not join
    - No -> Join
  - No -> Modded Joinable?
    - No -> Do not join
    - Yes -> Plugin requirements match?
      - Yes -> Join
      - No -> Do not join
#

That was the current flow I have

#

Just needs the version strictness added and the requirements check changed to take it into account, then.

#

How does this differ from CompatibilityChecker, though?

#

Not entirely sure what that mod does.

cyan flame
#

CompatibilityChecker makes a list of mods and shows you the differences when you error out on joining a lobby

sharp ridge
#

Ah, so it requires a perfect match?

cyan flame
#

technically no, it will let you join any lobby
it just displays the data if you error out

sharp ridge
#

Gotcha, okay

#

Fair, yeah, then I see the point in making this for sure

cyan flame
#

i assume this mod would have a similar error screen, i'm working on some UI concepts right now

sharp ridge
#

The error screen I gave was a list of mod mismatches iirc

#

Not sure if I ever got around to pushing that, though.

#

E.g. "Missing X mod" and "Y mod is out of date. Requires version A.B.C"

#

Can probably make it fancy tbh

#

Like a diff, but of required mods only

#
- Missing "MoreCompany" version 1.2.3
- Missing "LateCompany" version 2.3.4
! "MoonOfTheDay" version mismatch. Needs version 3.4.5
+ Required mod "UncertainCompany" was not found on the server
cyan flame
sharp ridge
#

I was thinking a hover box for the lobbies to show the mismatching mods would be nice.

cyan flame
#

for sure

sharp ridge
#

So you don't have to go through the join / error loop to find out what you're missing.

cyan flame
#

we might have to expand the error box, ideally you should be able to see:

  • the full list of mods they have installed
  • the mods YOU have that they don't
  • the mods THEY have that you don't
sharp ridge
#

Yeah, a diff

lament perch
#

could be nice to have a "filter compatible" to only see servers you can join too

high crown
humble elm
#

and a checkbox to show incompatible

#

thats how most games handle it

sharp ridge
#

I'd show something like this on error:

Client side    |    Server side    |    Reason
________________________________________________
+ Mod 1 (1.2.3)  |  Mod 1 (1.2.4)  |
- Mod 2 (1.2.3   |                 |  Server missing mod
-                |  Mod 3 (1.2.3)  |  Client missing mod
- Mod 4 (1.2.3)  |  Mod 4 (1.3.1)  |  Client mod outdated
+ Mod 5 (1.2.3)  |  Mod 5 (1.2.3)  |
#

(Low key proud of this 👌)

cyan flame
#

default filter compatible, maybe filter incompatible automatically if 0 exist
people who have too many mods should be able to see what they need to change to join a public lobby

dense forum
cyan flame
#

yeah, i remember you talking about it before

cyan flame
#

instead of using the attribute, for mods that need it

humble elm
#

i think it is good for it to be standalone from any api

humble elm
#

could always add a bepin dependency for it to things like lethallib force a lot of people to install it

#

lol

high crown
cyan flame
#

that looks good, i'd have to see how it looks when displayed ingame
ideally grandma should be able to parse it

sharp ridge
#

It's a simple enough system, and APIs might be incompatible with each other

#

Locking it down to one popular API would low key suck

dense forum
#

We were just going to do what jotunn does ¯_(ツ)_/¯

sharp ridge
cyan flame
#

i think this is probably going to be very similar to what jotunn does

#

the enums i proposed are basically identical

humble elm
dense forum
#

Yeah jotunn is also an api is what I was getting at.

lament perch
#

yeah it makes sense

sharp ridge
#

For better or for worse, Lethal Company's API landscape is fairly fractured. I wouldn't put extremely useful functionality like this in any one API at this point.

lament perch
#

yeah

humble elm
#

compatibility wise

cyan flame
#

tbh I would probably push to add it to LC-API but there's so much completely false information about it going around that people WILL violently resist depending on it

lament perch
#

just have major apis/libs have a dependency in thunderstore (or even bepinex dependency)

humble elm
sharp ridge
#

Sounds like a fair plan

humble elm
#

doesn't have to be a mod dependency, just a dependency in the manifest

sharp ridge
#

Aye

humble elm
#

so people who use modmanager will automatically install it

lament perch
#

(which is most)

dense forum
cyan flame
#

yeah i don't really understand where that came from

humble elm
lament perch
#

if anything it would be more company xd

humble elm
#

mainly

dense forum
#

because an old version of LC API did break the game lol

humble elm
sharp ridge
#

Early LC API was... interesting

dense forum
#

But the original dev doesn't even work on the api atm

humble elm
dense forum
#

I mean it's still the most downloaded mod, is it not?

humble elm
#

yeah

sharp ridge
#

Mostly due to BiggerLobbies, afaik

lament perch
humble elm
#

(i actually have no idea what mods use lcapi, sorry)

dense forum
lament perch
cyan flame
#

mostly biggerlobby/brutalcompany + forks

humble elm
#

in reality i have no idea what lc api does, since the only time i looked at it was back when the assetbundle issues were happening

sharp ridge
#

Like, a good 95% of its download count came from BiggerLobbies as far as I recall. No clue what uses it nowadays.

dense forum
cyan flame
#

how does it have twice the downloads of bepinex what the fuck

humble elm
sharp ridge
#

And the early days of that mod, and LC_API had a fair bit of drama / questionability. I can attest that that left a "Eh, probably won't touch it" impression on me at least. (No hard feelings, y'all are doing cool stuff nowadays)

humble elm
#

every update counts as a redownload

sharp ridge
#

I figure it's probably a similar sentiment with others tbh

humble elm
#

the more a mod is updated the more downloads it gets

#

:p

sharp ridge
#

That's... huh

lament perch
#

tend to avoid any dependencies tbh

humble elm
#

another reason for high download count is the fact that it is at the top of the list in the first place

#

everyone adds it to their modpacks for no reason

#

because people just mass add anything at the top lmao

dense forum
#

I'm just trying to provide a high level api that will prevent mods from breaking when the game updates lol

#

that is my only goal atm

lament perch
sharp ridge
#

Let's get back on topic, though.

#

What should I name the mod & repo? hmm

#

PluginChecker?

#

RequirementsChecker?

lament perch
#

Modded Lobbies?

humble elm
#

but yeah, i think a standalone mod which is added as a "dependency" by the main API mods will be a good solution. as long as it won't do anything that will easily break other mods

lament perch
sharp ridge
#

If major APIs add it as dependency, it'll get it out there

cyan flame
#

i can add it to biggerlobby

sharp ridge
#

Are you maintaining BiggerLobby? hmm

cyan flame
#

barely

sharp ridge
#

Huh, the more you know

humble elm
#

i forgot biggerlobby existed 😭

dense forum
#

This would require everyone in the server to download it, to work properly, right?

sharp ridge
#

If no required mods that use it are found, it won't do anything (to connectivity)

cyan flame
# cyan flame

lobby type 3 here would let it retain compatibility with older versions/people who don't have the compatibility mod
i think it won't be pushed to the top though

sharp ridge
#

ModdedLobbies, then?

cyan flame
#

i guess if it's making lobbies unjoinable to vanilla it will break if you're not hosting though

humble elm
#

the best name would be CompatibilityChecker but thats taken aStaffRuzikeLaugh

cyan flame
#

maybe remove the "unjoinable to vanilla" bit in private lobbies

sharp ridge
#

It'd only do that if a required mod is found

#

So if you're using it, and no required mods are installed, it won't block vanilla

cyan flame
#

i guess updated required mods would depend on the mod

sharp ridge
#

Yup

#

So I think that sorts itself out

dense forum
cyan flame
#

idk ModdedLobbies doesn't sound like a library to me

#

i don't have any great ideas though

sharp ridge
#

"Requirements"

humble elm
#

dev name time until someone comes up with something

sharp ridge
humble elm
#

lol

cyan flame
#

LethalCompatibility, LobbyCompatibility

sharp ridge
#

"NoMoreLobbyPain"

cyan flame
humble elm
humble elm
#

if you search lethal you do not find it lol

cyan flame
#

you really chose the most 2 generic words for that huh

lament perch
#

lol ye

sharp ridge
#

Alright, I need a dev name

humble elm
cyan flame
#

classic

sharp ridge
#

Ah yes

lament perch
#

CompatWombats

cyan flame
#

LobbyCompatibility seems fine for a dev name
maybe LobbyLibrary because it sounds goofy

humble elm
#

LobbyCompatibility is fine for me

#

can always come up with something better in future

sharp ridge
#

BetterCompatibility?

#

LobbyCompatibility is fine, aye.

cyan flame
#

BetterCompatibility doesn't sound "OH I URGENTLY NEED THIS" enough to me

#

sounds more like an improvement

sharp ridge
#

CriticalCompatibility

#

"YouNeedThisMod"

cyan flame
#

INSTALL_OR_DIE

earnest pilot
# cyan flame Attributes would probably look like this, a slightly modified version of what Jo...

The ServerMustHaveMod enum would have a pretty niche use case, but still something mods using NamedMessages could use.

The talk about the mod/lib potentially making non-modded players unable to find the lobbies is slightly problematic imo, a potential fix for this would be to check if a host only has client side mods/noneedforsync mods and setting the lobby to public instead of it being hidden, and only hide it when the host has a server-side/needsync mod.

For the weekly trials, the only ideas I have with them are pretty intrusive:
eg:

  • Allow server-sided/server and client must have mods but not client side ones (unless if the host allows it via a checkbox)
  • If a user has the mod, they won't be able to join weekly challenges unless if the server meets specific conditions such as: Host has the mod, and allows client-side mods

These ideas will overcomplicate how server listing works, and is still pretty flawed. Just my ideas though 😊

cyan flame
#

yeah i'm not really sure the best method for dealing with weekly trials

#

ideally people won't get mad and be like "oh, i'm not depending on this because I want to my users to be able to do weekly trials"

#

but realistically, you probably just shouldn't be able to do the weekly trials with mods
or at the least if you have any mods, the leaderboards should be completely separate

#

somebody could always ask zeekerss how they want things handled too

#

no way for us to stop cheaters, but we could at least prevent people accidentally doing it

cyan flame
sharp ridge
#

The talk about the mod/lib potentially making non-modded players unable to find the lobbies is slightly problematic imo, a potential fix for this would be to check if a host only has client side mods/noneedforsync mods and setting the lobby to public instead of it being hidden, and only hide it when the host has a server-side/needsync mod.

This was already the plan.

cyan flame
#

although I do want to keep vanilla and modded mostly separate, the ideal situation IMO would be a "modded lobby" toggle in vanilla that's off by default

sharp ridge
#

Yup

#

A "modded" field used by vanilla to check would be great

lament perch
#

was looking through game files to see how the leaderboard is added to; found out you can predict the seed used for each week

sharp ridge
#

Oh, yeah

#

It uses the same stuff I used for my MoonOfTheDay mod iirc

cyan flame
#

is there a way to add custom leaderboards that will never be rolled in vanilla ooc

sharp ridge
#

Likewise with the fixes to determinism I did

#

Maybe?

#

No clue how the Steam API works for that

#

What do we want as attribute name btw?

#

To summarise

cyan flame
#

just NetworkCompatibility would work fine if we're rolling clientside/serverside/everyone IMO

sharp ridge
#
  • Server/Clientside compatibility enum
  • Version compatibility enum
#

Thoughts on [LobbyCompatibility(...)]?

earnest pilot
#

lobby compatibility sounds nice

sharp ridge
#

Or do we want to use [NetworkCompatibility(...)]?

cyan flame
#

i think lobbycompatibility would work, but I don't think that should be the attribute name if that's the final library name

#

i do like it a bit better than NetworkCompatibility though

lament perch
#

🤔 looking through the challenge file stuff, currently only see it saving the file as LCChallengeFile, haven't found any method to add to some sort of leaderboard yet

earnest pilot
#

leaderboard could be a prefab

#

No idea how the leaderboard actually works. I haven't even seen it ingame yet 😂

high crown
#

could maybe just replace the leaderboard element entirely rather than hook into it?

sharp ridge
#

Thoughts on this?

#
/// <summary>
///     Specifies the compatibility level of the plugin.
/// </summary>
public enum CompatibilityLevel
{
    /// <summary>
    ///     Mod only impacts the client.
    ///     Mod is not checked at all, VersionStrictness does not apply.
    /// </summary>
    ClientOnly = 0,

    /// <summary>
    ///     Mod only impacts the server, and might implicitly impact the client without the client needing to have it installed
    ///     for it to work.
    ///     Mod is only required by the server. VersionStrictness does not apply.
    /// </summary>
    ServerOnly = 1,

    /// <summary>
    ///     Mod impacts both the client and the server, and adds functionality that requires the mod to be installed on both.
    ///     Mod must be loaded on server and client. Version checking depends on the VersionStrictness.
    /// </summary>
    ClientServer = 2,

    /// <summary>
    ///     If mod is loaded on the client, it must be loaded on the server. Not every client needs to have the mod installed.
    ///     Fairly niche and only applies if your mod works fine when a client doesn't have it. Version checking depends on the
    ///     VersionStrictness.
    /// </summary>
    ServerIfClient = 3
}
#

Slightly more explicit naming

earnest pilot
#

Naming the last enum is a challenge

#

its really confusing

sharp ridge
#

It basically boils down to the server needing the mod if the client has it

#

But not the client if the server has it

#

I feel, for our purposes, it's clear enough?

earnest pilot
#

yup got that part, the name of it is still confusing when you look at it for the first time without reading the summary

high crown
sharp ridge
#

Hm

cyan flame
#

hmm, ServerOnly makes me think "this will break things if used on the client"
maybe OnlyRequiredOnServer ?
I like EveryoneMustHaveMod better than ClientServer because it's more immediately obvious what it means

sharp ridge
#

It has a summary

#

I hope modders read those when working with something like a compatibility flag

#

Maybe I should rename the enum to "RequiredLevel"?

cyan flame
sharp ridge
#

Is that server-only required?

#

Or required if the client has it?

#

That feels more confusing to me imo

earnest pilot
#

server must have mod is like this:

  • Server can have the mod but it wont be intrusive to those that dont have it, but its functionality wont work for them
#

and those that have the mod will be able to use its specific functionality

high crown
earnest pilot
#

its like a gatekept function 😂

high crown
#

trying to figure out where that would fit in here

sharp ridge
earnest pilot
#

u no have? no work for u

#

but still, you can play game

#

something like that

sharp ridge
#

Perhaps another enum that implies the mod has a method?

lament perch
#

okay i think I found the leaderboard part

sharp ridge
#

Or I can add an interface

cyan flame
#

i think what you posted would be fine if you renamed ClientServer to EveryoneMustHaveMod/RequiredByEveryone/RequiredByAll or something

high crown
#

I have it planned for another mod i'm working on too

sharp ridge
#
/// <summary>
///     Specifies the compatibility level of the plugin.
/// </summary>
public enum CompatibilityLevel
{
    /// <summary>
    ///     Mod only impacts the client.
    ///     Mod is not checked at all, VersionStrictness does not apply.
    /// </summary>
    ClientOnly = 0,

    /// <summary>
    ///     Mod only impacts the server, and might implicitly impact the client without the client needing to have it installed
    ///     for it to work.
    ///     Mod is only required by the server. VersionStrictness does not apply.
    /// </summary>
    ServerOnly = 1,

    /// <summary>
    ///     Mod impacts both the client and the server, and adds functionality that requires the mod to be installed on both.
    ///     Mod must be loaded on server and client. Version checking depends on the VersionStrictness.
    /// </summary>
    Everyone = 2,

    /// <summary>
    ///     If mod is loaded on the client, it must be loaded on the server. Not every client needs to have the mod installed.
    ///     Fairly niche and only applies if your mod works fine when a client doesn't have it. Version checking depends on the
    ///     VersionStrictness.
    /// </summary>
    ServerIfClient = 3
}
#

?

cyan flame
#

that seems fine

#

it's probably worth asking everyone who's given feedback to 1 billion percent solidify the names before launch, since we won't be able to change them

#

but that can come later

#

i literally have no idea how you would get the last one across. it's a weird situation

sharp ridge
#

How about ClientOptional?

high crown
cyan flame
#

i like ClientOptional

lament perch
#

the leaderboards are gonna be a pain in the but to transpile

cyan flame
#

but do you think it's theoretically possible

lament perch
#

yes

sharp ridge
#

Isn't ServerOnly basically ClientOptional?

lament perch
#

names make it seem that way lol

sharp ridge
#
  • If the server has it, but the client doesn't, it's fine.
  • If the client has it, the server needs it.
cyan flame
#

no, ClientOptional means if it's installed on the client it must be installed on the server

sharp ridge
#

Right, but that's also the case with ServerOnly mods though

#

In general, I'd say?

cyan flame
#

would it be? I was assuming they'd go through fine

#

if it has required behaviour on the client it would be Everyone

lament perch
#

I say keep it in, and add a remark saying to only use if you know what you're doing

sharp ridge
#
    /// <summary>
    ///     Not every client needs to have the mod installed, but if it is installed, the server also needs to have it.
    ///     Generally used for mods that add extra (optional) functionality to the client if the server has it installed.
    ///     Mod must be loaded on server. Version checking depends on the VersionStrictness.
    /// </summary>
    ClientOptional = 3
#

I'm still not entirely convinced this doesn't come down to the same as ServerOnly, as far as compatibility goes hmm

#
    /// <summary>
    ///     Mod only impacts the server, and might implicitly impact the client without the client needing to have it installed
    ///     for it to work.
+    ///     Mod is only required by the server. VersionStrictness only applies if the mod is installed on the client.
    /// </summary>
    ServerOnly = 1,

Changed this for ServerOnly btw.

lament perch
#

ik someone was mentioning a mod that works on all clients if any single client has it, and although the server doesn't require it, it could be a contender for the enum

cyan flame
#

clientoptional mod would be like

     talk to server optionally, will break if server does not exist
if (server)
     do some behaviour```
server only would be 
```if (client)
     do nothing, this doesn't matter
if (server)
     do some behaviour 
sharp ridge
#

Fair enough

#

I understand the use-case

#

If clientoptional, still allows joins, but only join if server has it

lament perch
sharp ridge
#

Whereas if serverOnly, allow any joins

sharp ridge
#

Though also, I kind of assume there's some kind of network guards in place?

#

Maybe?

cyan flame
#

i can always ask something like

We're curious if there's any way you'd prefer weekly challenge moons to be handled. We could probably do any of the following:
Option 1: Prevent anybody using any mods from entering the challenge moons at all.
Option 2: Allow people with mods to enter challenge moons, but publish results to a separate leaderboard (we think this is possible, not 100% sure)
Option 3: Do nothing and put people with mods onto the same challenge moon/leaderboards
lament perch
#

this creates/loads leaderboards

cyan flame
#

generally(?) steam will just let you do whatever, so it would probably work
but i'm not sure

sharp ridge
#

Okay, but like

#

Is there a "RemoveLeaderboard"?

#

And can modders use that?

#

Because it sounds very wacky right now

cyan flame
#

i hope there's no clientside removeleaderboard because that would plunge things into chaos

sharp ridge
#

Like, anyone could grab that code and literally pre-make leaderboards for the next 1 million challenges

#

Or submit bogus results

#

if there's no guards in place for that, I'm honestly kind of concerned

cyan flame
#

yeah, you could probably do that

sharp ridge
#

Guess you could create a private leaderboard mod hmm

lament perch
#

I'll take a deep look

high crown
cyan flame
#

it's not really something we can stop, it's just an inherit problem with how steam leaderboards work unfortunately

high crown
sharp ridge
high crown
#

even highly competitive games have that problem with all the safeguards they have

sharp ridge
cyan flame
#

if it's a "I don't care, i'm fine with the leaderboards being kinda fucked" could just leave it as-is
it didn't seem designed to be super competitive

lament perch
#

I know we can at least stop modded leaderboards from being sent to the steam servers (if they have this mod)

sharp ridge
lament perch
#

yes

#

at least once

sharp ridge
#

In DMs?

lament perch
#

idk about dms

cyan flame
#

nah i just pinged him in his server before with an official-ish looking question

#

idk if he'll actually reply i'm sure he's flooded

sharp ridge
#

I swear to god, I don't know what I've done, but by all accounts he seems to ignore me 😅

lament perch
#

you aren't bobbie

cyan flame
#

i will be shocked if he's not competely ignoring DMs

#

that's what I would be doing

lament perch
#

honestly

#

rip that discord account

high crown
#

he answered me once in the bug reports channel on his discord when I brought up an update broke kicking functionality completely

sharp ridge
#

Genuinely - no clue what I've done, but I haven't received any replies in public either

high crown
#

I also had @cyan flame vouch for me tho lol

#

if i remember right

cyan flame
#

i doubt you've done anything he's probably just insanely flooded

cyan flame
sharp ridge
#

Maybe? Idk, kind of feel like I somehow annoyed him or something

#

Genuinely no clue what it could have been though

#

Anyway

#

Back to programming

high crown
sharp ridge
#
/// <summary>
///     Specifies the compatibility of a plugin.
/// </summary>
/// <example>
///     <code>
/// [LobbyCompatibilityAttribute(CompatibilityLevel.ServerOnly, VersionStrictness.Minor)]
/// class MyPlugin : BaseUnityPlugin
/// {
/// }
///     </code>
/// </example>
[AttributeUsage(AttributeTargets.Class)]
public sealed class LobbyCompatibilityAttribute : Attribute
{
    /// <summary>
    ///     Initializes a new instance of the <see cref="LobbyCompatibilityAttribute" /> class.
    /// </summary>
    /// <param name="compatibilityLevel">The compatibility level.</param>
    /// <param name="versionStrictness">The version strictness.</param>
    public LobbyCompatibilityAttribute(CompatibilityLevel compatibilityLevel = CompatibilityLevel.ClientOnly,
        VersionStrictness versionStrictness = VersionStrictness.None)
    {
        CompatibilityLevel = compatibilityLevel;
        VersionStrictness = versionStrictness;
    }

    /// <summary>
    ///     Gets the compatibility level.
    /// </summary>
    /// <value>
    ///     The compatibility level.
    /// </value>
    public CompatibilityLevel CompatibilityLevel { get; }

    /// <summary>
    ///     Gets the version strictness.
    /// </summary>
    /// <value>
    ///     The version strictness.
    /// </value>
    public VersionStrictness VersionStrictness { get; }
}
#

Thoughts?

cyan flame
#

lgtm

#

do you think doing just CompatibilityLevel with no VersionStrictness should be a supported usecase

#

or should we force the modder to define that so they know the behaviour

sharp ridge
#

ClientOnly is

#

Hence the default

cyan flame
#

oh, i see, i didn't read the constructor close enough

#

the default versionstrictness should probably be Minor
[LobbyCompatibility(ClientOnly)] // normal client only behaviour, same for serveronly
[LobbyCompatibility(Everyone)] // should be the default VersionStrictness, ideally "Minor" from conversation earlier

sharp ridge
#

Should the default be minor? hmm

cyan flame
#

conversation is up here #1194412635932856440 message

sharp ridge
#

I feel like if we're setting the compat level the lowest by default, the version strictness should be as well?

cyan flame
#

hmm

#

i think if I did [LobbyCompatibility(Everyone)] I would expect some version checking by default

sharp ridge
#

I can also just remove the defaults

cyan flame
#

yeah, that might be the best option

#

jotunn has no defaults

lament perch
#

I was about to say if we don't have defaults it'll cause issues with adoption potentially, but then I realized the default of ClientOnly would practically be the same as no default

cyan flame
#

ye

earnest pilot
#

Just got back, are we removing the fourth enum to replace it with serveronly?

lament perch
#

I believe not

cyan flame
#

Nah there was just a bit of confusion on the usecase

earnest pilot
#

ah alright. ill be back reading again 😸

sharp ridge
#

Repo is up

lament perch
#

do we want to prevent leaderboard upload in this or not?

earnest pilot
#

leaderboard prevention is simpler

sharp ridge
#

Not sure if that's within scope for this mod tbh?

earnest pilot
#

a sperate leaderboard is 🫠

sharp ridge
#

Actually

#

Separate leaderboard might be a simple one

#

A single transpiler to change the leaderboard string to something like modded_(original)

lament perch
#

yeah

earnest pilot
#

oh thats great then

sharp ridge
#

If a plugin is added that is >= serverside, use the custom leaderboard

#

Can do

#

Can you make an issue for this? @lament perch

lament perch
#

yep, may even begin work on it

sharp ridge
#

I'd like to use enums for lobby data keys, to keep it simple:

/// <summary>
///     Metadata keys for use when setting lobby data.
/// </summary>
public static class LobbyMetadata
{
    /// <summary>
    ///     The tag for the lobby name.
    /// </summary>
    public const string Name = "name";

    /// <summary>
    ///     The tag for the host's game version.
    /// </summary>
    /// <remarks>
    ///     Users who try to join compare against this. Mismatches cause a failure to join.
    ///     This should generally not be overridden.
    /// </remarks>
    public const string Version = "vers";

    /// <summary>
    ///     The tag for the lobby being joinable for vanilla clients.
    /// </summary>
    public const string Joinable = "joinable";

    /// <summary>
    ///     The tag for the lobby being modded.
    /// </summary>
    public const string Modded = "modded";

    /// <summary>
    ///     The tag for the lobby being joinable for modded clients.
    /// </summary>
    public const string JoinableModded = "_joinable";

    /// <summary>
    ///     The tag for plugin information.
    /// </summary>
    public const string Plugins = "plugins";
}
sharp ridge
#

You wanna go for it?

lament perch
#

sure

#

I'll look into leaderboards to real quick just to make sure it's okay to do so

sharp ridge
#

Sounds good!

sharp ridge
#

@humble elm

#

The top 3 are vanilla tags, the bottom 3 are tags we would use

cyan flame
#

looks good

cyan flame
#

we can always choose not to

cyan flame
#

ye

sharp ridge
#

We'll go with separate leaderboard

#

Either that, or no leaderboard upload

cyan flame
#

i'll see if zeekerss responds, if not I think we should default to seperate leaderboard

#

no leaderboard at all seems a bit overkill to me, but if he wants that done it'll happen

lament perch
sharp ridge
#

Doesn't Steam have docs?

lament perch
#

I'll look at steam docs kekw

cyan flame
#
#

there's these too for the library that's being used

lament perch
#

Steam docs:

You must call either this or FindLeaderboard to obtain the leaderboard handle which is valid for the game session for each leaderboard you wish to access prior to calling any other Leaderboard functions.

Leaderboards created with this function will not automatically show up in the Steam Community. You must manually set the Community Name field in the App Admin panel of the Steamworks website. As such it's generally recommended to prefer creating the leaderboards in the App Admin panel on the Steamworks website and using FindLeaderboard unless you're expected to have a large amount of dynamically created leaderboards.

You should never pass k_ELeaderboardSortMethodNone for eLeaderboardSortMethod or k_ELeaderboardDisplayTypeNone for eLeaderboardDisplayType as this is undefined behavior.

lament perch
#

so I think we can

#

but it's iffy

sharp ridge
#

Let's test it ✨

lament perch
#

lmao

cyan flame
#

only one way to know

sharp ridge
#

Does your IDE toss you some kind of "Delete" or "Remove" method btw?

cyan flame
#

i looked through and didn't see one in the leaderboard area

sharp ridge
#

The docs don't mention one

#

That makes me concerned as to whether someone could essentially spam new leaderboards tbh

#

E.g. with slurs or other spam names

lament perch
#

I think they could

sharp ridge
#

And Zeekerss would have to manually remove them one by one?

#

Yikes

lament perch
#

want me to try with some random name?

sharp ridge
#

"HiZeekerssJustTesting"? bob_lul

cyan flame
#

i mean if a user is creating a custom leaderboard i don't think it'll display

#

without like, a mod that redirects to that leaderboard

sharp ridge
#

It won't display on the user side

#

But on the administration side?

#

Heck, what if someone creates 1 million leaderboards?

#

I can't imagine Steam will just go "Oh, that's fine, carry on"

#

I'm actually surprised there's no precedent for this

lament perch
#

you can create leaderboards for all future weekly challenges

cyan flame
#

idk steam has like 40 billion dollars somewhere, there's probably precedent in some other game's leaderboards

#

i also think they can just eat whatever the cost is

sharp ridge
#

Also

#

Low key will use this for other games now

#

It'd be neat to add leaderboards for some other games

cyan flame
#

steam provides a bunch of nice stuff for "free"

sharp ridge
#

I need to look into it more tbh

lament perch
sharp ridge
#

That API alone is great

cyan flame
#

they do a lot of great stuff

#

big fan of steam input/proton

lament perch
#

same

sharp ridge
#

Proton & Steam input are amazing

#

Also

lament perch
#

and the stuff with The Day Before

sharp ridge
#

Can we add Newtonsoft.Json somehow? hmm

lament perch
#

steam themselves refunding all buyers

cyan flame
#

newtonsoft ships with the game

lament perch
#

^

sharp ridge
#

Hold up, it does?

cyan flame
#

i think so

#

in managed

lament perch
#

it does

sharp ridge
#

Lmfao

lament perch
#

I use it for my networking api

sharp ridge
#

We actually went through some trouble to add it to the dll for LethalAPI when we were messing with this

#

Dear god

#

I should have just checked the Managed folder

lament perch
#

xd

cyan flame
#

classic blunder

sharp ridge
#

Okay, that's great

#

Makes my life much easier

lament perch
#

if you need to use unity types with newtonsoft I found some random solution on the internet that works

#

fyi @sharp ridge you can just set the main plugin class with the internal attribute modifier and just refer to it as Plugin or whatever class name you want internally

sharp ridge
#

Wdym?

#

I set it as public so other mods can touch it if necessary

#

I do the same with all of my mods tbh

lament perch
#

do they need to touch the plugin class?

sharp ridge
#

They need to be able to access the instance inside of it, if they want to touch it in whatever way

cyan flame
#

oh yeah ryoo sent me some stuff that could be useful to you @sharp ridge #dev-general message

sharp ridge
cyan flame
#

that works

sharp ridge
#
public class LobbyCompatibilityPlugin : BaseUnityPlugin
{
    public static LobbyCompatibilityPlugin? Instance { get; private set; }
#

^

lament perch
#

maybe don't know enough about modding, but again, why do they need access to the instance?

sharp ridge
#

¯_(ツ)_/¯

#

No clue

#

But if they ever need do, they can

lament perch
#

hmm

sharp ridge
#

If they need to, I'd rather I make it easily accessible that force some wacky shenanigans

#

It doesn't change anything for development on my end

lament perch
#

I might reflect to do the same with my mods then

#

just leads to longer typing and I couldn't be bothered at first

cyan flame
#

i leave mine as public because that's what the template does

cyan flame
#

lmk once you have some of the basic patches ported over @sharp ridge i'll see if I can cook up some UI

#

(unless you were going to do that already)

lament perch
#

max, you're killing me
you don't even have an assembly-csharp reference or lord's game libs yet xd

sharp ridge
#

I was going to use environment variables

#

But I was wondering what y'all think is best?

cyan flame
#

if gamelibs works i would just use that
is lord's actually finished

sharp ridge
#

Wdym?

#

Lord's game libs?

lament perch
#

I think it''s called dehumidifer

sharp ridge
#

I seem to have missed something

lament perch
sharp ridge
#

Huh, damn

cyan flame
#

what's the difference between this and the other gamelibs

sharp ridge
#

Are we sure this is good?

lament perch
#

lord made it

sharp ridge
#

Like, no wackiness

#

Because it's certainly convenient

lament perch
#

@dusty root opinions if you're on

sharp ridge
#

Though, does it add all content in the Managed folder?

#

Or what

cyan flame
#

removes some, like mono/system dlls

lament perch
#

publicizes assembly-csharp

#

realized slight issue with leaderboard stuff, I'll look into it

sharp ridge
#

You wanna add Lord's stuff in a quick PR? @lament perch

lament perch
#

made @sharp ridge

sharp ridge
#

Right, these are also stripped

#

Hm

#

That's mildly annoying for checking things with Rider's decompiler tbh

#

Wasn't there some kind of user-specific csproj system you can use to override things locally? @lament perch @cyan flame

cyan flame
#

you can use .csproj.user files

lament perch
#

yeah

cyan flame
#

i think one of xilo's repos did that

lament perch
#

I can set you up

sharp ridge
#

Please do, I love the idea of these publicised, stripped assemblies for general project management / collaboration, but I personally prefer being able to jump into the decompiled source using Rider and having a read :P

lament perch
#

@sharp ridge instuctions only in pr, but added

#

user file will also automatically add your (debug) built assembly & pdb to your plugin folder (unless you remove that portion)

sharp ridge
#

I'll probably make a Record to store the plugin information, like I did for LethalAPI.

I'll add:

  • GUID
  • Version
  • Compatibility Enum
  • Version Strictness Enum
#

@lament perch @cyan flame

cyan flame
#

for internal use or for plugins to define

sharp ridge
#

For internal use

#

To serialise and unserialise / compare to

#

So we serialise a list of those for plugin information

lament perch
#

sounds good

#

I got fun times :#

#

powers flickering

sharp ridge
#

IRL?

lament perch
#

ye

sharp ridge
#

RIP

lament perch
#

went long enough to turn off my monitor for a sec but not long enough to turn off my pc

#

issue isn't even power going out, just power flicker

#

we got a generator here just cause it's the boonies

cyan flame
lament perch
#

storm went through today in ga

sharp ridge
#

Seems overkill tbh

cyan flame
#

would probably be only useful if we added a like

sharp ridge
#

Could be added as a check and logged as a warning?

cyan flame
#

"MAXMIMUM COMPATIBILITY EMERGENCY MODE", I wouldn't worry about it for now

sharp ridge
#

But yeah

#

KISS for now

lament perch
#

who wants to send checksums that may be incorrect /s-ish

cyan flame
#

thankfully no modders have ever updated their mod without changing the internal version

lament perch
#

never

#

actually luckily thunderstore prevents that

#

wait no

cyan flame
#

in the manifest, not the dll

lament perch
#

it dont

#

ye

#

hmm... just realized I'm trying to transpile an async

cyan flame
#

which method

lament perch
#

MenuManager.GetLeaderboardForChallenge

#

and RemoveLeaderboardScore as well

#

I got async IL lmao

#

ik there's a harmonyx helper for enumerators

#

but I don't think this counts

#

may have found a solution

sharp ridge
lament perch
#

ye I'm using

#

just trying to target the right method

dense forum
#

I kinda dislike codematcher lmao

#

idk why

sharp ridge
#

It's clean, easy to maintain, and less prone to user error

#

What's there to dislike?

#

@lament perch @cyan flame

Clean this up ✨

        if (pluginInfo.Any(plugin => plugin.CompatibilityLevel is CompatibilityLevel.ServerOnly
                or CompatibilityLevel.Everyone or CompatibilityLevel.ClientOptional) &&
            !lobby.GetData(LobbyMetadata.Name).Contains("modded"))
            lobby.SetData(LobbyMetadata.Name, "modded // " + lobby.GetData(LobbyMetadata.Name));
#

I swear to god, I've put it into curly braces, but Rider insists on making it as compact as possible

cyan flame
#
if (!lobby.GetData(LobbyMetadata.Name).Contains("modded") && pluginInfo.Any(plugin => plugin.CompatibilityLevel is not CompatibilityLevel.ClientOnly))
{
  lobby.SetData(LobbyMetadata.Name, "modded // " + lobby.GetData(LobbyMetadata.Name));
}
sharp ridge
#

Fair

#

It's nearly 6 AM

#

My brain is dead

cyan flame
#

tbf it assumes we won't add more compatibility levels

#

that would affect that at least

#

(which we probably won't but idk)

sharp ridge
cyan flame
#

if Enum.Length > 4 throw NotImplementedException

sharp ridge
#

Should I document in depth? hmm @cyan flame

cyan flame
#

wdym

sharp ridge
#

As in, like I've been doing here:

/// <summary>
///     Patches <see cref="GameNetworkManager.SteamMatchmaking_OnLobbyCreated" />.
///     Adds extra lobby metadata to be used for dependency checking.
/// </summary>
/// <seealso cref="GameNetworkManager.SteamMatchmaking_OnLobbyCreated" />
[HarmonyPatch(typeof(GameNetworkManager), nameof(GameNetworkManager.SteamMatchmaking_OnLobbyCreated))]
[HarmonyPriority(Priority.Last)]
[HarmonyWrapSafe]
internal static class SteamMatchmakingOnLobbyCreatedPostfix
{
    [HarmonyPostfix]
    private static void Postfix(Result result, ref Lobby lobby)
    {
        // lobby has not yet been created or something went wrong
        if (result != Result.OK)
            return;

        var pluginInfo = PluginHelper.GetAllPluginInfo().ToList();

        // Modded is flagged as true, since we're using mods
        lobby.SetData(LobbyMetadata.Modded, "true");

        // Add plugin metadata to the lobby so clients can check if they have the required plugins
        lobby.SetData(LobbyMetadata.Plugins, PluginHelper.GetLobbyPluginsMetadata());

        // Set the joinable modded metadata to the same value as the original joinable metadata, in case it wasn't originally joinable
        lobby.SetData(LobbyMetadata.JoinableModded, lobby.GetData(LobbyMetadata.Joinable));

        // Add a prefix to the lobby name to indicate that it's modded, if it doesn't already have some kind of modded mention
        if (pluginInfo.Any(plugin => plugin.CompatibilityLevel is CompatibilityLevel.ServerOnly
                or CompatibilityLevel.Everyone or CompatibilityLevel.ClientOptional) &&
            !lobby.GetData(LobbyMetadata.Name).Contains("modded"))
            lobby.SetData(LobbyMetadata.Name, "modded // " + lobby.GetData(LobbyMetadata.Name));

        // Disable vanilla joining if any plugins are present that clients need
        if (pluginInfo.Any(plugin => plugin.CompatibilityLevel == CompatibilityLevel.Everyone))
        {
            LobbyCompatibilityPlugin.Logger?.LogWarning(
                "You are hosting a lobby with plugins required by everyone. Disabling vanilla clients from joining.");
            lobby.SetData(LobbyMetadata.Joinable, "false");
        }
    }
}
dense forum
#

switching just doesn't feel right

sharp ridge
#

Gotta move along with new technology ✨

dense forum
#

never had problems with the old ways ¯_(ツ)_/¯

sharp ridge
# cyan flame wdym

I'm not generally a fan of too much in-code documentation, since it tends to become outdated if not updated diligently. But perhaps it's important for this mod?

cyan flame
#

yeah i'd say to keep things fairly documented since it's very important that it remains maintained

lament perch
#

is it possible to patch a struct? haven't tried and having difficulty targeting it

sharp ridge
#

Preference?

#
        switch (source.VersionStrictness)
        {
            case VersionStrictness.None:
                return true;
            case VersionStrictness.Major when target.Version.Major != source.Version.Major:
            case VersionStrictness.Minor when target.Version.Major != source.Version.Major ||
                                              target.Version.Minor != source.Version.Minor:
            case VersionStrictness.Patch when target.Version != source.Version:
                return false;
            default:
                return true;
        }
    internal static bool MatchesVersion(PluginInfoRecord source, PluginInfoRecord target)
    {
        if (source.VersionStrictness == VersionStrictness.None)
            return true;

        if (source.VersionStrictness == VersionStrictness.Major)
        {
            if (target.Version.Major != source.Version.Major)
                return false;
        }
        else if (source.VersionStrictness == VersionStrictness.Minor)
        {
            if (target.Version.Major != source.Version.Major ||
                target.Version.Minor != source.Version.Minor)
                return false;
        }
        else if (source.VersionStrictness == VersionStrictness.Patch)
        {
            if (target.Version != source.Version) return false;
        }

        return true;
    }
#

Sometimes, Rider truly suggests something I can only describe as horror bob_lul

#

Kinda neat you can get it that compact, though.

#

But I assume we're all in favour of #2?

cyan flame
#

yeah that switch case is kind of hurting my brain

lament perch
#

I like switch statements but the second is easier to read

sharp ridge
#

I didn't even know you could write switch cases like that tbh

cyan flame
lament perch
#

ye

#

also has the slight issue of being compiler generated, so I have to access it somehow 💀

cyan flame
#

wtf are you trying to patch

lament perch
#

an async method

#

I'm trying to use harmonyx's enumerator patch thingy, but it just seems like it doesn't want to target

#

maybe cause it's not designed for asyncs

#

not in the version of BepInEx we use

cyan flame
#

oh no is it bepinex 6

lament perch
#

imma take a look at what it does tho

sharp ridge
#

I'm gonna get some sleep now, since it's 6 AM

#

I'm sure there's some stuff in there that I'll have to fix in the morning

#

I've got the patches mostly moved over, but they depend on the PluginHelper

#

If y'all want to do a quick review, I can toss a PR up with the initial patch at least

cyan flame
lament perch
#

yep

cyan flame
#

it might be reasonable/possible to do but you'd probably have to ask a bepinex person

#

i assume you can't just patch another submethod and it NEEDS to be a transpiler

lament perch
#

since it's async it definitely needs to be a transpiler I believe

#

(also a lot of code that can cause breaking on update)

cyan flame
#

are you just trying to change the name

lament perch
#

yep, trying to change a single string

cyan flame
#

hmm, could you prefix SteamUserStats.FindOrCreateLeaderboardAsync with like
ref name => name = name.Replace("challenge", X)

lament perch
#

🤔

#

no wont work

#

actually also have to patch several other things in the method

#

relating to save files

#

unless I patch that too

cyan flame
#

you could just patch that too

lament perch
#

but it starts getting real finnicky

cyan flame
#

yeah it's not ideal

#

if you're just looking for a POC you could just patch string.format lol

#

i guess if you have to change other save file things it wouldn't matter tho

lament perch
cyan flame
#

preloader patches/monomod always an option if there's truly no way

sharp ridge
lament perch
#

sorry looked through it but got disctracted, will write in gh rq

sharp ridge
#

Have a peek at the patches here

#

I still need to do some cleaning / testing tomorrow, but this should be roughly the logic

cyan flame
#

sure thing, I might play with the UI a bit

#

i will probably just buy a second copy of the game and use my steam deck unless there's a better way to test pubs lol

lament perch
#

have friends clueful

sharp ridge
#

The game doesn't have any copyright protection

#

You can just boot it up through the .exe, and connect on LAN

#

It's how I locally test my stuff

cyan flame
#

yeah I usually do that, I need to specifically mess with the lobby list though

#

don't think you can do that through LAN

sharp ridge
#

...fair point

lament perch
#

not that I've been able to do

sharp ridge
#

Just hassle Xilo to help you

#

Goodnight lads

cyan flame
#

night night

sharp ridge
#

(Ping me if you need me)

lament perch
#

nighty night

cyan flame
#

oh SteamLobbyManager.LoadServerList is async huh

lament perch
#

lol

#

need to figure out this async thing, huh

#

running into a confusing issue

#

I got the MoveNext method

#

which has the code I need to patch

#

but I get an error from harmony saying that the method is null

#

but I check to see if the method is null beforehand, and it isn't

#

nvm

#

im an idiot

#

the method I said to use to transpile was private xd

#

okay it works

#

@cyan flame

#

I can transpile async methods now

cyan flame
#

what's the meta

lament perch
#

this shit

cyan flame
lament perch
#

hf

#

:)

cyan flame
#

could you uh

#

give that in text form perhaps

lament perch
#

I'll comment and give text

cyan flame
#

i'm gonna have to REALLY fuck up SteamLobbyManager.LoadServerList probably

earnest pilot
#

transpiling async? whuhuh

lament perch
#
internal static void AsyncPatcher(Harmony harmony)
{
    // Get the Method Info of the target Async Method; Can replace Type & Method with any async method
    var getLeaderboard = AccessTools.Method(typeof(MenuManager), nameof(MenuManager.GetLeaderboardForChallenge))
        // Find the AsyncStateMachine class from target method
        .GetCustomAttribute<AsyncStateMachineAttribute>()
        // Get the struct type (random compiler junk), and then get the <GetLeaderboardForChallenge>d__80::MoveNext
        .StateMachineType.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.NonPublic);

#if DEBUG
    // Make sure we get it
    LobbyCompatibilityPlugin.Logger?.LogDebug($"{getLeaderboard} - {getLeaderboard == null}");
#endif

    // Use a manual patch to patch the MoveNext method - transpiler must be public
    harmony.Patch(getLeaderboard, transpiler: new HarmonyMethod(typeof(LeaderboardPatch).GetMethod(nameof(ModdedLeaderboards))));
}
earnest pilot
#

terrifying

lament perch
sharp ridge
#

Is there a reason we can't just use BepInEx 6?

lament perch
#

it would force everyone to use it, no?

sharp ridge
#

...probably?

#

Maybe?

lament perch
#

which can cause a lot of compat. problems

sharp ridge
#

Is it backwards compatible?

cyan flame
#

no

sharp ridge
#

Alas

cyan flame
#

it's fairly different

lament perch
#

if the major incremented, I assume not

cyan flame
#

and more actively developed / less stable(?)

#

it has il2cpp support tho

sharp ridge
cyan flame
#

looking into lobby filtering options for the lobbylist UI

sharp ridge
#

May be able to reuse the tag system

#

Have a look there

lament perch
sharp ridge
#

Okay, back to sleep I go

cyan flame
#

yeah, i might reuse tags or add some new lobby data
it does seem like it's going to be fairly extensible

lament perch
#

welp imma head to bed too

cyan flame
#

i'll probably cook up a concept or two but I doubt i'll get much done tonight

#

the sort area is already getting a bit crowded in vanilla

dusty root
# sharp ridge Right, these are also stripped

Feel free to look over my code - the significant stuff is in build/program.cs
the readme of the Dehumidifier describes what it does :)
Yeah they have to be stripped so I can redist :(

dusty root
#

'network syncing lib'? What are we network syncing

#

in terms of mod compatability, Lc-api has an open issue for networkcompatability

cyan flame
#

the title kind of sucks. it's for lobby compatibility with different mods though

cyan flame
dusty root
#

right

#

so

#

to do this

#

hmm

#

I'm torn on whether they should be separate

cyan flame
#

i think ideally it's separate and fairly light, to make as many people as possible depend on it

dusty root
cyan flame
#

oh yeah we won't be able to get everyone

#

it's not possible at this point

#

but like, maximize the amount of people who are willing

dusty root
#

whereas if it's a feature integrated into a helpful API that is used for its other features

#

but

#

what we could do is make it separate

#

and then have lcapi depend on it

cyan flame
#

yeah, agreed
that's probably the best of both worlds

dusty root
#

hmmm

#

@dense forum I think lcapi needs to become separate modules

#

because this would benefit from making use of lcapi networking

#

specifically the custom net messages

cyan flame
#

tbh we can do everything with custom lobby data, it's not really necessary

dusty root
#

true

dusty root
#

Which is a superset of that

#
  • lobby data to provide info prior to connect
  • attempting to connect is allowed in some cases even if lobby data implies incompatibility (because this mod, and mod authors are fallible)
  • host options for allowing/disallowing incompatible clients and what counts as incompatible
  • on connect, the host checks the modlist and rejects connection if it fails verification
cyan flame
#

i posted some concepts a bit higher up in the chat

#

#1194412635932856440 message

#

[WIP] Proposed Lobby Mod List Syncing Library

dusty root
#

yeah the concept looks great

dusty root
#

in terms of behaviour

cyan flame
#

this is all I have so far, i'll go ahead and push it before sleeping though

#

the join button is REALLY wide, it's going to be kind of annoying to work around

#

either we're going to have to accept a massive gap, move it slightly, or scale it down horizontally

#

@sharp ridge i forsee needing to get/otherwise use lobby mod data in the following places:

  • SteamLobbyManager.LoadServerList for filtering lobbies initially. probably will ask steam to check for _joinable to filter out vanilla lobbies, if the user requests it
  • SteamLobbyManager.loadLobbyListAndFilter for displaying the above lobby data (is state Compatible/Incompatible/Unknown?)
  • Need diff data whereever we decide to hook up that modal where the question mark is currently
  • Need diff data if join failed (even if lobby was labeled compatible, failures can happen)
#

so, ideally we'd have some diff/status that's cached per lobby id so we don't have to keep calculating it

dusty root
#

Can I also recommend following Jotunn's lead on the enums

#

CompatabilityLevel

NotEnforced: The compatibility check ignores if the mod is installed or not and also does not check the version at all. VersionStrictness value is ignored.

VersionCheckOnly: The compatibility check ignores the mod if it is not installed on both sides. If it is installed on both sides the version is checked according to the VersionStrictness value.

EveryoneMustHaveMod: It is checked if the mod is installed and if the version matches the requirements of the VersionStrictness value.

ClientMustHaveMod: If the mod is installed on the server, every client must have it, too. VersionStrictness does apply when installed on both sides.

ServerMustHaveMod: If the mod is installed on the client, the server must have it, too. VersionStrictness does apply when installed on both sides.
#

I think it would be good to use consistent naming across the communities

earnest pilot
#

oh ServerMustHaveMod is a lot more understandable than what we have rn lol

earnest pilot
#

Oh btw, slightly off topic but I would suggest to have a toggle for a functionality wherein:
If the game updates to a new version/version number changes the mod will revert all its changes to vanilla functionality unless the user enables the config to allow for possible issues (if a game update breaks the mod for example),

Ideally if this config is toggled off by default and the game updates, it will show a notification that the mod functionality is disabled, and how to enable it if you want

#

This would lead to people not using mod managers to be informed whether or not to update the mod in the future

dense forum
#

no matter what I do with LC API it will break pretty much every plugin using it if I make an update like this

#

at least separating the server sided stuff from the client sided stuff

dusty root
#

one that appears unaffiliated to lcapi

#

But yeah, I get what you're saying

#

it's probably a good idea

dense forum
#

The thing is I have to do something

#

lol

dusty root
#

I was thinking about it yesterday actually

#

and coming to the same conclusion

dense forum
#

idk what I'd call the new library tho

dusty root
#

yeah

#

something clever, like Jotunn

dense forum
#

lol

#

where does jotunn even come from for its name?

dusty root
#

and jotunn is a mythical creature from germaic (Viking, I think) mythology

#

you could go the Minecraft route and call it Lethal_Forge 😂

dense forum
#

What if I called it "Experimentation" lol

dusty root
#

you know

#

I really like that actually

#

WAIT

#

I HAVE AN ACTUAL GOOD IDEA

lament perch
#

you can't just drop that and not say your idea

dense forum
#

he sent it to me lol

dusty root
lament perch
#

lol ok

dusty root
#

no because it's actually so good

#

i thought it would be taken for sure

lament perch
#

I wanna know what it's called now xd

sharp ridge
sharp ridge
sharp ridge
lament perch
#

this leaderboard patch sort is larger than I first though lol

sharp ridge
lament perch
#

I think a_this would work

cyan flame
#

hover is a good option but coding that without assetbundles would be fairly annoying

dense forum
#

nice room name

cyan flame
#

oh yeah i didn't even check it

sharp ridge
#

Rectangle with some text

cyan flame
#

i could do it, creating new UI elements through code only is a lot of trial and error though
recttransforms were NOT meant to be fucked with through code

#

i mostly just didn't feel like it last night. i'll see about today

#

it would need to do this thing too

sharp ridge
#

Easy enough, no?

cyan flame
#

ye

sharp ridge
#

Create the rect separately

#

Could always just opt to always place it in the top right, as well

#

Or left side?

#

Actually, left side might be best?

#

So you can go through the joins

#

Far more space to work with, as well

cyan flame
#

hmm, i'll see how it looks (and how wide it ends up being)

#

there is a lot of empty space towards the right

sharp ridge
#

E.g. ⚠️ means X

cyan flame
#

Yeah, I need to cook up some icons

#

Do y'all generally agree that we should try to avoid using assetbundles for maintainability reasons

lament perch
#

yeah, it would be nice

#

though how do you load custom images?

cyan flame
#

Should be able to convert an embedded dll PNG to a sprite at runtime

#

That's what most beat saber mods do for UI

lament perch
#

oh, huh

#

cool

dense forum
#

Is there a reason you're trying to avoid asset bundles?

lament perch
#

anytime you want to update something with ui or whatnot, you don't have to go into unity to update accordingly

cyan flame
#

I would ideally like things to be maintainable with as little specific knowledge as possible

If you ever need to update UI, you'll have to install unity/update things in there/create a new bundle

#

If it becomes necessary I don't mind using bundles, I don't think there's gonna be much UI though

sharp ridge
#

I can help with programmatical UIs if we want

#

I've done a little bit of work with it

lament perch
#

@sharp ridge have you had any luck with transpilers working with the mod? currently trying to transpile, seems like it is running, but no noticible changes nor DumpedAssembly show up

cyan flame
#

my transpiler has worked fine

lament perch
#

🤔

cyan flame
#
    [HarmonyPatch]
    [HarmonyPriority(Priority.First)]
    [HarmonyWrapSafe]
    internal class LoadLobbyListAndFilterTranspiler
    {
        [HarmonyTargetMethod]
        private static MethodBase TargetMethod()
        {
            return AccessTools.EnumeratorMoveNext(AccessTools.Method(typeof(SteamLobbyManager), nameof(SteamLobbyManager.loadLobbyListAndFilter)));
        }

        [HarmonyTranspiler]
        static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            // The method used to get the LobbySlot after instantiation (yes, it's accessed through GetComponentInChildren)
            var lobbySlotMethod = AccessTools.Method(typeof(GameObject), nameof(GameObject.GetComponentInChildren), new Type[0], new Type[] { typeof(LobbySlot) });

            return new CodeMatcher(instructions)
                .SearchForward(instruction => instruction.Calls(lobbySlotMethod))
                .ThrowIfInvalid("Could not find LobbySlot method")
                .RemoveInstructions(1)
                .Insert(new CodeInstruction(
                    OpCodes.Call,
                    AccessTools.Method(typeof(LoadLobbyListAndFilterTranspiler), nameof(InitializeLobbySlot))))
                .InstructionEnumeration();
        }```
#

make sure all the necessary attributes are in the proper place ig

lament perch
#
[HarmonyPatch(typeof(MenuManager), nameof(MenuManager.SetIfChallengeMoonHasBeenCompleted))]
[HarmonyPriority(Priority.First)]
[HarmonyWrapSafe]
public class LeaderboardPatch
{
    [HarmonyTranspiler]
    private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
    {
        LobbyCompatibilityPlugin.Logger!.LogDebug("Attempting to change save file");
        
        // For all instructions, do this: (for each)
        var newInstructions = instructions.Select(codeInstruction =>
            {
                // If the operand is "LCChallengeFile", replace it with "LCModdedChallengeFile", otherwise leave as is
                codeInstruction.operand = codeInstruction.Is(OpCodes.Ldstr, "LCChallengeFile")
                    ? "LCModdedChallengeFile"
                    : codeInstruction.operand;
                return codeInstruction;
            }
        );
        
        newInstructions.Do(inst => LobbyCompatibilityPlugin.Logger.LogDebug(inst));

        return newInstructions;
    }
}

(mostly) what I got rn, I can see debug output, but it just doesn't seem to actually do anything in game

cyan flame
#

is it actually modifying the IL

lament perch
#

I see the updated values in the output

[Debug  :LobbyCompatibility] ldstr "SetChallengeFileMoney"
[Debug  :LobbyCompatibility] ldc.i4.0 NULL
[Debug  :LobbyCompatibility] ldstr "LCModdedChallengeFile"
[Debug  :LobbyCompatibility] call static void ES3::Save(string key, bool value, string filePath)
[Debug  :LobbyCompatibility] ldstr "ChallengeWeekNum"
[Debug  :LobbyCompatibility] ldloc.0 NULL
[Debug  :LobbyCompatibility] ldstr "LCModdedChallengeFile"
[Debug  :LobbyCompatibility] call static void ES3::Save(string key, int value, string filePath)
[Debug  :LobbyCompatibility] ldstr "FinishedChallenge"
[Debug  :LobbyCompatibility] ldc.i4.0 NULL
[Debug  :LobbyCompatibility] ldstr "LCModdedChallengeFile"
[Debug  :LobbyCompatibility] call static void ES3::Save(string key, bool value, string filePat
cyan flame
#

hmm

#

are you sure it's the right method

sharp ridge
#

The async method?

lament perch
#

just a normal transpiler in general

#

it's called, gets the right info, modifies the IL, but it seems like the IL isn't actually modified in-game

lament perch
cyan flame
#

not harmony

lament perch
#

oh huh

#

yes it is

lament perch
#

I may have been dumb then, lemme check some'in rq