Camille
Camille copied to clipboard
Create sub library for the LCU
Suggestion to create Camille.LCU
which will enable people to interface with the league client from C#
Are you mainly interested in making an user interactive app that hooks into LCU, or just want to collect data?
Mainly in a User interactive app. Although a bit of data collection to support it wouldn't hurt the app.
Which endpoints are you planning on using? We should pick some to support initially and add more as needed
Endpoints to be included (preliminary):
Control
- GET/POST/DELETE
/lol-login/v1/session
- GET
/riotclient/region-locale
- POST
/riotclient/kill-and-restart-ux
- POST
/riotclient/kill-ux
- POST
/riotclient/launch-ux
- POST
/riotclient/unload
- POST
/riotclient/ux-flash
- POST
/riotclient/ux-minimize
- POST
/riotclient/ux-show
- GET
/riotclient/ux-state
Summoner
- GET
/lol-summoner/v1/check-name-availability-new-summoners/{name}
- GET
/lol-summoner/v1/check-name-availability/{name}
- GET
/lol-summoner/v1/current-summoner
- GET
/lol-summoner/v1/current-summoner/autofill
- PUT
/lol-summoner/v1/current-summoner/icon
- GET
/lol-summoner/v1/current-summoner/rerollPoints
- GET/POST
/lol-summoner/v1/current-summoner/summoner-profile
- GET
/lol-summoner/v1/summoner-profile
(by puuid) - GET
/lol-summoner/v1/summoners
(by name) - TODO
Champion
- GET
/lol-champion/v1/.
??? SummonerID seems to be required to be the logged in user's (so you can't see others' skins)
Ranked
- GET
/lol-ranked/v1/apex-leagues/{queueType}/{tier}
-- enums - GET
/lol-ranked/v1/current-ranked-stats
- GET
/lol-ranked/v1/league-ladders/{puuid}
- GET
/lol-ranked/v1/ranked-stats
- GET
/lol-ranked/v1/ranked-stats/{puuid}
- GET
/lol-ranked/v1/signed-ranked-stats
?? - TODO
Chat
-
/lol-chat/v1/
?
Specifically: Plugin lol-champ-select
is what i need.
GET /lol-champ-select/v1/bannable-champions
POST /lol-champ-select/v1/battle-training/launch
GET /lol-champ-select/v1/current-champion
GET /lol-champ-select/v1/disabled-champions
GET /lol-champ-select/v1/pickable-champions
GET /lol-champ-select/v1/pickable-skins
POST /lol-champ-select/v1/retrieve-latest-game-dto
GET /lol-champ-select/v1/session
PATCH /lol-champ-select/v1/session/actions/{id}
POST /lol-champ-select/v1/session/actions/{id}/complete
POST /lol-champ-select/v1/session/bench/swap/{championId}
PATCH /lol-champ-select/v1/session/my-selection
POST /lol-champ-select/v1/session/my-selection/reroll
POST /lol-champ-select/v1/session/simple-inventory
GET /lol-champ-select/v1/session/timer
GET /lol-champ-select/v1/session/trades
GET /lol-champ-select/v1/session/trades/{id}
POST /lol-champ-select/v1/session/trades/{id}/accept
POST /lol-champ-select/v1/session/trades/{id}/cancel
POST /lol-champ-select/v1/session/trades/{id}/decline
POST /lol-champ-select/v1/session/trades/{id}/request
GET /lol-champ-select/v1/team-boost
POST /lol-champ-select/v1/team-boost/purchase
Alright, I have an initial version that you can test. https://www.nuget.org/packages/Camille.Lcu/
Currently the setup is pretty hidden from the user, just calling var lcu = new Lcu()
reads the lockfile in the default install location ("C:\Riot Games\League of Legends"
). So as long as the client is open and running it should automatically be able to connect. But in the future we can have it look for the league process and get the install dir from that (code).
Very minimal example usage: https://github.com/MingweiSamuel/Camille/blob/6358c6d/Camille.Lcu.Test/UnitTest1.cs#L27-L39
I will test this somewhere this week. And try to build a nice application that we can use as an example/showcase of the LCU
It seems like the LCU also has support for WebSockets. Which might be a good idea to also implement. The HTTP approach seems to work, but it will cause a harsher API for programmers to work with without having any event driven notifications etc.
André from the API discord linked: https://gist.github.com/Pupix/eb662b1b784bb704a1390643738a8c15 to me as a showcase of how to use the WebSocket approach
Will put that down as a TODO, but unfortunately don't have time to work on it this week
Looks like it will be easiest to just support .net core & standard 2.1+ due to the custom cert used by the client https://docs.microsoft.com/en-us/dotnet/api/system.net.websockets.clientwebsocketoptions.remotecertificatevalidationcallback?view=netcore-3.0#System_Net_WebSockets_ClientWebSocketOptions_RemoteCertificateValidationCallback
~~Could enable the Riot CA cert globally for earlier versions https://stackoverflow.com/a/2675183/2398020 + https://github.com/MingweiSamuel/Camille/blob/28af5aa/Camille.Lcu/src/LcuConfig.cs#L59-L76 (This changes all cert verification I believe, but would be reasonably secure assuming Riot doesn't leak their private key).~~ this would be insecure I believe
Also took a look at https://github.com/sta/websocket-sharp as an alternative but looking at the commit history, it's like twilight zone, no idea whats going on with that package (and no nuget updates in 3 years).
Enabling the Cert globally sounds risky in terms of security. My preferred option here would be to bump the version of the LCU library. Then we don't have to rely on updates of other libraries, and we keep it completely in the framework.
Any opinions on what the interface should look like? My philosophy has been for Camille to do as little as possible to get a one-to-one mapping between the methods available and the api methods. In this case a minimum WAMP would essentially be:
event OnConnected
event OnDisconnected
subscribe(string/enum eventtype, event handler)
publish(string/enum eventtype, obj message) // not really needed, probably
(list of events here: https://github.com/MingweiSamuel/lcu-schema/blob/master/help.json#L2-L1433)
But it would be nice to have other features. Automatic reconnects, maybe awaitable events. Not sure what would be useful and how it would interact with the REST portion of the LCU (if at all)
I generally add everything that is public facing to the interface, if an external library/project may have need of it. Since otherwise you will constantly be casting the interface back to the impl or just use the impl flatout. But that being said, if we add everything here it might be one gigantic interface.
Ah, I meant "interface" in the more general way of how the API would look like on the outside.
Anyway, I have a first-pass version: https://github.com/MingweiSamuel/Camille/blob/0b45aca/Camille.Lcu.Test/UnitTest1.cs#L18-L48 https://www.nuget.org/packages/Camille.Lcu/3.0.1-nightly-2019-11-30-0b45aca4fa
Essentially there is a new .wamp
with OnConnect
, OnDisconnect
, and also a .Subscribe(string topic, handler fn)
. List of topics is here: https://github.com/MingweiSamuel/lcu-schema/blob/6d78900/help.json#L2-L309
Currently the subscribe handler just gets a jtoken object, not an actual plain class. But the LCU does actually provide that info so I will add it eventually.
There's not a way to unsubscribe right now. It's a bit clunky but works as a first-pass.
Expanding on the first-pass version, what about lcu.ChampSelect.SubscribeAsync(() => lcu.ChampSelect.GetSession(), (e) => something)
or lcu.ChampSelect.SubscribeAsync(nameof(lcu.ChampSelect.GetSession), (e) => something)
if the mapping can be easily figured out?
Using Func
it could be lcu.ChampSelect.GetSession.Subscribe(() => something)
.
BTW, I've ported a modified version of lcu-schema/update.ps1 to GitHub Actions so that I can continuously test my LCU integrated app.
if the mapping can be easily figured out?
The mapping seems to be "OnJsonApiEvent_lol-champ-select_v1_session".Replace("OnJsonApiEvent", "").Replace("_", "/")
which gives us /lol-champ-select/v1/session
. help.json
is way less cryptic when sorted 😂.
I've yet to check the other events.
I've opted for using event
in my implementation, which does use a similar Subscribe(string, Action<TData>)
under the hood:
private event LeagueClientEventHandler<LolChampSelectChampSelectSession> _sessionChanged;
public event LeagueClientEventHandler<LolChampSelectChampSelectSession> SessionChanged
{
add
{
if (_sessionChanged == null)
EventRouter.Subscribe("OnJsonApiEvent_lol-champ-select_v1_session", (LolChampSelectChampSelectSession args) => _sessionChanged?.Invoke(this, args));
_sessionChanged += value;
}
remove
{
_sessionChanged -= value;
if (_sessionChanged == null)
EventRouter.Unsubscribe("OnJsonApiEvent_lol-champ-select_v1_session");
}
}
Note: the code excerpt doesn't have created/updated/deleted implemented.
My implementation of events is now live with version 12.4.1.336 if you want to get a feel for it (e.g. (new LeagueClient()).LeagueOfLegends.ChampSelect.SessionChanged +=
). The events would have to be triggered by interactions with the LeagueClientUx, or something else, since I don't do POST/PUT/PATCH/DELETE yet. The main supporting classes are RmsClient
and RmsEventRouter
.