-
롤 실시간 관전 UI 제작은 가능할까?개발일지 2023. 3. 18. 19:54반응형
리그 오브 레전드는 매우 유명한 게임이다.
2011년 출시 이후 12년이 지났지만, 아직까지도 많은 사람이 즐기고 있으며, 다양한 챔피언들과 함께 e스포츠 리그도 크게 성장한 종목이다.
리그 오브 레전드 리그 오브 레전드의 개발사 라이엇 게임즈는, 세계 각지의 프로 리그를 운영한다. 그리고 각 리그의 최강자들은 Worlds에 모여서 세계 제일이 누구인지를 가린다. 이게 참 재밌다.
그런데 말이죠?
밴-픽 UI LCK를 보다가 이런 세련되고 예쁜 UI를 만들어보고 싶어졌다.
인게임 UI. 와중에 한화는 지고 있다 마침 학교에서도 e스포츠 대회를 개최하는지라, 이걸 만들면 학교 행사에 써먹을 수 있지 않을까? 라는 생각이 들었다.
내가 필요한 것은?
- 킬 스코어 정보
- 골드 정보
- 타워나 드래곤 등 오브젝트 정보
- 게임 진행 시간 정보
자료 조사를 통해 얻은 것
일단 UI를 만드는 것은 둘째치고, UI에 들어갈 정보를 얻어야 하는데, 이 정보를 어디서 얻을까?
조사 결과, 라이엇의 API를 사용하면 게임에 대한 정보를 얻어올 수 있었다.
https://developer.riotgames.com/apis
Riot Developer Portal
developer.riotgames.com
라이엇 게임즈는 공식적으로 게임에 대한 API를 지원한다.
API를 사용하면 게임의 유저 정보 등 여러 정보를 가져올 수 있다.
API 사용법
일단 API를 가져오기 위해서는 API 토큰을 생성해야 한다.
Riot Developer Portal에 로그인하자. 계정은 자신의 라이엇 계정을 사용하면 되며, 따로 회원가입할 필요는 없다.
라이엇 디벨로퍼 대시보드 자신 계정의 대시보드에 들어가, API KEY를 생성한다.
만약 이전에 API KEY를 생성하였고, 그 키가 만료되었다면 새로 생성하면 된다.
이후 생성된 키를 Copy 버튼을 통해 복사한다.
이 키를 이용해 API를 불러온다.
파이썬에서 API를 이용해보자
파이썬은 매우 강력한 언어이기 때문에, 일부 작업을 제외하면 거의 모든 것을 할 수 있다.
패키지 관리자인 pip를 통해 API를 사용할 수 있는 패키지를 다운받아보자.
- JSON 모듈
- requests 모듈
requests 모듈은 API를 이용할 때 사용하며, JSON 모듈은 API를 통해 얻은 정보를 가공할 때 사용한다.
pip install requests pip install json
requests와 json을 불러오기한다.
import requests import json
인게임 정보를 얻어오기 위해, 현재 게임을 플레이하는 유저를 찾았다.
https://www.op.gg/spectate/live/pro-gamer
프로게이머 관전하기 - League of Legends
롤 전적, 모든 게임의 전적, 챔프 평점, KDA, 승률을 볼 수 있고 리플을 보거나 자신의 게임을 녹화를 할 수 있습니다. 지금 바로 당신의 소환사명을 검색해보세요!
www.op.gg
친절하게도 전적 검색 사이트인 op.gg에는 현재 게임을 진행중인 프로게이머의 정보를 알려 준다.
이 글을 쓰는 당시에, 이른 아침이었기 때문에 1명만 게임을 플레이 중이었으나, 아마 저녁쯤 되면 더욱 많은 사람이 게임을 하고 있을 것이다.
게임을 플레이하는 중인 유저의 닉네임을 변수로 저장해두자. 동시에 API KEY도 변수로 저장한다.
SummonerName = "ShrimpVicious" KEY = "RGAPI-7c4-c85a-4f92-be8d-1f1cb40781"
shrimpVicious라는 닉네임의 유저가 게임을 진행 중이었다.
'shrimp' 선수라고 한다.
KEY 변수는 API KEY를 넣은 것이다. 유출되어서는 안 되기 때문에 몇 개 글자를 빼뒀다.
KEY를 넣는다고 API에서 정보가 뚝딱 나오는 것이 아니다. 어떠한 사람이 어떤 환경에서 무슨 사이트에 접속하려 하는지 정보를 보내자.
header = { "Accept-Language": "ko-KR,ko;q=0.9,ja-JP;q=0.8,ja;q=0.7,en-GB;q=0.6,en;q=0.5,en-US;q=0.4", "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8", "Origin": "https://developer.riotgames.com", "X-Riot-Token": KEY } #API 호출 헤더
무슨 API를 호출해볼까?
라이엇 API 목록에서 실시간 정보를 볼 수 있는 것을 찾는다.
spectator V4 SPECTATOR-V4 섹션에 active-games를 사용하면 게임의 진행 시간을 볼 수 있다.
사진에 보이는 gameLength가 게임의 진행 시간이다.
이 API를 호출하기 위해서는 암호화된 유저 ID가 필요하다.
이것도 API를 사용하면 알아낼 수 있을 것이다.
찾았다. 유저 닉네임을 이용하여 id를 얻어낼 수 있다고 한다.
바로 사용해보자.
def id(): response = requests.get(f"https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/{SummonerName}", headers = header) response = response.text data = f"{response}" data = json.loads(data) return data['id'] #라이엇 암호화된 ID 추출 id = id()
id 라는 함수를 만들었다.
함수를 실행하게 되면 id를 return한다.
바로 spectate도 사용해본다.
def spectate(): response = requests.get(f'https://kr.api.riotgames.com/lol/spectator/v4/active-games/by-summoner/{id}', headers = header) response = response.text response = f"{response}" response = json.loads(response) return response # 현재 진행중인 게임 정보 return
이제 spectate()를 출력하면 게임의 정보가 나온다.
{'gameId': 6410555448, 'mapId': 11, 'gameMode': 'CLASSIC', 'gameType': 'MATCHED_GAME', 'gameQueueConfigId': 420, 'participants': [{'teamId': 100, 'spell1Id': 12, 'spell2Id': 4, 'championId': 7, 'profileIconId': 4568, 'summonerName': 'godqhrgkrhtlvek', 'bot': False, 'summonerId': 'hyszqVU0pGgfG7BL0peLQVXM3FBWrhWK7kY-KKfVeJDXq94', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8112, 8139, 8138, 8135, 8226, 8237, 5005, 5008, 5003], 'perkStyle': 8100, 'perkSubStyle': 8200}}, {'teamId': 100, 'spell1Id': 6, 'spell2Id': 4, 'championId': 22, 'profileIconId': 5663, 'summonerName': '므리야', 'bot': False, 'summonerId': 'ImaOHOdneOSipoMET4IEkw4eLML6AjDKK9uy2nsSgUsbEyA', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8008, 9101, 9104, 8017, 8345, 8410, 5005, 5008, 5002], 'perkStyle': 8000, 'perkSubStyle': 8300}}, {'teamId': 100, 'spell1Id': 14, 'spell2Id': 4, 'championId': 497, 'profileIconId': 5675, 'summonerName': '티슈히메', 'bot': False, 'summonerId': '-oUP_CQnp0ueQ9qh6kPUsH2pnM5LPIfo53If4QfYjA2JTok', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8465, 8463, 8444, 8242, 8136, 8106, 5008, 5008, 5002], 'perkStyle': 8400, 'perkSubStyle': 8100}}, {'teamId': 100, 'spell1Id': 4, 'spell2Id': 12, 'championId': 62, 'profileIconId': 3159, 'summonerName': '근 두', 'bot': False, 'summonerId': 'kQLRUVuaggYObjSfPk0ZtFSTMBbg7253E4H5MWgQ7T_-kQ', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8010, 9111, 9104, 8299, 8444, 8451, 5005, 5008, 5003], 'perkStyle': 8000, 'perkSubStyle': 8400}}, {'teamId': 100, 'spell1Id': 4, 'spell2Id': 11, 'championId': 254, 'profileIconId': 5641, 'summonerName': '전용준 캐스터', 'bot': False, 'summonerId': 'tzCuyFMjTHh9SMuN9LBXB4Fjal6UGBe8vND8C5DnJiRhMKo', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8010, 9111, 9104, 8014, 8347, 8304, 5005, 5008, 5002], 'perkStyle': 8000, 'perkSubStyle': 8300}}, {'teamId': 200, 'spell1Id': 3, 'spell2Id': 4, 'championId': 117, 'profileIconId': 23, 'summonerName': 'zzsup', 'bot': False, 'summonerId': 'H1nzHeOuXluT4Py9Q4qescfKDpabenDdg8Rf-ksvGUUqdsN8xpWc2GrXmg', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8465, 8463, 8473, 8242, 8345, 8347, 5008, 5008, 5002], 'perkStyle': 8400, 'perkSubStyle': 8300}}, {'teamId': 200, 'spell1Id': 14, 'spell2Id': 6, 'championId': 8, 'profileIconId': 23, 'summonerName': '핏빛 계약', 'bot': False, 'summonerId': 'P2GJbnnSVQog89q5EBObA3eMQE7i_dNCu3fU4Gsem0tkzIw', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8214, 8275, 8234, 8237, 9105, 8299, 5008, 5008, 5002], 'perkStyle': 8200, 'perkSubStyle': 8000}}, {'teamId': 200, 'spell1Id': 11, 'spell2Id': 6, 'championId': 120, 'profileIconId': 5533, 'summonerName': '요즘따라 행복해', 'bot': False, 'summonerId': 'MPZTLTtoapn3s3pfh_zb_ugp1akz56s9Fh8FA5QiBJEZ0jI', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8230, 8275, 8234, 8232, 9111, 9105, 5005, 5008, 5002], 'perkStyle': 8200, 'perkSubStyle': 8000}}, {'teamId': 200, 'spell1Id': 6, 'spell2Id': 4, 'championId': 221, 'profileIconId': 3587, 'summonerName': '추와와', 'bot': False, 'summonerId': 'GSWfXYPFWq-zYgE9MOf6bLrW45qGYqhF-AHcDc61foCtOg', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8008, 9101, 9104, 8014, 8473, 8451, 5005, 5008, 5002], 'perkStyle': 8000, 'perkSubStyle': 8400}}, {'teamId': 200, 'spell1Id': 12, 'spell2Id': 4, 'championId': 61, 'profileIconId': 984, 'summonerName': '파닭세대', 'bot': False, 'summonerId': 'zwX22jMNqNYb4UKOYZ3HR8iB1hYrljjt8XhAnlEZKHEaHg', 'gameCustomizationObjects': [], 'perks': {'perkIds': [8230, 8226, 8210, 8236, 8009, 9105, 5005, 5008, 5003], 'perkStyle': 8200, 'perkSubStyle': 8000}}], 'observers': {'encryptionKey': 'BT2JQEtipC4KRC5lZWH0k31MIyakobpq'}, 'platformId': 'KR', 'bannedChampions': [{'championId': 350, 'teamId': 100, 'pickTurn': 1}, {'championId': 45, 'teamId': 100, 'pickTurn': 2}, {'championId': 150, 'teamId': 100, 'pickTurn': 3}, {'championId': 238, 'teamId': 100, 'pickTurn': 4}, {'championId': 412, 'teamId': 100, 'pickTurn': 5}, {'championId': 53, 'teamId': 200, 'pickTurn': 6}, {'championId': 39, 'teamId': 200, 'pickTurn': 7}, {'championId': 119, 'teamId': 200, 'pickTurn': 8}, {'championId': 64, 'teamId': 200, 'pickTurn': 9}, {'championId': 60, 'teamId': 200, 'pickTurn': 10}], 'gameStartTime': 1679134559256, 'gameLength': 82}
제일 뒤에 있는 gameLength가 게임이 시작되고 흐른 초이다.
82라면 1분 22초를 나타낸다.
이것만으로는 부족하다.
더욱 많은 정보를 얻어내자.
그러나...
더이상의 정보를 얻어낼 수는 없었다.
사실 게임의 정보를 얻어내는 방법은 하나 더 있었다.
matchid를 통해 게임의 정보를 얻어낼 수 있는 API가 있었으나...
진행중인 게임의 정보를 얻어올 수 없었다.
이외에 별다른 방안은 없는 것 같아 여기서 마무리.
마치며
리그 오브 레전드에서 관전 모드를 이용한다면, 실제 게임과 5분 딜레이를 두고 볼 수 있다.
물론 랭크 게임 등 중요한 게임에서 관전 모드를 이용하면서 딜레이가 없다면 맵핵이나 다름 없는 행위이니 어쩔 수 없다.
그러나, 악용을 할 이유가 없는 사용자 설정 게임에서도 이런 제한을 건다는 것은 약간 이해되지 않는 부분이다.
기술적으로 문제가 있어서인지, 혹은 그 외 이유가 있어서인지는 모르겠으나, 만약 문제가 없다면 이런 딜레이는 없애 주는 것도 나쁘지 않을 것이라는 생각을 한다.
관전 UI를 변경하는 방식으로 또 다른 방법은, 클라이언트를 변조하여 게임 자체의 UI를 바꾸는 것이다.
그러나 이 방법은 게임의 이용 약관을 어기는 일이기 때문에 실제로 실행하는 것은 절대 금물이다.
오늘의 목표는 달성하지 못했다.
하지만 API가 어떻게 작동하는지 대충 알았다는 것을 위안으로 삼아야 하겠다.
반응형