Technology Interns in 2020: League of Legends, TFT, & VALORANT
The Riot internship program helps technical players drive their professional growth by embedding them on tech teams and having them contribute to impactful, exciting technology projects. Last year we published an article by some of our interns, giving readers a glimpse at the projects technical interns get to work on. We’re doing a follow-up this year, but with additional sections to reflect our new games.
There were so many interns excited to contribute to this article that this year we’ll be doing a 2-part series. Intern stories are sorted into categories - the first post (this one) includes all blurbs for League of Legends, TFT, & VALORANT, and the second post focuses on General Game Tech & Tooling/Infrastructure.
Thanks to all our interns for their hard work and excellent projects this summer!
League of Legends
Role: Software Engineer
Team: LoL Competitive
Hi everyone! My name is Wei Zhang. I study games and computer science at the University of Southern California, pursuing a master’s degree. This summer, I worked as a software engineer intern on the LoL Competitive team. My team focuses on providing a high quality competitive experience for LoL players.
My main project for this internship was implementing a user interface that provides introductory information about the Ranked game mode and lists corresponding end of season rewards for each tier. The design consists of a high-level textual summary of Ranked, a visual image describing the tier/division system, and finally a tier carousel of cards that players can navigate to see the different rewards. It also indicates the player's current rank progress and warns the player if they’re still on provisional matches. The goal is that by using this interface, players can have a persistent reference for the current Ranked season.
The League client UI operates within the framework of Ember.js and the client frontend consists of many applications representing different functionalities/compartments. Applications are divided into “components” which include smaller features associated with their specific application. The Ranked Reference modal I worked on is part of the Profile application of the League client frontend, which offers the overview of player’s level/tier information. The implementation of the modal utilizes the frontend UI kit, a facilitating toolkit program developed by frontend engineers, that provides helper functions and useful templates to meet basic UI needs. Taking advantage of the UI kit also ensures the aesthetics of the modal are in line with the client environment.
The most interesting part to me was the attention to detail and the understanding of how the client frontend communicates with other parts of the service pipeline. For example, the tier card carousel requires a player’s Ranked and reward information, which is interpreted by the program and dynamically loaded onto the tier cards UI.
Working on the League client has been a fascinating and eye-opening experience for me. Prior to the beginning of this internship, I did not possess substantial frontend/web client development experience, nor did I work with Ember.js. Adapting to this new tech stack and ramping up my knowledge couldn’t have happened without the help of my mentor, my team, and my peers. The company culture of thriving together can be easily sensed in code reviews, daily interactions, and dedicated attitudes towards building a good and reliable product/experience. Despite the various difficulties presented by 2020, this has been a truly phenomenal and memorable journey, and I will be forever grateful for it.
Role: Software Engineer
Team: Champion Engineering
Hello everyone! I’m Felix “Arasseo” Guo and I’m a student at the University of Waterloo. This summer I worked as a software engineer intern on the Champion Engineering team on League. The Champion Engineering team is responsible for developing the underlying tech required to make champions fun and exciting, and projects vary from building out the systems that enable making any champion stealthed (Senna) to enabling different dimensions (Mordekaiser). This involves interacting with various underlying systems of the League game engine, and helping designers write the higher level scripts to manage the interactions for every champion’s spells and abilities.
This summer, I fixed various bugs related to an assortment of champions as part of our team’s bug-fixing projects. Some of these bugs included: adjusting bonus health scaling to work properly on Kled, properly applying adaptive runes to Elise, correctly displaying VFX in spectator mode for special skins like Pulsefire Ezreal and Pizza Delivery Sivir, and more. I also had the chance to build out a color picker dialog for our internal scripting tool to enable designers to more easily prototype.
Outside of tooling work, I had the chance to work on a couple of new up-and-coming champions by doing some scripting and script optimizing, which was super exciting! There are some really interesting and fun champions on the timeline for the rest of 2020 and beginning of 2021 that I’m really excited to be able to play.
Finally, I did a deep investigation into minion construction times and worked on a refactor of our internal network packet registration system to be more efficient and easier to follow, as well as gaining performance in minion spawning times. As the age old saying goes, everything is coded as a minion.
Role: Software Engineer
Team: Champion Engineering
Hello! I’m Cliff “IG Baolan1” Zhu, a computer science student at Carnegie Mellon University. This summer I worked as a software engineer intern on the Champion Engineering team with Felix. As Felix mentioned, the Champion Engineering team writes code that enables champions to cast their abilities. We collaborate with designers, artists, and quality analysts on new champion development cycles.
Over the course of my internship, I fixed a bunch of bugs that are gameplay-affecting or even esports-impacting. For example, I made sure Rek'Sai empowered auto-attacks don't fizzle immediately after her ultimate cast, Lilia can E-flash, Lilia’s ultimate VFX doesn’t get hidden by large champion health bars, Darius empowered attacks deal the correct amount of damage to spell shields, and more. I also built tools to remove experience and manipulate levels on champions in internal testing builds to slow down playtests if needed.
Aside from that, I got to work on some new champions that will be released early next season. I worked on the tech prototype for gameplay and VFX display for one of their abilities.
Outside of tech work, I’m most impressed by Riot’s culture. Everyone here is friendly and supportive and highly passionate about the product we are making. With regular feedback and bonds we build with each other, we’re working to keep making League of Legends better than ever.
Role: Software Engineer
Team: Esports Digital
Hey, I’m Alessio “Asymons” Symons, and I’m wrapping up school at the University of Waterloo where I’m studying computer science. This summer I worked as a software engineer intern on the Esports Digital team. My main focus was working on features related to drops, a system where web operators can issue rewards to all viewers watching the stream on lolesports.com.
For drops, I worked on enabling sponsors to optionally show their logo on the drop in certain locations. Some sponsors don’t want their logo overlays on art. This caused a slight issue with how we were currently working, because we would reuse a sponsor image in multiple areas of the drops page. To remedy this, we opted to allow images to be uploaded based on location of the image, rather than having general small/large images.
This may seem like a simple “add a column and be done with it” database change. Unfortunately, as drops have already been launched, we needed to migrate changes from the old columns into the new columns in a way that didn’t break the current drops. So with a bit of trial and error, SQL, and lots of services being deployed, we were able to rename some old columns and copy data from old columns to a new one to fit sponsor requirements. We have a tool called Liquibase which allows us to sequentially apply migrations to our database for consistency, and migrations are applied in order.
Overall I felt the internship went fantastic and I managed to contribute to a bunch of things that the Esports Digital team owns. I feel like the opportunities presented to me in this internship were amazing overall and technically challenging enough that I was able to see myself grow over the few short months.
Role: Data Scientist
Team: League Data Central
Hi! I’m Reed, a data science intern on the League Data Central (LDC) team. We handle data-related projects for League of Legends, Teamfight Tactics (TFT), and Wild Rift. LDC’s work includes systems that directly affect the player experience like matchmaking algorithms and Your Shop. My project involved doing Natural Language Processing (NLP) for TFT mobile app reviews.
TFT mobile has millions of downloads and averages several hundred reviews per day. The purpose of my project was to help understand what these reviews are actually saying, helping product managers and analysts more easily digest player sentiment. There were two main components of my project:
1. Tracking fixed topics of interest.
2. Dynamically testing whether reviews are similar to any new, human-generated topic.
To capture meaning in text, we used Bidirectional Encoder Representations from Transformers (BERT), an NLP model pre-trained on a large corpus of text (mostly Wikipedia). Pretty much all BERT models are trained as text classifiers, but we can use the hidden layers as embedding vectors that (hopefully) represent the meaning behind what the text is saying. This is similar to Word2Vec, Autoencoders, and other deep learning approaches for learning embeddings. With a good embedding model, we can tell if a review is complaining about lag, connection, game balance, or whatever else we might be interested in by comparing their embeddings.
Computing review topic similarity
As a proof of concept, we had a BERT model trained to classify text into one of several common categories. These categories were labeled using the presence of keywords, and the model was only trained on general survey data from League players. This got us part of the way there, but cosine similarity on the model’s embeddings was really imprecise for about half of the fixed topics we were interested in. We got noticeably better results after making a few key adjustments, like training the model using masked language modelling (which can be done unsupervised, unlike topic classification). We also included TFT-specific data and experimented with model/embedding parameters.
We used the TensorFlow implementations of BERT from HuggingFace. Tuning the final BERT model took about four hours on a single-GPU databricks cluster. We set up a Tableau dashboard for tracking similarity for fixed topics across regions, time, and other variables. For dynamically testing new topics, we wrote a script on Databricks that reads inputs from a Google sheet and sends the results via email. (It’d be better to serve the model in a web app, but we hit some time constraints here.)
Part of the Tableau Dashboard
To me, the coolest part of the internship was getting to use a fairly cutting-edge NLP approach, BERT, to make something that will hopefully help Riot meet the needs of TFT players. I had to do a good deal of research to understand the methods behind Transformers and BERT, but it felt rewarding to learn something new and then use it in a project. I especially appreciated the culture of learning and trying new things at Riot. For instance, my mentor was open-minded about letting me try things that I thought looked promising, even if we weren't sure whether they’d work out.
Role: Software Engineer
Team: TFT Gameplay
Hey, my name is Kallen and this summer I was a software engineer on the TFT Gameplay team. My team generally works on sets, traits, and UI improvements.
My main project this summer was improving the combat recap feature in TFT. Currently, the combat recap shows the player how much damage their units dealt in the most recent battle. My task was to pipe in and display new event data on damage blocked, shields, and heals. This new enhancement to combat recap would allow players to adjust their comps based on defensive and utility stats - and my changes will be live in patch 10.20.
In the new combat recap, players will be able to toggle different modes depending on what stats they want to see. In offensive mode, they’ll be able to see damage dealt (physical, magic, true). In defensive mode, they’ll be able to see the damage blocked (physical, magic, true) and in utility mode, they’ll see the amount of shields and heals their units grant.
This feature required event data piping from an EventSystem listener, which means that when any unit casts a heal or takes damage, the event data will be dispatched to the combat recap. The data gets replicated and delivered to the client, which then goes through a sort to determine the order they would be displayed on screen. On the UI side, some adjustments also needed to be made for the new modes such as new tooltip strings and bar colors. There were also some quality of life changes such as scrolling or button transitioning that I worked on in order to add polish.
Alongside the combat recap improvements, I was also able to do some designer work which included creating a unit and a couple of traits for the upcoming set. This work was fairly different to typical engineering because it included some amount of scripting as well. I’m super excited to see what players think of the next set!
I had the chance to work with a really awesome team, and the environment and culture is incredibly welcoming and inclusive. Everyone at Riot is unafraid of bringing up hard topics and resolving new issues that arise. I’ve been really impressed at seeing Rioters wanting to improve the company and working hard to make sure their voices are heard. My internship on the TFT team has been a great experience and I’m extremely grateful to have had the opportunity to intern at Riot this summer.
Role: Software Engineer
Team: TFT Personalization
Hi everyone! My name is Hensen Huynh and I am starting my master’s in computer engineering at UC San Diego this year. This summer I worked as a software engineer intern on the TFT Personalization team. Personalization is the team behind the cosmetic items and other features surrounding cosmetics in TFT such as the battlepass, the loadout (where you select your items), the TFT mobile store, Twitch Prime rewards, and other out-of-game features.
My project focused on working in the League client, where I added new features and improvements to the loadouts for TFT. I was given a variety of different features I could implement and had to decide the order in which I wanted to tackle them. I worked on a few features, such as a new item section, a glow for recently acquired items, and a search bar.
New Item Section
This involved me delving into the cosmetics plugin which acted as an endpoint for the loadout, and changing it so that it would save the purchase date from the inventory plugin when it retrieves the information, and then send it to the frontend when the information is requested. Once I had this information in the frontend, I was able to use it to generate a new item section with the most recently acquired items.
New Item Highlighting
The complication here was that there was no good way for us to determine whether or not an item was new. An additional complication was that we wanted the items to remain highlighted until it was hovered over or if the specific loadout tab (i.e Little Legend) was opened and then the user logged out. To ensure we were accurately capturing log outs, I added a new endpoint that saves the last time that loadout tab is opened and items that should no longer be considered new. We then use the purchase time information and the last opened time to determine whether or not an item should be highlighted. This information is then sent along with the item data object to the frontend where it will keep track of what has been hovered over until the loadout closes. When it closes, it then sends the items that have been hovered over so the next time it won’t be considered new again.
The last feature that I was able to complete during my time here was the search bar feature that allows you to search through whatever loadout tab you are currently in. Initially, I implemented a really simple solution by completely repopulating the loadout everytime a search was performed. During code review, it was noted that this method is expensive and can be slow on older computers. This prompted me to reimplement it with a solution that relies on setting attributes on the individual items. While implementing this solution, I changed how the existing filter (show unowned items) worked to also use this principle. This allows us to easily create a filter system in the future by just adding it as an additional check in the method that determines whether or not an item is shown.
I really enjoyed my time here at Riot Games and can’t imagine a better remote internship experience. Everyone on my team was really nice and friendly and made me feel really welcome here during my short time on the team. I really enjoyed the emphasis on having 1 on 1s with people. It was amazing getting to hear the stories of so many people in different disciplines. This made me really appreciate the diverse experiences of everyone around me.
Role: Software Engineer
Team: Core Services
Hello! My name is Kenta “kenbobernator” Tellambura and I’m a computer engineering student studying at the University of Alberta. This summer, I worked remotely from Edmonton, Canada as a software engineer intern on the Core Services team for VALORANT. The Core Services team works on supporting and building features for the backend services and game servers that VALORANT runs on. As an intern, I got to witness firsthand how many of the online aspects of the game are implemented and I had the opportunity to work with some of the microservices that keeps the game going.
VALORANT’s backend primarily consists of microservices written in Go, each with their own important role. I spent my summer working on adding new features and fixing bugs in these microservices. Working on each of these different components gave me a better understanding of how they all work together.
Implementing a Third Party Endpoint
While I had the opportunity to work on a handful of different features and bug fixes, what was most interesting for me was implementing one of the third party endpoints for VALORANT. Riot provides an API for external developers to access game stats. One of my tasks was to build an endpoint that returned the match IDs of all VALORANT matches in the last hour. With the sheer number of matches going on at one time, this ended up being a bit more difficult than expected.
In our first iteration of this feature, we had aimed for returning the last hour of matches. The plan was to add a third party endpoint to our Match Details service, a service that stores and processes various details of a completed match. On an end-of-match event, our microservice would store the match ID in Redis, paired with the match end time as the score. A background loop would continuously purge match IDs that had expired. When a query comes in requesting for the last hour of matches, we would simply fetch the match IDs from Redis. Any third party request would have to get routed through the 3rd party team’s services so we didn’t have to worry about any rate-limiting or authentication issues.
Our first iteration seemed to work fine in our dev environment. The results were returned quickly and our services didn’t encounter any performance issues or errors. However, issues started to pop up when we hit our servers with a load-test that simulated end-of-match events at our predicted rate. At the peak, the list of match IDs being returned ended up being 20 to 30 megabytes! It would take several seconds for the JSON response to be sent over to the client and this was not something we could work with. These slow response times could slow down this entire service and end up affecting real players!
Our easiest solution was to cut down our response from 1 hour to 10 minutes of matches. This made things far more doable. However, this still meant the request was several megabytes and the response was still taking too long to return. To solve this, we introduced compression to the response. The response would now be returned as a gzip file instead of a raw JSON blob. To top it off, the zipped response would be cached and stored in-memory to reduce the load on our Redis database and speed up the response time. After making these changes, we ran the load test again and we managed to reduce our response time and response size significantly. Finally, adding a few more instances of the service got us to the point where we were happy with our load testing results.
While this project seemed simple at first, optimizing it to achieve our performance goals was a lot of fun. It taught me that building even the simplest of features can be challenging when building it for hundreds of thousands or even millions of players. My time at Riot was shortened due to COVID-19, but the impact I managed to have and the experience I gained was far more than I expected. By the time this gets posted, my third party endpoint should be live, so I hope those of you reading this will have a chance to play around with it and build something cool.
Check out the rest of our 2020 intern series:
Part I: League of Legends, TFT, & VALORANT (this article)