#[WIP] LobbyCompatibility
1 messages · Page 2 of 1
okay i got the resouce sprite loading thing
neat
big ass triangle with an exclamation mark in it
okay it seems to have worked
time to figure out how to clean this up
[HarmonyTranspiler]
[HarmonyPatch(typeof(MenuManager), nameof(MenuManager.SetIfChallengeMoonHasBeenCompleted)), HarmonyPatch(typeof(MenuManager), nameof(MenuManager.EnableLeaderboardDisplay))]
[HarmonyPatch(typeof(SaveFileUISlot), nameof(SaveFileUISlot.Awake)), HarmonyPatch(typeof(SaveFileUISlot), nameof(SaveFileUISlot.SetChallengeFileSettings))]
[HarmonyPatch(typeof(GameNetworkManager), nameof(GameNetworkManager.Start)), HarmonyPatch(typeof(GameNetworkManager), nameof(GameNetworkManager.SetLobbyJoinable)), HarmonyPatch(typeof(GameNetworkManager), nameof(GameNetworkManager.SaveGameValues))]
[HarmonyPatch(typeof(StartOfRound), nameof(StartOfRound.SetTimeAndPlanetToSavedSettings))]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
8 different methods need to run through the same transpiler 
(technically 10, but two are ran through it elsewhere)
then split it into 1 method for each transpiler
like have 8 instances for the same transpiler or 8 transpilers that call the same method?
is that everywhere the string is hardcoded
probably no way around the MegaMethod then
this is a cleaned up version
[HarmonyTranspiler]
// Normal Menu Manager methods to patch
[HarmonyPatch(typeof(MenuManager), nameof(MenuManager.SetIfChallengeMoonHasBeenCompleted))]
[HarmonyPatch(typeof(MenuManager), nameof(MenuManager.EnableLeaderboardDisplay))]
// Save File UI Slot methods to patch
[HarmonyPatch(typeof(SaveFileUISlot), nameof(SaveFileUISlot.Awake))]
[HarmonyPatch(typeof(SaveFileUISlot), nameof(SaveFileUISlot.SetChallengeFileSettings))]
// Game Network Manager methods to patch
[HarmonyPatch(typeof(GameNetworkManager), nameof(GameNetworkManager.Start))]
[HarmonyPatch(typeof(GameNetworkManager), nameof(GameNetworkManager.SetLobbyJoinable))]
[HarmonyPatch(typeof(GameNetworkManager), nameof(GameNetworkManager.SaveGameValues))]
// Start of Round method to patch
[HarmonyPatch(typeof(StartOfRound), nameof(StartOfRound.SetTimeAndPlanetToSavedSettings))]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
and then I can't really see if the leaderboard patch works correctly
also unsure if this can be unpatched
likely to cause errors
could you patch ES3?
for some
just a hardcoded string check on the save/load methods
unless I just turn it so anytime it tries to set/retrieve LCChallengeFile, give it LCModdedChallengeFile
then it probably would work
lemme see rq
you could probably do that, just like
if (ref path == "LCChallengeFile")
path = MyReplacementMethod()
MyReplacementMethod() =>
return ModsEnabled ? "ModdedLCChallengeFile" : "LCChallengeFile";
that probably would have been easier...
now you've gained newfound transpiler knowledge, all part of the journey
some of those comments just seem unnecessary lmao
a few really aren't necessary, more to just separate up the code to make it easier to read (while providing info if you somehow don't understand some of the code)
images look a bit rough
i might crunch them, they don't fit super stylistically
and the checkmark should either not be there or be something else
how well does harmony deal with overloads?
wdym
those should be fine, you might just have to specify a type array for each overload
generics might be a little weird but i'm sure it's possible
i think most of the trouble for generics is when you only want to patch a specific value of T but it won't matter here
wait I might be able to patch a lower level
yeah if they're all just calling one method at the base
you can probably just patch that
how are we feeling about a layout like this chat
what about a similar color scheme to what the game has
as in a darker background and lighter text
yeah, it could probably look more like the game's alert panel
the actual structure of displaying mods is going to take some thinking
because people WILL have like hundred-mod-long diffs
how hard is it to create a scrollbar?
shouldn't be too hard, i have some code for it already in lethalrebinding
the UX implications of putting a scrollbar into a hover modal is kind of weird tho
maybe a more details option or something? idk
i would like a quick, at-a-glance way to check other people's mods
max posted this, I think it'd work great on a disconnect #1194412635932856440 message
need to do some thinking for something easily digestible though
maybe display how many mods are incompatable, and display the first 6/8?
perhaps, maybe you get the small list on hover
but if you want the full disconnect screen you would click the icon
i would love some way to integrate with r2modman profile codes but
probably out of scope for now
yeah that would be a nice feature to add at some point, but not rn
just a quick thought I want to write down for future reference on that topic:
should we only have the mods that are necessary, or all mods the server host has?
I think all mods should be displayed somewhere
but in the "at-a-glance" modal, probably just necessary ones
okay I have the leaderboard patches down to two ~17 line files
it's anti-aliasing I think?
could be
that and it being on sub-pixels
there's a lot of subpixel fuckery, some bits are 2-3 pixels off
i think I like this spot for the icon but i'll have to play with it more
yeah that looks good
@sharp ridge are we going to use Lord's GameLibs, or a user file directing to a dependencies folder/managed folder
just so I know which to use for the PR for leaderboards
Lord's seems fine imo?
kk
The user thing was moreso for me personally / I wanted to know how to do it
Lord's seems more long-term maintainable though
I think I need at least a publicized assembly for facepunch.steamworks
or some weird c# shenanigans to get it anyways
lemme try something
That's part of Lord's, no?
it's not publicized
I'm using it in my patches and only using Lord's
Ah
I'm personally not super iffy about having some patches not use nameof tbh
I can't even access the class without publicizing it lol
if need be I can easily manually patch it
maybe just slap some reflection in for now
can I use that in an attribute or just through manual patching?
you could use [TargetMethod]
ah yeah
like up here
has anyone used the challenge thing yet?
the save files should be separate so I should be able to try both rq
I'll push it in a little
okay yeah it's not too competitive just seeing it rn
you can continously restart
I'll check again, but I don't think so
didn't see any checks to make sure you only submit once
also wtf is the insanity noises now
heard a loud ass laugh
works
making pr
so people know it's a different leaderboard
i have this for the lobby mod icon, just needs to be hooked up as a button
(and probably redo the checkmark icon)
does this compile you for locally
oh, i probably don't have the c#12 feature
any chance you could change it to new Type[]{x,y} just for older net sdk compatibility lol
sure thing
pretty sure adding <LangVersion>preview</LangVersion> would also allow it for at least net 7 sdk lol
but I changed it to new Type[] { /*...*/ }
I just jumped off the ship before it landed
fair
okay yeah you're right
it's meant to just be tried as many times
so you can get the best possible score
learn the layout, have a team of 50 people each grab 1 scrap
then kill 49 of them and collect their bodies
insane strats
maybe not now, but do you think it'd be worth separating the leaderboards even more in the future
I was thinking about it
but how much is too much?
we could add a leaderboard attribute to the thing and allow mod devs to decide if they want a separate leaderboard
maybe, i just don't want it to be overkill
should the lobby expansion mods have separate leaderboards? maybe!
but every mod that changes gameplay in any way would probably be too much
lets boil it down to the top 10 mods that change gameplay dramatically
if that even
10 is so many permutations
even like 5 would probably feel a bit too empty
might be better to just keep it simple
yeah that might work
would be cool if we could tack on people's r2 profile codes/modlist, but that's likely not possible
you could have it be user-inputted into a new box here
but, it would introduce other problems
including people putting fake data, or r2 codes with malware hidden somewhere
yeah
should be something we look into at somepoint
cause it would be cool to have like osu!'s leaderboard thing where it shows what mods they use
oh explicitly saying "this leaderboard score used these mods" would go pretty hard
i don't think we should spend time on that for now tho
yeah
later thing
I'll make issues for the two "for the future" things just so we can more easily track it
theoretically, it should be possible to generate the r2 profile code based off the mod list, no?
true
could always talk to mythic if we end up wanting to do that though
though thunderstore also has their own api on github no?
I don't think that's for us to do tbh
yeah maybe not
although if there's demand it might be handy to provide an attribute or something
Could always add a "click to copy to clickboard" kind of thing
Seperate mod imo
Let's keep the focus of this on reducing conflicts between vanilla & modded lobbies and gameplay.
No scope creep
fair
okay yeah
i'm gonna see if I can get the hover/"more details" UI panels hooked up for this tonight
after that I don't think there's a ton to do
I'll finish my patches today or tomorrow, and then we can do some testing
I'll try and figure out some github workflow stuff (cause I need it for my api too)
just to make releasing easier
nuget support is gonna be a godsend, i would look into hooking that up through CI if you have some time
i don't remember if I linked trombloader before but it has all the fancy thunderstore package/nuget CI seutp
GOTH GIRLS?!?!
you could try using the thunderstore api so you can just click on the mod name and it opens the page
or even click a button and it installs it if you have the app
#1194412635932856440 message
Do we want to add a compatibility level that is ClientOnly, but only for modded lobbies?
Basically for modders to flag any mods that technically are client only, but might be considered cheating/overpowered in vanilla?
So it essentially requires a modded lobby (even if only using this mod)
I'm kind of on the fence
People just wouldn't use it
If someone's making a cheat-y mod, they're not gonna mark it as cheaty
such that people are unable to use it
if the default behavior is just no checks, I think adding the modded lobbies check to ClientOnly would work
though what lord's saying is true
I'm a bit confused about what this discussion is about because the behaviour of compatabilty levels on jotunn's docs are really well documented
wait what? but you are providing the Networkcompatability attribute right?
You'd be better off generating a 'profile export' ( just mod manifest ) yourself and asking R2 to make a profile code out of it
We're not making Jotunn, though 
I'm moreso talking about mods that aren't cheats, but might also not be considered "vanilla" or strictly QoL.
Though it's a niche case regardless
It mostly stems from some debate on the unofficial community where some people draw the line between what is considered "vanilla" / allowed in vanilla advertised lobbies, and what warrants a modded lobby.
That discussion was regarding an attribute for custom / mod-defined modded leaderboards. That definitely does not fall inside of the scope of something like this.
We do have a modded leaderboard patch to prevent modded users from interacting with the vanilla leaderboard. But anything more than that is definitely out of scope / should be a separate API/mod.
A discussion related to that, as well as other points, can be found further up in chat.
yeye
maybe not, but you are making something that does exactly the same thing
So jotunn, being well-established and loved, makes a very good template
Right, that's cool. And we've definitely taken some inspiration from it (as mentioned at some points in this thread). The modding landscape and game are fairly different though, so some critical thinking and discussing rather than carbon copying is probably a good thing.
This would also rather apply to modders that don't make cheat mods. Since I very much doubt those modders would use something like this in the first place?
E.g. the minimap mod would be a good example. It could be considered a huge advantage over vanilla, but not a traditional "cheat" mod.
And as far as I'm aware, it's entirely client-side
There's a case to be made that one might want to flag such a mod as "modded lobby only"
Though such decisions are obviously up to the modder
And providing the option, even if it's a niche case, isn't harmful, imo.
what about an attribute to force the modded lobby check?
I was considering that as well
That might be a good one, rather than adding an enum
@cyan flame thoughts?
Idk, that's basically what LC-API does right now and people fucking hate that feature specifically
It'd be up to the modder to declare it
I don't really agree with you, I think they're almost identical in the ways that are relevant here
I would prefer to implement a tried-and-true pattern over risking something potentially confusing and bloated
But that's my personal preference
It should be up to the lobby host, not the modder, imo
How would vanilla hosts toggle that, though
At best we could do a lobby name check and spread the word
E.g. "[Vanilla]"
Which is somewhat clunky, ngl
a vanilla host can't do any enforcement based on modlist because it has no awareness
so it is 'not enforced' in all cases
ie check disabled
the client does the enforcement, no?
Well, yeah, obviously. That's the point to providing modders an option to flag a potentially vanilla-intrusive/cheaty mod as modded-only
I think this is a difference in how you guys are implementing this vs how I would have done
There have been a lot of complaints from vanilla players about modded players joining their games. Even just "QoL" mods.
it should be both client- and (optionally) server-enforced
and this won't fix that
because those people will just not install your mod
and continue joining vanilla lobbies
If it's an attribute the mod uses, they can't use the mod, so that... fixes that?
Taking cheaters / mod modifiers into account is pointless for this argument for hopefully obvious reasons.
This would be about the common / general case
it's not an argument, it's a discussion
The argument is pointless for the discussion as well 😅
@lament perch, were you onboard with the idea, or do you think it's something we shouldn't chase?
It's fairly simple to add to the currently implemented check / system.
I don't think it's a bad idea to have some clientside mods marked as "cheaty", but actual implementation wise I'm not sure the best way
because all the following use cases should (ideally) be supported:
- people in private lobbies playing with friends who allow it
- people hosting modded public lobbies who don't allow it
- people hosting low-stakes public lobbies who do allow it
- people in vanilla who don't allow it
- people in modded (without LobbyCompatibility installed) who do allow it
I can see the argument about it being annoying (aka how LC_API has it), though at the same time - if a modder wants to flag their mod like that (which seems to be the case - considering the usage of the LC_API feature) I feel like the option should be provided?
this may be getting too complex but what about the attribute for mods and a tag for lobbies that can override it
Hm. We could add a warning with a yes/no instead.
Something akin to "You are using mods which may be considered unfair for vanilla. Are you sure you want to join this vanilla lobby?"
That might be annoying as well, though.
Yeah idk
I wish there was a way to differentiate between vanilla lobbies and people without the mod
There is
Lobby metadata
I already add a "modded" field to lobbies using this mod
Oh, whoops
Misread
fair
To my knowledge, there's no real way of checking it?
like 98% sure there isn't
You might be able to guess based on the connection erroring out
Though that's not a guarantee
The best thing we can hope for is mass adoption of this mod ✨
Which would be fairly easy for the majority of cases if most APIs add it as thunderstore dependency tbh
if we wanted an easy in-between solution, we could just have:
- cheaty attribute
- host has an "allow cheaty mod" toggle when creating a lobby
- follow LobbyCompatibility cheatymod settings
- make no attempt to disallow cheaty mods in vanilla/old mod lobbies to minimize the amount of things that break
Might veer into too niche / vague of a terrain tbh
I think we should worry about it later
Agreed
yeah
Same with the specifics of the enums, I'd rather just make Something that works and have a discussion before a proper release
The enums are simple enough to change anyway
would be nice to do some sort of polling to see how to communities feel anyways
I can toss out a poll on the unofficial community before release
extremely easy prerelease
once it's out they're probably stuck that way tho
yeah
Yeah, needs a community poll for sure
i'm curious what the jotunn devs would think
if there's anything they would change with a do-over, or if they'd just pick the same enum they've been using
would be intersting to know what they think
i also got that an unpolished version of that thing working, i just need to clean it up a bit and add an "on click" modal
we still need a diff/result datatype or something for display, whenever you get around to those patches @sharp ridge
i think the working idea is to display the first 6-8 incompatible/different mods in the hover
then show the full list in a bigger window if you click the button
I like it!
Sounds good imo
Can do
If you mean for the mod overall, I really disagree with you
but it seems you might have changed your mind
To some degree
I was referring to this
Yes, I get where you're coming from when talking about vanilla hosts
There's no way to detect this in any case. It's a moot point
I think most of those people aren't clever enough to do anything more than install the extra mod 😅 but I agree with you in those cases you listed
Anyway, let's move on / back on track
I'm saying I think mitigation for the case where extra (cheaty) mods are blatantly added would be good
how does the harmony priority system work?
want to make sure any leaderboard mod can override the leaderboard & challenge save file patches
(specifcally letting other prefix patches override the changing of parameters)
like do other prefix patches with lower priority get the original parameter or the modified parameter?
It uses priorities then whichever was first loaded for that priority afaik
say a mod has normal priority, if I want it to change the parameter before this mod's patch attempts to change it, would I want the patch to be higher or lower priority
specifically this patch checks a string to see if it either starts with a certain string or is equal to another string before modifying it
higher priority is ran sooner
I'll set it to the lowest priority then
I've most of my patches set to run last
yeah I was reading on the harmony priority system and it seems sorta finnicy in terms of what priority you want to allow other mods to have actual impact in the game
like changing the result of a function, you want the mod that impacts the game to run last
unless you only modify it slightly, then maybe you don't
though I think I understand it now
Hey, sorry for the delay. My rabbit was showing some concerning signs, and I spent the day watching her to make sure everything is okay. @lament perch @cyan flame
no rush, hope she's doing alright
btw, added a draft pr for github workflows courtesy of @dusty root
probably got some stuff wrong, hence a draft pr
do the buttons look fine rn
ye that looks good
nah it's that i found one
wait no you don't have that
the join button being orange is very bothering
you can't change?
oh is it?
think so
oh wait
I understand
you didn't change the color of challenge servers
(I haven't seen them yet)
Yeah, I think so
that's great!
you're so consistent with this typo lmao
there's only one I!!
Still keeping a close eye. Rabbits are notorious for being able to go downhill fast
I like it! Looks clean / basically as if it were vanilla tbh
But yeah, assuming she remains alright, tomorrow I should have most of the day for working on this.
sounds good, hopefully it turns out to be nothing
if it makes people go "oh hey, the dev added mod info to lobbies" we'll have done our job damn well
I hope so 😅
You'll have done your part damn well
I ain't taking any credit for that excellent UI work
the question remains if I will actually figure out a way that makes sense to display the diff lmao
who needs a spell checker
That'll be useful for a proper diff
could definitely try line-by-line coloring, might experiment with some icons too
scrollrect is probably going to be a must because some people install like 140 mods
it would be really cool to generate like, a modpack or r2modman profile code based on a lobby's diff
but there's a lot of problems that come with that, definitely not for release
just increase the resolution of the game to fit the ui
i should have just used an assetbundle 
actually i can probably just steal the lobby list's scrollrect
we're so back
damn I barely have to make any adjustments too
thats nice that zeekerss programmed the mod for you
Nice!
Bobbie, you're doing the lord's work right now
that looks good
okay, i hooked up some basic mock data, it seems to work
tomorrow I just need to:
- hook up the hover modlist
- probably discuss better category names / way of presenting them
- restructure things to be a bit less messy, and easier to hook into the real impl
not sure how much time ill have the rest of the week, need to get ready for a trip on the 20th-27th
i'm just trying to get something out before i go MIA for like ten days probably lol
the best thing I made is the random mod name generator for sure
slight critique, but it would be nice to have an incompatible mod count at the top of the modal
agreed, it needs some counters
honestly even a "total server mods"/"total incompatible mods" counter somewhere next to the player count could be nice
but i'm not sure how much space there is to add more stuff, it's pretty clean right now
either next to the player count or on the hover thing
off topic, but the bug reports here really need reorganizing
like what are you supposed to do with that
idk man
you should really be required to provide a log
if you have 120 mods either there's an exception that makes it obvious, or you're narrowing that down yourself
the L, H, and the W really make that icon
I tried to do it with (slightly) nicer handwriting
but it's hard to do that with a mouse
it is shameful how long it took me to make the odin serializer one
(their logo was a low-res png so I remade it as an SVG)
Counters would be good
@dusty root when you get the chance, can you have a quick look through this pr? want to make sure I set it up right
https://github.com/MaxWasUnavailable/LobbyCompatibility/pull/11
forgot to remove the NetcodePatch depend target from the pack thunderstore conditions -_-
will fix when I get back to my pc
Lol I missed that
I've opened a GitHub review
kk thanks
what do we want the thunderstore team name to be?
https://github.com/MaxWasUnavailable/LobbyCompatibility/pull/11#discussion_r1451578509
idk, i just want something that ideally at least 2 people would have access to
ideally something we could add people to without worrying about other packages
could just use LethalAPI maybe
LobbyLobbers, LobbyLovers, CompatibilityCrew, something short and utilitarian like LCTeam
BMX would be funny as an acronym of our names but idk if that's too self-indulgent
i also dunno if it's like, trademarked or taken
doesn't trademark only apply to things in the same economy, as in like dirt bikes?
also it wasn't taken
wanna go with BMX? lol
there's some with BMX & other words in the name for software, but nothing i'm worried about
i actually don't even think it's trademarked for dirt bikes i think it's a generic term
i think it's funny
if nobody objects i'm down with that
@sharp ridge ^
If y'all want to do future projects together, I'd be down for that 
ye
Okay I think the github workflow is complete and ready to be reviewed/merged
https://github.com/MaxWasUnavailable/LobbyCompatibility/pull/11
Yup!
probably a future thing, it could be worth looking into adding translation
i don't expect there to be like
more than 10 strings of text
Should be simple enough
that would be nice
honestly would be cool to have a localization api
but that's a whole 'nother project
agreed
Someone tried that at some point - never finished it though afaik?
I might very well be wrong
LocalCompany?
good name
i don't think i've heard of any localization API for mods at least
the subtitles API is probably the closest thing
which does have mandarin localization iirc
yeah that's what I was thinking of
but yeah shouldn't be too hard, just a lot of transpiling
and stuff with terminal commands
XUnity.AutoTranslator is already pretty good for game localization tbh
i'm not sure what all it works with (mod strings might not be translated?), i haven't used it recently
even just something mod authors can use would be nice tho
something like cs LocalizationString { EN: "X" FR: "Y" CZ: "Z" };
that helps handle switching language/autosetting based on system settings
for the mods that have very few strings (most of them tbh) and can find people for translating
A json file or something that could be read could be cool
so all translators have to do is make that file
That's how it works in Minecraft, yeah
and also uhhhhhh intelliJ platform plugins
I think langfiles being JSON is kind of widespread
you saying my idea isn't original? 
perhaps not original, but it has merit simply because it's not original 🤣
potentially something for our thing
lol
your... thing?
our thing

What's the thing?
we haven't really told anyone about it yet
That's the question, ain't it
the thing 
what journey do I have to depart on to locate this marvelous thing?
mine or steven's commit history
just don't go shouting it to the heavens pls 🤣
WIP
yea
I feel ya
it's also impossible to fix the other one
can I contribute to your... thing?
of course
honestly don't even know what to contribute lol
will say, like the name
I kinda hope to convince all the big ones to either join up in the org or give us permission to integrate, so we can actually get the stuff in the same place. Lobby compatibility included lol
(btw recommend OdinSerializer, it's amazing)
remember when I said 'i have a good idea' and then didn't say it here? That was it 🤣
ye I remember
glad you're enjoying :)
Speaking of that @dense forum I think the best thing for now is to have odinserializer DLL in the repo
which we are allowed to do
Where should I put the ref tho?
base dir?
make a libs folder
in root dir
and the ref should be like
$(ProjectDir)../libs/OdinSerializer.dll
^ hint path
Guys, maybe move this elsewhere
😈
(sure)
idk where tho
so we'll continue this another time lmao
oh yeah, i'm fine with LGPL if y'all are @sharp ridge @lament perch
btw, any thoughts on making errors a bit brighter so it's easier to tell them apart? i do kind of like the cleaner first look
something like the second is probably more useful tho
hmm, i was racking my head trying to think of a way to like
add a filtering option that ONLY filters compatible lobbies
without destroying the steam servers, and ideally doing it all in one query of SteamMatchmaking.LobbyList.WithKeyValue(x)
so it can get as many lobbies as possible in the filtering phase, instead of just getting 50, 45 of which are invalid
and it's kind of stupid, but I think you could just add a key/value with all installed required mods?
specifically lowered to the lowest compatible version, and sorted alphabetically
as an additional lobbydata field to the normal modlist
eg, say you have the following required mods installed
LethalLib-2.0.0
LethalThings-1.0.0
MoreScreams-1.2.0
LateCompany-1.5.1
you could, from the side of both the client and lobby:
- get all required mods
- sort that list alphabetically
- truncate the version to the OLDEST compatible version
lobby.SetData("LethalLib-2.0.0,LethalThings-1.0.0,MoreScreams-1.2.0,LateCompany-1.50", "Dummy");```
so in theory, if you do WithKeyValue with your modlist, it will always only return valid lobbies
with the single key check
the max key/value limit is 255 (i think, not sure about value)
which isn't a ton, but like
instead of passing the entire plugin names directly, what if you hash everything (so it's consistent between clients)
you don't need a long hash either, just something "good enough" to avoid most collisions and it would work in 99.9% of cases
LateCompany-1.5.1 => LateCompany-1.5.0 // Truncate to lowest compatible version so all compatible versions will match
LateCompany-1.5.0 => ba271d80 // Convert to some hash - CRC32, for example
// Sort all hashes
a87abcde, ba271d80, c82ab7312
// Convert into one big string we can publish
lobby.SetData("a87abcdeba271d80c82ab7312", "Dummy");
// This key should be present for anybody who has EXACTLY those required mods, as they've gone through the same process
// Should be able to just add the following to clients matchmaking
LobbyList.WithKeyValue("a87abcdeba271d80c82ab7312", "Dummy");
obviously it's kind of flawed, but I think it would work well enough?
i don't think you even need 8 characters per hash too, it might be fine with less
6 characters would be support for checking up to 42 required mods within the character limit
and realistically, most people will be running like 1-20 required mods
instead of showing 50 (almost certainly all) incompatible lobbies, it would give a chance to find exact matches first, then fallback to normal matchmaking
i guess you could also just hash the entire required mods list at once, idk why i didn't think of that first lmfao
anyways, weird backwards 2am thought process disregarded, we should just do something like this
private string GetModsHash(List<PluginInfoRecord> plugins)
{
using SHA256 sha256 = SHA256.Create();
string mods = "";
// Use only mods 100% required by both server and client
foreach (var plugin in plugins.Where(x => x.CompatibilityLevel == CompatibilityLevel.Everyone).OrderBy(x => x.GUID))
{
mods += plugin.GUID;
mods += plugin.MinimumVersion; // Calculate minimum required version
}
return sha256.ComputeHash(Encoding.UTF8.GetBytes(mods)).ToString();
}
private void DoMatchmaking()
{
string hash = GetModsHash(plugins);
SteamMatchmaking.LobbyList.FilterDistanceWorldwide().WithKeyValue("JoinableModded", "true").WithKeyValue("ModsHash", hash);
// First, attempt to load lobbies from this first query
// If there's more than 50 returned, great! Just use those
// We still need to run all the normal compatibility checks with the real modlist, but this should drastically reduce the amount of invalid lobbies returned
// Namely sorting ServerOnly/ClientOptional
// If zero or few lobbies remain, get the normal list and append that.
// These will likely not be as compatible, but some people WILL have zero exact lobby matches
// In base game, normally up to 50(?) lobbies can be shown
SteamMatchmaking.LobbyList.FilterDistanceWorldwide().WithKeyValue("JoinableModded", "true");
}```
we'll still need the modlist networked normally ofc, but this will SIGNIFICANTLY improve amounts of compatible lobbies in the matchmaking filter phase
This lol, but use a longer hash (32-40 characters)
looks like you did :)
ye, sha-256 should work just fine
sha256 in utf8 encoding should be exactly 32 characters actually haha
Second one is better imo. But would poll some random users.
Might be able to make a hash of the entire string?
Actually, nvm, I see the flaw in that
Nah that would work fine I think
when it comes to compatibility.everyone you can, and I think that'll be most mods anyway, so what Bobbie's written there gives a good heuristic for 'probably compatible lobbies' and then you can apply the other network compatibility filters later
Might be weird with e.g. the 4th type of mod. Where it's not required by clients, but is required by the server if the client has it
I feel like there's other cases I might be missing
Oh yeah it's not perfect at all
but it's great as a heuristic
It would just be a loose first filter though
I wouldn't be surprised if there's hundreds or thousands of modded lobbies, and as-is the compatibility stuff will be great
But it's still randomly picking 50
so it would remove a little bit of randomness
That's fair. I'll add a method to the PluginHelper to get a hash of all mods required by everyone?
I'll disregard the 4th type for this check

sounds good, I would only add mods that are 100% guaranteed to be both required on server and every client
Hm
so just everyone would work
Yeah, sounds like a safe bet
Are there any plans for config syncing or some sort of config difference checking? Like when you have all the required mods and try to join the lobby it will display all the differences in the configs and then there could be a button "Sync & Restart" (since it would probably require a full game restart to work properly), maybe there could be a checkbox on each mod/setting to allow them to ignore specific settings (if something is client side only, like keybinds).
For my own personal plugin I run with my friends I have a code on the main menu (e.g CODE: leaf weather invite) which is a checksum for 1. plugin names and versions, 2. plugin config key values, 3. hash of the DLL files (the plan was to include AssetBundles in this but I didn't get around to it, the only reason I didn't do all files is because UnityExplorer had log files in it so I don't know what other mods might put there). It's based on the BIP39 word list so I am only using the first 11 bits (2048) of the hash for each of the words but I think it's significant enough for our use-case.
I just did something like this for the config (to give you an example of how to get the config values)
private static string GetConfigString()
{
StringBuilder result = new StringBuilder();
foreach (BepInEx.PluginInfo pluginInfo in Chainloader.PluginInfos.Values)
{
result.Append($"{pluginInfo.Metadata.GUID}@{pluginInfo.Metadata.Version}\n");
ConfigFile config = pluginInfo.Instance.Config;
foreach (ConfigDefinition configDefinition in config.Keys)
{
ConfigEntryBase configEntry = config[configDefinition];
result.Append($"\t[{configDefinition.Section}] [{configEntry.SettingType}] {configDefinition.Key}={configEntry.BoxedValue}\n");
}
}
return result.ToString();
}
Sorry if this has been discussed before or if there's some limitation which makes this problematic, I only noticed this thread yesterday and Discord doesn't let me search messages in specific threads
I think something like this would definitely be nice, I'm not sure it's in scope for this library specifically
at least for now
It would be a ton more data to network, and I think defining specific configs/config entries that need to be synced would be a little more complicated
I don't wanna make things too complicated to start, because it'll probably take another two months if I added everything I wanted
I think network config syncing is much more in-scope of 'The Thing'
I have an idea of how to do it
that would be much more useful than the existing config syncing solutions
[Info : BepInEx] Loading [LobbyCompatibility 0.0.1]
[Debug :LobbyCompatibility] Patching...
[Debug :LobbyCompatibility] Patched!
[Info :LobbyCompatibility] Plugin LobbyCompatibility is loaded!
[Info : BepInEx] Loading [LobbyCompatibilityTest 0.0.1]
[Info :LobbyCompatibilityTest] Plugin LobbyCompatibilityTest is loaded!
[Message: BepInEx] Chainloader startup complete
[Debug :LobbyCompatibility] Plugin metadata: [{"GUID":"LobbyCompatibilityTest","Version":"0.0.1","CompatibilityLevel":2,"VersionStrictness":1}]
[Debug :LobbyCompatibility] Lobby plugins: [{"GUID":"LobbyCompatibilityTest","Version":"0.0.1","CompatibilityLevel":2,"VersionStrictness":1}]
[Warning:LobbyCompatibility] You are hosting a lobby with required plugins. Disabling vanilla clients from joining.
@cyan flame @lament perch
Patches work 👌

so, I think I'm gonna (try and) change the target framework to both netstandard2.1 and net4.7.2 specifically for the nuget package
to increase support
Am I good to PR these patches?
Or do we want to change anything
Still need to verify vanilla blocking & modded joining works, if someone can help me test that tomorrow (or if you two can run a couple of tests rq on that branch)
@lament perch @cyan flame
I'm at the fitness rn, and will then be out of the house for the rest of the day
So I can test tomorrow at the earliest
Go for it
idk if I'll have the opportunity to test until I'm back from my trip on the 27th
I'll be on tomorrow
might be worth wrangling some other folk from #dev-general
We can test tomorrow, then
just @ me when you're on tomorrow
Will do
Should hopefully just be testing 5 scenarios.
Vanilla x Modded
Modded x Modded (not matching mods)
Modded x Modded (not matching mod versions)
Modded x Modded (matching)
Modded x Vanilla
I'll be on in a little
okay I'm on @sharp ridge
@cyan flame just tested with @lament perch. Confirmed it works in all of the above cases
Please review
i made a PR for the UI changes @sharp ridge @lament perch https://github.com/MaxWasUnavailable/LobbyCompatibility/pull/15
if anything needs to change lmk, i will probably be unavailable for the next ~10 days
Hoping to be ready for release by the end of the month, I don't think there's much left to be done besides the diff bridging
and a basic implementation of that hashing & lobby type filtering
right before release, i think we have the following to do also:
- take a second look at the enum names (i'm honestly fine just 1:1 matching jotunn, we should talk to the devs of that and see if they'd make any changes if they had a do-over)
- take a second look at the final name (is LobbyCompatibility good? or do we want to make it more generic/expandible)
- somebody will need to write a snippet (to put on the README/wiki/announcements) going over how to add support to your mod
i'll make a few issues of things i've been wanting to fix, incase any of y'all have time to work on them (and so they don't get wiped from my memory)
once the leaderboard and UI changes are merged, most of them should be very simple fixes
Sounds good! I can toss my example mod up as well.
I obviously have not contributed to this whatsoever so I have no particular right to a say 😅 but I think asking the jotunn devs about that is a great idea
can somebody get in contact with the jotunn devs while i'm gone
ideally slap em in here and see if they have opinions on the enums / the idea of just using the ones directly from jotunn
Margmas
I think the names are better than in Jotunn, at least the shorter names feel nicer. Although ServerOnly implies it can't be installed on the client and vice versa, maybe something like ServerManaged would fit better?
Otherwise I don't see any mayor cases missing, they are a bit different arranged but somehow the 4 have nearly the same effect than the 5 in Jotunn. Well guess VersionCheckOnly isn't there, but that's rarely used anyway
I'm not sure how lethal company networking is playing out tho, I guess it's not as bad as in Valheim? Rather the host manages all behaviour?
hmm, are there even servers? Host might be a better name if that's not the case
Nah. Server is fine
But the enum is rather flexible anyway, adding a case or even renaming existing levels is easy like it was done in Jotunn
Overall I think it's a good system. For example ServerSync has implemented it a bit different with a minimum required version but that isn't as nice, since you have to keep updating the minimum version separately when only the minor version is synced etc. Or build some concatinated strings, where your version is split into multiple variables
binding version strictness to the semantic versioning feels natural
(all the text is from him)
@lament perch Margmas answered (MSchmoecker)
tell them thanks!
Ey, we did a good job 😄
@lament perch @cyan flame
Thoughts on what format is best?
- MyMod—1.0.0 (Need 1.1.0)
- MyMod-1.0.0 (Need 1.1.0)
- MyMod (1.0.0 — need 1.1.0)
- MyMod (1.0.0 — 1.1.0)
- MyMod (1.0.0) — need 1.1.0
- MyMod (1.0.0) — 1.1.0
Note the difference between em dashes & normal hyphens
I like that last one
Perhaps changing "is" to "was"?
Since it might imply less of a "global" requirement
Some users might get confused otherwise
maybe yeah
yep
it'd be even better to show
- MyMod-1.0.0 — v1.1.0 is Required to Connect
but that gets too long I think
- MyMod-1.0.0 — v1.0.0 was required
How's this?
I think that works
We can always change it after some testing. It's just a string after all.
Currently refactoring some of Bobbie's code
ye
Changed the models to records, moving some behaviour out of it
if there's anything that needs to be done, just lmk
Adding docstrings
Hm
You could start writing up a wiki article?
The basic concept / guide should remain the same, regardless of pre-release changes
Modders add the attribute
Users can see the diff & whether a lobby is compatible or not
New PluginDiff version 🙏
using System;
using LobbyCompatibility.Enums;
using UnityEngine;
// ReSharper disable InconsistentNaming
namespace LobbyCompatibility.Models;
/// <summary>
/// Diff between two plugins.
/// </summary>
/// <param name="PluginDiffResult"> The compatibility result of the plugin. </param>
/// <param name="GUID"> The GUID of the plugin. </param>
/// <param name="Version"> The version of the plugin. </param>
/// <param name="RequiredVersion"> The required version of the plugin (null if not required) </param>
public record PluginDiff(
PluginDiffResult PluginDiffResult,
string GUID,
Version Version,
Version? RequiredVersion)
{
/// <summary>
/// The color of the text to display for this plugin.
/// </summary>
public Color TextColor
{
get
{
return PluginDiffResult switch
{
PluginDiffResult.Compatible => Color.green,
PluginDiffResult.ClientMissingMod or PluginDiffResult.ServerMissingMod => Color.red,
PluginDiffResult.ClientModOutdated or PluginDiffResult.ServerModOutdated => Color.yellow,
_ => Color.gray
};
}
}
/// <summary>
/// The text to display for this plugin in the UI.
/// </summary>
public string DisplayText
{
get
{
var name = $"{GUID}-{Version}";
if (RequiredVersion != null)
name += $" — v{RequiredVersion} was required";
return name;
}
}
}
Old:
using LobbyCompatibility.Enums;
using System;
using UnityEngine;
namespace LobbyCompatibility.Models
{
// just my temporarily implementation of how a diff *could* look
// done to get UI logic in place
// should be easy to swap out with a new/better impl if needed
public class PluginDiff
{
public CompatibilityResult CompatibilityResult { get; }
public bool Required { get; }
public string Name { get; }
public Version Version { get; }
public Version? RequiredVersion { get; } // only applicable when ServerModOutdated / ClientModOutdated
public string NameAndVersion => $"{Name}-{Version}";
// Used for compatibility colors in modlist UI
public Color TextColor
{
get
{
// Nice and bright green if we're compatible
if (CompatibilityResult == CompatibilityResult.Compatible)
return Color.green;
// Red if we're required and not compatible
if (Required)
return Color.red;
// Gray if it's not required, but also not compatible
return Color.gray;
}
}
// Display a "Need (version)" prompt for version conflicts in proper full modlist
public string DisplayName
{
get
{
var name = $"{Name}-{Version}";
// Add the required version to version-based conflicts
if ((CompatibilityResult == CompatibilityResult.ServerModOutdated || CompatibilityResult == CompatibilityResult.ClientModOutdated) && RequiredVersion != null)
{
name += $" (Need {RequiredVersion})";
}
return name;
}
}
public PluginDiff(CompatibilityResult compatibilityResult, bool required, string name, Version version, Version? requiredVersion = null)
{
CompatibilityResult = compatibilityResult;
Required = required;
Name = name;
Version = version;
RequiredVersion = requiredVersion;
}
}
}
Whoops, gotta finish the params
I added yellow as outdated plugin colour, compared to red for missing plugins
Figure it's easier on the eyes to skim through what mods you're missing vs what mods just need an update, rather than both of those cases having the same colour
Do we actually want TextColor and DisplayText as properties? A "GetTextColour" and "GetDisplayText" might be cleaner? 
Thoughts? @lament perch
I don't really see the point in switching, but if you think it looks cleaner go ahead
I do think the colors should be configurable for colorblind people though
like a config with Outdated, Missing, and Compatible as color options
@sharp ridge btw can you merge pwetty pwease? 
https://github.com/LethalCompany/ModdingWiki/pull/82
using past tense seems a bit silly for something the user sees immediately or does it not show immediately
It implies that version was required for the connection attempt that just happened
ah
Rather than that version being required, period
Since some lobbies might run different plugin versions, in some rare instances. Less tech-savvy users might think that specific version would be required universally or whatnot
It's minor semantics, but I think it might help prevent situations where a user gets confused tbh
Does this add a build test on push?
what does the current attribute look like?
using System;
using LobbyCompatibility.Enums;
namespace LobbyCompatibility.Attributes;
/// <summary>
/// Specifies the compatibility of a plugin.
/// </summary>
[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, VersionStrictness versionStrictness)
{
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; }
}
kk thanks
(Removed the example tag, so Discord's formatting doesn't break)
An example would be
[LobbyCompatibilityAttribute(CompatibilityLevel.ServerOnly, VersionStrictness.Minor)]
class MyPlugin : BaseUnityPlugin
{
}
so, are we going with CompatibilityLevel.Everyone or CompatibilityLevel.EveryoneMustHave?
also what do we think on the comments we were given?
examples of Forge's display (the 2nd isn't a good example since they should be compatible according to semver)
Seems like they think we've done a better job overall, so I think our system is fine? ServerOnly vs ServerManaged is a fair discussion to have - though I feel like (considering the enums will be presented towards modders only) it's clear enough / still correct?
Everyone seems alright to me, personally
@lament perch Could you document the lobby metadata format as well?
Just in case other libraries decide they want to do their own thing, at least they'll hopefully follow our standard - so multiple implementations can remain compatible
I'll look into it
Nearly done with the refactor & tying the diff calculation to the system 🙏
Will this mod require the host to have it too or just me?
to get mod info from the lobby, the host will need it, but otherwise, it's entirely client-side
Quick update: mod appears to be functional. We're in the polish & testing phase.
I'm fairly sure the loadLobbyListAndFilter patch is incompatible with MoreCompany (since MoreCompany overwrites LoadServerList which results in the loadLobbyListAndFilter enumerator never being ran)
that mod is just a neverending pain of incompatibility
the github for it hasn't even been updated since v1.7.2 (it's on v1.7.6 now)
There's a few things BiggerLobby patches that literally has 0 changes to the base game code too 😂
But yeah morecompany will likely conflict with the current version of the lobby compatibility mod
I think we'd have to patch the method he uses
to add the coroutine
do not look at what I've said in dev general
Tbf I might just dm him and ask him to update the GitHub
Tbf I'll probably just do a PR to fix it when/if he updates the repo since it's also the cause for one of my mods being incompatible with MoreCompany
so you are saying it's compatible with biggerlobbies? as that's what I use
i'm pretty sure that patch is a fairly old version of that code
so it'll probably have to be overridden
yeah, it's before the tag system
i don't think that patch changes a ton right now. so we could probably just disable it
and re-add the "add increased max player amount to UI" bit ourselves
or just ask them to change the patch
i've already changed the patch on morecompany to a postfix instead, just need to do a PR once i fix the errors i caused by trying to clean up a bunch of the patches they do
did you switch it to use transpilers ooc
ye
nice
@cyan flame time to rename this thread, I think 
[WIP] LobbyCompatibility
Much appreciated! 😄
Does anyone know when this mod will be released? This will be by far the best mod for the game
It still currently is being worked on, but we are nearing private testing & then release soon after if all goes well
Just a heads up
https://github.com/MaxWasUnavailable/LobbyCompatibility/blob/master/LobbyCompatibility/Patches/LoadLobbyListAndFilterTranspiler.cs#L22
the transpiler here gives me the server list Container (SteamLobbyManager) instead of the specified LobbySlot which InitializeLobbySlot needs
the compiler is prob doing some wonky op::implicit bs to make this happen
👀 did I fuck up again?
wait wdym?
When I yoinked the code here somehow the argument for private static void InitializeLobbySlot(LobbySlot lobbySlot, Transform levelListContainer) LobbySlot lobbySlot was returning the steamlobbymanager
since Ldloc_1 isn't the lobby slot
its the steamlobbymanager
But isnt that what gets passed into InitializeLobbySlots first arg
the lobby slot from the dup?
Ohh wait I think I may have misunderstood..?
does
new CodeInstruction(OpCodes.Ldloc_1),
new CodeInstruction(OpCodes.Ldfld, levelListContainerField),```
complete `SteamLobbyManager.levelListContainer`?
On my code I removed `new CodeInstruction(OpCodes.Ldfld, levelListContainerField)` since I had no need for it. Causing SteamLobbyManager to be passed in as `InitializeLobbySlot(SteamLobbyManager_var)` instead of `InitializeLobbySlot(LobbySlot_var)`
hold on let me load up dnspy lol
the ldfld uses up the ldloc_1
That would make so much sense lol
removing ldfld would then mean the localvar at index 1 is passed instead of the container
(which the localvar is steamLobbyManager)
My mistake lol, mb for the false report
Yeah that clears it up. I honestly thought the stack for levelListContainerField would already be completed
transpilers 🗿
nah, it needs an instance to reference from
(since the field isn't static)
I've spent so much time in transpilers recently I'm getting pretty well-versed in them
We are just about in the demo/testing phase now before official roll-out. I (and others) will be pinging people we believe would be interested in being part of this phase - as well as those we'd be interested in having be part of this phase. If you aren't interested, no problem!
If you are not part of these mentions but are interested, feel free to reach out! We will gladly include those interested in participating in this initial "closed" test.
We just want to start getting this out there being tested to make sure any potential quirks are ironed out before releasing to the public, as well as have a smooth roll-out.
We'd also be glad to answer any questions you may have, and we ideally will have a 24/7 server up soon that you can test against with your mod. We will also have the pre-release NuGet package up that you can use to reference.
README for Usage Reference:
https://github.com/MaxWasUnavailable/LobbyCompatibility?tab=readme-ov-file#for-developers
do tell more 👀
lmao got timed out with that silent ping xd
@lament perch sprung ya from jail
🙏
send him to the stairs instead
reason for the pings? i can ping em for ya if its fine
Five's the limit btw
just people that I believe would be interested or are interested in getting them into testing phase @lusty linden
I (and others) will be pinging people we believe would be interested in being part of this phase - as well as those we'd be interested in having be part of this phase. If you aren't interested, no problem!
good to know
@dusty root @bright tinsel @coarse grove @tame hill
@atomic sentinel @silver flicker
refresh me a little
what attributes do I have to throw in my plugin
?
also the random ping without a big explanation paragraph was probably a bad idea :P
sorry, not quite out yet (will be within next day), but do have the readme:
https://github.com/MaxWasUnavailable/LobbyCompatibility?tab=readme-ov-file#for-developers
Essentially is an attribute or register method modders can use to mark their mod as whether only clients need, only server needs (but clients are allowed), everyone needs, or server needs it if the client has it
as well as version strictness
The mod/lib will then take that and allow/prevent the client from joining servers based off that information.
It'll only block joining if the server has a mod everyone needs while the client doesn't or if there are version mismatches outside of any mod's allowed strictness - specific to that mod, as well as a few other cases - but not any that would prevent mass adoption (there's more to that, I'll explain at another point).
thought you were interested (I hope I got the right person) #1202702876930220122 message
So I take it you pinged us so we can test it?
Oh I forgot about that.
I'm still interested in testing it.
Essentially; though specifically for you I thought you could be interested in the other part of the mod/lib (currently undocumented - will sort that out tn), that allows you to get the mod list/diff of the server & client
Oh yeah 100%
Down to implement this but
-
All LLL dev is on hold given the current situation going on
-
For it to be useful for LLL, I think I would need some sort of function to register AssetBundles to compare against.
(AssetBundles should probably be directly supported anyway but also might be cool in the future for mods to somehow optionally create a data structure of a provided type and implement a function that's meant to populate them for comparison)
- Understandable
- It is planned, though not currently implemented as we wanted to get this released sooner than later
Forsure and no rush, Just explaining why I might not test this just yet 👍
yeye no worries
in a perfect world ig it would be nice if i could do something like
LobbyCompatability.RegisterComparableDataSet<T[] dataSet>(); or something
then for example it would be generic enough for to use this to check if client and host both have the same moons and dungeons loaded and their set to be registered in their configs (cuz in LLL you can potentially have content installed but not enabled)
Also worth noting if necessary I could totally work with just value types and atleast for my usecase it would just be determining if the two datasets are identical, no other further details
It honestly shouldn't be too hard to at least provide the dataset in the lobby metadata - though we likely will look into doing a bit more than that
yet another brilliant use for none other than
odin serializer
/j
has to be string :(
(newtonsoft.json works fine in this case)
can't you just
'hee har these bytes are a string'
like, as long as 'encode' it with an encoding that has 256 codepoints, each codepoint being 1 byte
MyType whatever = null!;
var someBytes = SerializationUtility.SerializeValue(whatever, DataFormat.Binary);
var muahahaIsString = Encoding.Latin1.GetString(someBytes);
var notStringAnyMore = Encoding.Latin1.GetBytes(muahahaIsString);
MyType whateverAgain = SerializationUtility.DeserializeValue<MyType>(notStringAnyMore, DataFormat.Binary);
@lament perch Checkmate
(Latin1 refers to ISO-8859-1, which is an 8-bit = 1-byte encoding using exactly 256 codepoints)
you got me 💀 (but people will complain about additional deps & Newtonsoft.Json works just fine so
)
yeah hehe
nice to know tho
we already have a checksum, shouldn't be too hard to add to that/replicate that
maybe just don't complicate it then
don't do the whole dataset thing
let modders register content hashes
then the modder is free to use whatever they want to generate the hashcode for their mod's content
serialize to bytes with [insert library of choice] then MD5sum? plain HashCode implementations? they can do whatever they want
you don't need to care
(In my case I don't really know what I would even want in that regard so i would still prefer the option to send a data set to you and let you do it for me)
you're being braindead batby
all they are going to do for you is hash the dataset and compare the hashes
you can hash it yourself and they can compare the hashes for you
I mean, we could add (required) content info to this checksum & other stuff elsewhere for additional checking
https://github.com/MaxWasUnavailable/LobbyCompatibility/blob/master/LobbyCompatibility/Features/PluginHelper.cs#L249
it's infinitely more flexible
yes i know
👍
so what were you complaining about ? i'm confused
you could just use 0 as a default if people provide no value
It wasn't a complaint. I think having the option to directly provide the hash makes a lot of sense for people who want to do that, But I (and I think a lot of people who may have fairly light experience in C#) find it a lot easier and readable to just pass in a data set and let LobbyCompatability do the work if were not picky on how the sausage is being made.
the only people who are going to be using the feature are content-registration API authors
that is an extreme minority
it doesn't make sense for them to waste time implementing a dataset comparator
tbf it'd honestly just come out as a facade mod with the CompatibilityLevel.Everyone enum
it would take you 3 minutes to google 'how to make a checksum for a list in c#' and implement it
Not to be rude but like, I kinda was asking Xilo. If they don't wanna implement it they don't have to.
a bit hacky but does the trick 😂
we'd have to represent it in the mod list somehow 
I preferred your previous suggestion - i.e. every mod has a content checksum, 0 is the default and means 'nothing to check'
but yeah either or
if you're saying 'not to be rude' you should probably just not say it at all lmao
either be rude straight up, or don't say it
just want to be careful adding more and more checksums as doing so would begin to clog up steam's lobby metadata
Aye I get you
#1194412635932856440 message
also @coarse grove i gotta make sure you know i didn't mean it when i said you're being braindead, you're a smart guy & i love your ideas
Ok? don't aggressively answer questions on behalf of someone else. Xilo @'d me for testing so I gave my thoughts on the matter, They can do whatever they want with those thoughts. It's really weird to call me braindead and tell me I can just google how to do it myself when i'm just the giving feedback I thought I was meant to be giving. I know I can do it myself, that's not the point of the feedback.
i'm sorry you misinterpreted my enthusiasm as aggression, it's a pretty common thing
i'm not good at making it obvious which i'm being
I know we chill
i certainly have asked before, adding the attribute means that whoever has the mod needs lobby compat as well, right?
doom music fades
yee the feedback is great, I could just see the scope-creep phasing into existence and went into 'shut that shit down' mode 😂
Yes, but we have added a register method that you can use if you want to use it as a soft dependency
ah, sweet
trauma response to working w/ clients directly 
i might put it on my freecammer mod since that seems like something people may want to know if the lobby host is using
you guys are getting clients?
also weird question @dusty root, if you add an attribute but the runtime doesn't have the reference to the assembly where the attribute comes from, does it error?
I was about to say that - i'm pretty sure the attribute way is actually the softdep way
the RegisterPlugin would require you to do some dynamic stuff i think
because that's how uhhh
Willis' config mod
supports softdeps
(attributes )
lmao cool
mfw #if has assembly LobbyCompat B)
register method is still cool for registering multiple things (say for content libs)
[BepInDependency("com.willis.lc.lethalsettings", BepInDependency.DependencyFlags.SoftDependency)]?
so in order to use it as a soft dep, it'd be the attribute or the method?
want to be sure
alright
Yeah actually
I think it must be the attribute
because
i can use CodeAnalysis attributes
and the runtime doesn't care
I think
you sure? currently seems like it does:
[Info : BepInEx] Loading [LethalCompanyHarmonyModTemplate1 1.0.0]
[Error : Unity Log] Couldn't extract exception string from exception of type FileNotFoundException (another exception of class 'FileNotFoundException' was thrown while processing the stack trace)
[Error : Unity Log]
Stack trace:
[Error : Unity Log] Couldn't extract exception string from exception of type NullReferenceException (another exception of class 'FileNotFoundException' was thrown while processing the stack trace)
[Error : Unity Log]
Stack trace:
(works with the assembly in question being loaded, doesn't when it isn't)
This wiki contains information and other resources to aid with modding Risk of Rain 2.
im like 90% sure thats at compile time
it is afaik
like #if CPU x86
really? :(
@dusty root you lied to me about attributes
:(
always need to reference
is not the same as 'always need to be present at runtime'
or have i read the wrong part
I just don't understand how my mods are working with annotations from CodeAnalysis and stuff
at runtime
well I tried it with lobby compat and it didn't work
[BepInDependency("lobby company oibnlit or whatever the modguuid is", BepInDependency.DependencyFlags.SoftDependency)]
class MyMod : BaseUnity Plugin {
// ...
if(BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey("modguid")) {
// register it here
}
}
would this not work? its what ive been doing for soft dependeencies is there a better way?
maybe CodeAnalysis is auto-loaded from dotnet (if I got that right)
no it doesn't
because you'll get a TypeLoadException
This wiki contains information and other resources to aid with modding Risk of Rain 2.
Unless you LoadAssembly(satellite) inside the if
essentially you need
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void SomeMethodThatRequireTheDependencyToBeHere()
{
// stuff that require the dependency to be loaded
}
afaik
me omw to repackage lobbycompat in all of my mods:
this has worked for me for ages?
the MethodImpl annotations are quite important haha
Is that function in your plugin class too?
or a separate type?
the second two are in my config class
yeahhhhhh
so it's basically what Xilo linked
Which is a good way of doing it
just.. long
@lament perch I wonder whether putting the attribute on a type unrelated to Plugin works
cos bepinex is gonna try and load the Plugin class right
... not for our current implementation
maybe...
something like uhh
wait would assembly attributes work
ugh that would be a pain to investigate holy shit
I think this would definitely work
[Compatibility(typeof(Plugin), ...)]
public static class PluginCompatibility { }
but obvs it would be cleaner if the class could be omitted
so just
[assembly:Compatibility(typeof(Plugin), ...)]
@lament perch I'd love to test the mod rn but I just finished doing some stuff for my mod and it's 3 am for me, I'll do it later when I wake up. I'll let you know how it goes through here
np at all, it'll be a few days at least until offical release
[Info : BepInEx] Loading [LethalNetworkAPI 2.1.7]
[Warning: HarmonyX] AccessTools.GetTypesFromAssembly: assembly LethalNetworkAPI, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null => System.Reflection.ReflectionTypeLoadException: Exception of type 'System.Reflection.ReflectionTypeLoadException' was thrown.
Could not load file or assembly 'OdinSerializer, Version=2022.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.
Could not load file or assembly 'OdinSerializer, Version=2022.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.
at (wrapper managed-to-native) System.Reflection.Assembly.GetTypes(System.Reflection.Assembly,bool)
at System.Reflection.Assembly.GetTypes () [0x00000] in <787acc3c9a4c471ba7d971300105af24>:0
at HarmonyLib.AccessTools.GetTypesFromAssembly (System.Reflection.Assembly assembly) [0x0000d] in <474744d65d8e460fa08cd5fd82b5d65f>:0
System.IO.FileNotFoundException: Could not load file or assembly 'OdinSerializer, Version=2022.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.
File name: 'OdinSerializer, Version=2022.0.0.0, Culture=neutral, PublicKeyToken=null'
System.IO.FileNotFoundException: Could not load file or assembly 'OdinSerializer, Version=2022.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.
File name: 'OdinSerializer, Version=2022.0.0.0, Culture=neutral, PublicKeyToken=null'
[Info :LethalNetworkAPI] LethalNetworkAPI v2.1.7 has Loaded.
warning, but works
though that may be unrelated xd
maybe a fresh project to be sure?
cos yeah, those errors could just be cos your network api does actually use odin 😂
fine ಥ_ಥ
big effort tho 😭
you do not have to 😭
not with template :)
i almost didn't suggest it (testing on fresh project)
and then i thought 'nah, he'll love the chance to use his template'
😂
already did it once
.
wait what
oh lmao 😂
does in fact seem to work
[Info : BepInEx] Loading [LethalCompanyHarmonyModTemplate1 1.0.0]
[Debug :LethalCompanyHarmonyModTemplate1] Patching...
[Debug :LethalCompanyHarmonyModTemplate1] Finished patching!
[Info :LethalCompanyHarmonyModTemplate1] LethalCompanyHarmonyModTemplate1 v1.0.0 has loaded!
[assembly: RegisterFormatter(typeof(LethalCompanyHarmonyModTemplate1.LethalCompanyHarmonyModTemplate1))]
namespace LethalCompanyHarmonyModTemplate1;
[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
public class LethalCompanyHarmonyModTemplate1 : BaseUnityPlugin
(will still keep the register method as it's still useful in some cases ahem content libs ahem)
ahem harmony ahem
oh
you mean that
technically a new dungeon/moon would be a mod imo
but I see the reasoning in other cases
My tired 4AM brain has been staring at this for like, 2 minutes
What?
Also, yeah, if anyone has feedback / questions / etc... - you can also ping me ✨
random testing junk
Bobbie too, technically, but they're recovering from being dead
While I have almost recovered
(didn't rename my template, so that's what it came out as)
And Xilo still has no excuse for not testing the ModData library
(/j)
the point of it is that that (assembly) attribute will not prevent a Plugin type from being loaded by BepInEx if the lobby compat DLL is not present
It still contains the info you need in the typeof(Plugin) parameter
Interesting
ye the reason it fails currently is cause the class/type is loaded in
The confusing part there is that RegisterFormatter is an odin serializer one
that xilo was using for quick testing
not to mention LethalCompanyHarmonyModTemplate1.LethalCompanyHarmonyModTemplate1
Look up here for what I was suggesting
hehehehe
Compatibility was just cos i forgor the correct name
honestly we could have it without the type and use reflection shit on our end to find the plugin class
but eh
nah
because multi-plugin .DLLs
so you'd have no way to correlate them
true
Okay, the beta release on GitHub is out! We are currently sorting out issues with the NuGet package ID - which may take a while - so as a temporary measure, you can follow these instructions:
The NuGet package is not currently up due to issues with the package id, so the following will not work. To reference the mod temporarily, use the .dll - available under the zip - as a reference, or download the .nupkg, add that package as a NuGet source in your IDE, and use the following reference:
<PackageReference Include="BMX.LobbyCompatibility" Version="0.1.0-beta" PrivateAssets="all" />
Link to the GitHub release here:
https://github.com/MaxWasUnavailable/LobbyCompatibility/releases/tag/v0.1.0-beta
We will be looking into providing a much easier and cleaner method to soft-depend on the mod. Thank you for helping us smooth out the quirks in this testing phase!
For some reason I can't access the Features namespace
nvm it just let me
dunno what happened lol
welp
oh whoops
mf when publicizer:
@sharp ridge we want the helper class to be accessible to the public, right? or do we want to make a "wrapper" class that just references the lobby helper class
I need to double check what the LobbyHelper provides, but we can just internal any methods not for public use
kk, just thought we were already doing so hence the documentation. I'll make a pr with those changes, unless you want to refactor (if you're well) tomorrow
also think I have an implementation for the assembly attributes, currently trying to test but my laptop is slow and decided to present me with a black screen
