Technology Interns at Riot Games

栏目: IT技术 · 发布时间: 4年前

内容简介:The RiotOur technical interns worked with fellow Rioter technologists on everything from game engineering to developer tooling. Before they left, we asked some of them to share projects they contributed to, and to tell their stories of interning at Riot.Th

The Riot internship program exists to help players drive their professional and personal development. During their time with us, interns gain valuable work experience, an opportunity to build mentoring relationships with full-time Rioters, and a memorable time spent within their intern community. Last summer, we hosted over 20 tech-focused interns across different products for a 3 month long journey. 

Our technical interns worked with fellow Rioter technologists on everything from game engineering to developer tooling. Before they left, we asked some of them to share projects they contributed to, and to tell their stories of interning at Riot.

This article is split into two categories - League of Legends and Tooling & Infrastructure . Within each category are blurbs by each of the interns who volunteered to write about their experiences. 

Thanks to all our interns for their fantastic contributions this summer!

League of Legends

Sumedha Raman

Role: Software Engineer

Team: LoL Competitive

My name is Sumedha Raman, and I’m studying Computer Science at Georgia Tech. This summer, I worked as a software engineering intern on the League of Legends Competitive team. The team works on features that improve the competitive experience for LoL players, including Ranked, Clash, and Champion Mastery. The project I focused on was a rework of how end of game data is sent to the Champion Mastery service. 

The Champion Mastery service evaluates a player’s performance with a particular champion. At the end of every game, the service calculates the grade and Champion Points for each player. Earning CP helps players progress through the seven levels of Champion Mastery and obtain the associated rewards.

End of Game Data

Previously, services that require the end of game data listened for End of Game Event messages posted to a queue. These messages consist of a subset of all the statistics available at the end of a game. If a service required additional stats, it would have to be added to the message. This increases the payload size, which is often unnecessary for other services which do not use the additional data. This proved to be inefficient as services are updated over time.

With the redesign of this system that I worked on this summer, the End of Game Event is no longer used. Instead, when a game is completed, the End of Game Data Service pushes a message containing a list of file names to the queue. The services consuming these messages then post requests for certain files which contain the data required. In this way, services only receive the data they use.

Polling Client

As an added optimization, I also implemented a polling client for the End of Game Data Service, which polls for a list of end of game messages at a fixed interval. This is a backup mechanism to ensure that the games which are not processed successfully while consuming messages from the queue are processed via polling. An important consideration while writing the client was deduplication - a service should only process each game once, either through the message listener or through polling. For this, the client keeps track of processed games in a database. These entries are cached for quick access. When the client polls the End of Game Data Service, an end of game message is processed only if the database does not contain an entry for the game.

Conclusion

So how does this fit in with Champion Mastery? With the availability of more end of game statistics, the grade calculation model can be refined to better represent a player’s performance. In addition to this, the polling client will ensure that the service processes all games, even if they are missed by the queue listener. This should improve the overall player experience with Champion Mastery.

Technology Interns at Riot Games

Working on this project involved collaborating with multiple teams in LoL , giving me an opportunity to learn from many amazing people here at Riot. As an intern, it has been incredibly exciting to contribute directly to a product that operates at a massive scale. My work this summer has truly been a challenging and rewarding experience.

Mindy Chi

Role: Software Engineer

Team: LoL Summoner’s Rift

I'm Mindy "impuuuu" Chi, a software engineering intern working on the Summoner's Rift team. This team’s responsibilities cover gameplay once a game launches - this includes champion balance, items, jungle camps, and Runes. This summer I worked on long-term changes to the Item Shop and contributed to preseason features. I specifically focused on balance, Runes Reforged, Death Recap, and general bug-smashing.

The technical space I worked in included C++, and C and Javascript for client work (for Runes). I also worked with the Lua script which lies on a layer above the code that is for designer use, and GDS for data storage. 

My projects were very data-driven. My team worked with a data scientist who generates a JSON file that contains all the relevant information, and it’s up to us to make sure it gets into GDS and into the game. The most interesting part for me was architecting how the process would look to regularly bring data into the actual game.

Audrey Thompson

Role: Software Engineer

Team: Merch

Hello! My name is Audrey “Audibles” Thompson - I’m a rising senior studying Computer Science at UC Berkeley, and I’m a software engineer intern on the Merch team. My team is in charge of creating and delivering merch to players across the world. As an engineer on the team’s web division, my job has been to work on the merch website and to create and maintain supporting services behind the scenes.

My team’s technical environment contains both frontend and backend components, and our main product is the merch website , which is our main way of getting products into players’ hands. It’s also an important component in many LoL -wide events like KD/A or Odyssey, as we regularly update our homepage to reflect in-game campaigns and show off related merch. As an ecommerce site, we also manage player transactions and safely store customer data. Since our merch can be purchased in most regions where LoL is played, we also need to make sure our site is accurately localized so that every player can understand its content without having to rely on external translation software.

As an intern I haven’t had one central project to complete over the course of the summer; instead, I’ve worked on a ton of smaller projects and worn many different hats along the way. I’ve made a Slack bot to help monitor and classify potentially fraudulent orders and make life easier for the player support agents we work with. I also designed and created a landing page for a new section of the site, and worked on implementing A/B testing for new pages. I added a new script to our automated emailing service to send out invoice emails to EU players, which required making new API endpoints as well as designing a clean and responsive email template. And one of my largest projects and challenges has been adding a feature to the merch site that allows players to enter their email on an out-of-stock product so that they can be notified when it comes back in stock.

Slack Bot

The Slack bot was one of the first projects completed during my internship, and was super fun for me as it introduced me to the Slack API. I was tasked with creating a bot to help automate some of our internal systems for marking and processing potentially fraudulent orders. Previously, our player support agents would have to manually approve any order that was marked as potentially fraudulent via our ecommerce backend portal, which could be a bit tedious. To make things easier for the agents, we first set up a service that automatically sends messages to a specific Slack channel whenever our system marks an order as potentially fraudulent. Then, a player support agent checks the order info from that message, and if the order is not fraudulent all they have to do is react to the message with a specific Slack emoji. My Slack bot monitors this channel for emoji reactions, and whenever that specific reaction is made on a message, it parses out the relevant order info from the message text and makes an API call to our ecommerce service to mark the order as safe to fulfill. This saves the PS agents a lot of time and ensures that they don’t need to worry about sending API calls themselves.

Technology Interns at Riot Games

Technology Interns at Riot Games

A/B Testing

My next project was much more frontend-heavy and UI-focused. To prepare for a new content launch, I designed a landing page specifically intended to be a different experience from the rest of the merch site. This required coding in React and Typescript as well as formatting with traditional CSS. One of the main challenges other than making the UI look good was implementing a custom search bar. I also had to pull from several different datasets to populate the search results and image assets for the page, so I had to be careful about the page’s loading time and React’s state updates to make sure everything was mounted in the correct order. Since this page is different stylistically from the rest of the merch site, we then had to decide if it would be worth including this new product experience rather than the usual list of purchasable products. To make an informed and data-driven decision, we implemented A/B testing using Google Optimize. Each visitor to the site will be shown one of two experiences, either the landing page or the traditional collection of products, and we can view the resulting customer conversion rates (how many people actually purchase something) to judge which experience is better. Depending on the results, my landing page may not end up being implemented, but it was still a blast to create it and I learned a lot during the process!

Notification Service

The last major project I worked on was the notification service for letting players know when a product comes back in stock. This feature had both frontend and backend components, but was definitely much more backend-heavy as it required putting together a database and API to store and retrieve data for players who want to be notified. Since I’ve been mostly focused on frontend in the past, I was super excited to take on this challenge and level up my backend knowledge. The first thing I did to create this feature was set up a simple Django app coded in Python that could store, fetch, and modify a MySQL database when certain API endpoints were triggered. Django makes dealing with tables inside of a database much easier, and since we only needed this database to do a few simple operations (mostly writes as people enter their emails into the site, and then occasionally a lot of reads as we retrieve players to notify them when something is back in stock). I then implemented the frontend portion, which was a simple input form that appears on a product page when something is out of stock but we plan on restocking it in the future. This form would trigger a customer lookup and then store that customer’s id within the MySQL database along with the product that they want to be notified for. When a product comes back in stock, we use an internal admin panel to grab all of the table rows for people who signed up to be notified for that product, and send them an email using our existing automated emailing system. I’m really happy to have implemented this feature, since it’s something I would have personally appreciated having in the past when popular products sold out quickly like the Star Guardian jacket. Now players will be informed and prepared when cool merch gets restocked!

Technology Interns at Riot Games

Conclusion

I had a great time spending my summer interning here at Riot. Outside of my day-to-day work with the merch team, I was also really lucky to get the chance to talk to the students of the Girls Who Code program hosted here on Riot’s campus as part of an intern panel. Since I participated in one of GWC’s summer programs myself back in 2015 and was a teaching assistant for another GWC program later, it was awesome to see the girls learning to code and making their own projects. One of my favorite parts of the summer was meeting the girls and seeing them be inspired to start their tech careers just like I was!

Allison Chow

Role: Software Engineer

Team: LoL Growth

Hi! I’m Allison “AL33SIN” Chow and I study computer engineering at the University of Waterloo. I’m on the League Growth team as a software engineering intern. My team has primarily been focused on new player experience in the League client, but we’re now taking on experiences for players in every part of their lifecycle!

My team mainly works in the League client, building plugins with JavaScript and C++. They own the new player client experience - this includes what you see when a new player logs in, and the new player experience rewards hub. We also took on the hub for Teamfight Tactics, which I got to help ship!

Technology Interns at Riot Games

During my internship, I spent a lot of time doing front-end development for different plugins in the client. I built the game mode select screen for new players coming in after TFT’s launch, worked on the celebrations in the TFT hub, and added to a couple different components in the TFT and new player hubs. I got to learn about toggling features in the client with JMX toggles, and even got to stand up an entirely new plugin for a new experience for “revived” players.

Technology Interns at Riot Games

I also worked on the next version of rewards for new players, which just shipped, and a hub for revived players which is expected to ship soon!

Technology Interns at Riot Games

TFT & the New Player Experience

My first task for the new player experience for the launch of TFT was definitely the most challenging. Because there are a fixed set of actions we expect a new player to take when they log in to the client for the first time, there were a lot of adjustments and re-factors that had to happen for me to add the ability for new players to only see TFT-related content. Since most things happen asynchronously on login and “Home” has always made sure it was shown first, it was difficult to force the TFT hub to be shown instead.

For what seemed to be a simple feature, we ended up having to refactor parts of Home to make sure there was one controlling entity defining what was shown first in the client. There was too much inconsistency in allowing any tab to request to be shown first, especially since there was no real mediator or security around accepting or rejecting those requests. This was a really great opportunity to clean up some tech debt while building the feature we needed!

In general it was really interesting to me to learn about web from the perspective of a client that can be open for indeterminate amounts of time. Most of the web work I’ve done has been on service websites that expect to be manually refreshed. The client being constantly active, sometimes in the background, leads to a lot of interesting considerations around watching for events and caching data.

Conclusion

This summer, I really enjoyed participating in Rumble - the Riot internal League competition. We made it to Gold semi-finals, which was a really cool accomplishment for my first go at competitive play! Beyond that, I really appreciated Riot’s commitment to supporting women and minorities in tech. Getting to hang out with the Girls Who Code on campus, and seeing how excited and passionate they were after spending a few weeks surrounded by supportive Rioters was uplifting.

Tooling and Infrastructure

Vighnesh Vijay, David Long, Brian Chen

Roles: Software Engineers

Team: Riot Platform Portal

Hi, we’re Vighnesh, David, and Brian, and we worked on different parts of the same team, which primarily builds out tooling to support other teams who access player account data. We manage how data is stored, access policies, and compliance with international data laws. The project our intern group worked on this summer defines how internal and external teams work with our team’s data by specifying creating a corresponding client that defines its access needs, and mapping data claims to use cases. We’ll each describe our projects here.

Vighnesh

Hi! My name’s Vighnesh “Notorious VIG” Vijay and I’m a student at the University of Pennsylvania, and I’m a software engineering intern. My team works primarily to facilitate the efforts of teams who deliver content to players, but we own quite a lot of services!

We work with a variety of tech - we use Golang for our microservices, C++ for our patching tech, and Typescript for our frontend work. Our project is a self-service tool, composed of a frontend and a web-service that enables devs to more easily integrate Riot Sign On in their apps.

Architecturally, our project makes extensive use of gRPC, a tool for specifying the structure of a Web Service, as well as Bazel, a build system for making easily reproducible builds of a project. My role in the project was mostly on the frontend, mocking out initial designs and architecture for the project, as well as working with CI and build systems.

The thing I’ve most enjoyed about Riot is that it’s like being in a theme park with the people who build the roller coasters - it’s awesome to see the passion and love people have for games in the office!

David

Hi! My name is David “SushiKilla” Long and I’m from the University of Illinois at Urbana-Champaign, and this summer I was a software engineering intern. My team works on connecting players to games - we own the Riot Client and the various technologies shared across Riot’s content.

Over the past few months, I’ve been working with Vighnesh and Brian on building a tool to manage Riot’s OAuth clients. OAuth allows users to authenticate with a different service (i.e. log in with your Facebook account). One of our use cases is allowing League of Legends players to link their accounts to their Twitch account. In order to do this, Twitch needs to get an OAuth client from us. The tool we built makes it easier for developers to request OAuth clients and specify their use cases.

For the project, I primarily worked on the service backend. During this project, I learned how to code the service in Golang and used the gRPC library to define all the endpoints that our service supports. I also had the chance to work with database operations using SQL and learned how to set up a full continuous integration pipeline using Jenkins (defines how to build the service), Docker (makes the service portable), and AWS (hosts the service).

Not all my work as a software engineer was technical. When we first received the project, we had a rough idea of what the goal of the project was. From there, we worked together on gathering requirements by talking with engineers and project leads. We also discussed which functionalities and requirements should be prioritized, and whether they should be must/should/could/won’t. Additionally, after we demoed our MVP implementation of the project, we were able to gather valuable feedback and make our project production ready.

The thing I’ve enjoyed most about working at Riot Games has been the people. Everyone on my team was a pleasure to work with and very helpful. If I ever had a question, they didn’t hesitate to walk me through the steps. It’s very easy to get feedback, which lets me know what I’m doing well and also gives me opportunities for growth. It’s also cool to see people playing games after lunch or right before dinner. I also got to participate in Riot Rumble, Riot’s internal League tournament, which was a blast.

Brian

Hey! I’m Brian “Ranma” Chen, a senior Computer Science student from the University of Toronto, and this summer, I worked as a software engineering intern on the Player Accounts team. Our team works with player account data closely, and monitors how other teams access and use this data. As part of the team that “owns” player account data, I was in charge of collaborating with other teams to define how this project would be structured.

My work was focused on the front-end development of this application. I worked with Vighnesh and end-users to create mocks, wireframes, and workflows that mapped out how the project should look. In the end, we after deciding the general direction of the project, we followed Material Design guidelines and created the frontend using Node, React and Redux.

This was a very different experience compared to what I was used to - I’m primarily a backend developer, and only had sparse frontend experience working on personal projects. Working with React and learning how to appropriately pass state, props, and compartmentalize components was an interesting experience. Additionally, halfway through the project we decided to implement Redux to handle state management, which was also something new to me. Learning how to refactor state to use Redux, and how it changes the development paradigm was also an interesting challenge.

Riot was a great experience overall. Aside from the technical growth and mentorship, I really appreciated the community and open culture. People here are always open and excited to talk about their work, and explain the nuances of their projects. It helps give a lot of context for me to the work we’re doing as an organization. In my time here, I’ve spoken to gameplay designers, VFX and art specialists, software architects, and many other amazing people. Getting that bit of insight from every discipline helped me grow my perspective and understand how large organizations work together to deliver one of my favorite games.

Technology Interns at Riot Games Technology Interns at Riot Games

Chunlok Lo

Role: Software Engineer

Team: Foundations

Hello! I’m Chunlok “BoxedCube” Lo. I’m studying Computer Science at Georgia Tech and this summer I worked on the Foundations team. We’re responsible for the Riot Client SDK - the client backend that handles login, chat, launching your game, and other services that connect players to our games.

The Riot Client SDK is built in C++ with many exposed microservices APIs. We work to maintain the health of the SDK and ensure that we provide a stable platform for game teams to develop their games on. We work closely with game teams to make it as easy as possible to integrate with our SDK. This summer, I was responsible for extending integration testing to ensure our changes don’t block game teams. I also worked on a few other features such as rewriting our player preferences to be a higher level service that game teams can more easily interface with and scoping out our team’s need to support an R&D project.

Robert Lin

Role: Software Engineer

Team: RDX Operability

Hey! My name is Robert “BobHead Bot” and I study mathematics at the University of British Columbia. I’m a software engineering intern on the Riot Developer Experience Operability team, which aspires to simplify the foundation for developing and operating services, empowering teams to focus on their own problem spaces.

Operability owns a variety of systems within Riot Games, though our current work mostly pertains to operational monitoring - providing engineers at Riot visibility into how their services are performing and behaving across the globe.

This summer I worked on two projects in this space.

The first was designing and building an extension to Riot’s deployable artifact specification to allow declarative specification of alerts on services. The specification allows operators in various Riot regions a way to discover how to deploy and monitor a service. This extension would give these operators additional context on what metrics emitted by a service are important to track, while giving engineers in Riot regions an automated way to deploy alerts through our deployment service.

The second project was a tool for holistically looking at all alerts firing across Riot, and constructing a graph of them based on the associated data centers, related applications, network topography, and more to aid in triage and root cause analysis of events.

Extension

For my first project, the work was split into several parts: designing and implementing the extension specification, and implementing the complete deployment flow for alerts. The specification design involved weeks of back-and-forth between various teams to ensure that the format was expressive enough to accommodate current use cases, compatible with our existing infrastructure, and extensible enough to allow future features as we migrate to a new metrics warehouse. The specification implementation was also a bit of work - our application specification format has a wide range of “override” mechanisms to allow operators to deploy specific instances, environments, and more that the extension had to work with, as well as a feature I added for meta-configuration that had to be evaluated at deploy time. To deploy these new alerts, I had to implement additional capabilities within our alerting engine to translate the new specification to our existing infrastructure and deploy them appropriately.

Technology Interns at Riot Games

Visualization

My second project leveraged a graph database to generate a visual representation of a sliding window of recently triggered alerts. Doing so allows engineers to ask questions about alerts based on relations. For example, how soon was alert A fired after alert B? Do they have a network dependency between them? Are they owned by the same team? Do they all happen to be in the same datacenter? The goal was to help engineers triage the root cause of issues that might cause a cascade of alerts across Riot’s microservice ecosystem through a flexible visualization tool.

Technology Interns at Riot Games

Highlights and Challenges

The nature of my alerts extension project meant I got the opportunity to work a lot on Riot’s deployment service, which is a behemoth of functionality designed as a versatile set of extension and jobs for deploying services out to a variety of environments. Adding to a large, comprehensive system like this was a pretty awesome experience, especially thanks to the amazing support I got from Service Lifecycle, the team that owns the service. It was also a neat demonstration of the complex, varying demands that the diverse teams at Riot have, and how a seemingly simple problem space can quickly sprawl out into a huge set of use cases that have to be accounted for.

Another interesting challenge was a feature I added to allow teams to define alert thresholds based on other configuration in their specification and perform arithmetic on them. This involved special processing for evaluating selected variables using a custom syntax and feeding it to an arithmetic library for evaluation, and adding the appropriate hooks to the deployment pipeline for evaluating the configuration at deploy-time and reflecting them to the user for verification.

My alerts visualization project was a great sandbox experience for me that allowed me to try my hand at leveraging all the tooling that Riot’s ecosystem offered - one-click deployment to anywhere, logs, metrics, service discovery, networking, and my own alerting extension - to build a service. Designing a graph representation of alerts through a network of “quads” (each of which represent a subject, predicate, object, and subgraph label) that was ergonomic and performant to query required a bit of a change in mindset as well after my time with relational databases.

Technology Interns at Riot Games

Conclusion

My favorite aspect of Riot is how everyone is happy to talk about their work and guide you through making contributions - my project spanned codebases owned by several teams, and the support I got throughout the whole process was great. I felt like I had the freedom to propose new things, changes, and even entire projects, and that I could count on my team’s support even when I am wrong. Although our work is not directly player-facing, the regular company-wide meetings, updates, and AMA sessions gave the sense that as a company we were a single team working together towards the same goals, and that everyone had a voice and were key to achieving these goals.

Sahil Ashar

Role: Systems Engineer

Team: Core Infrastructure Team - Data Center

Hey y’all! My name is Sahil  Ashar (or Riot Ashar), and I’m a senior Electrical Engineering major at The University of Texas at Austin. This summer, I was the systems engineering intern for the Core Infrastructure team.

Team Context

My team wears many hats, and is responsible for a lot of different things across the board, but our core function is to design, build, and deploy infrastructure of any scale to any region on the planet.  For our team, a lot of what we do is broken down into two buckets - emergent and operational. 

Emergentwork is unplanned work that comes up, either from internal teams at Riot or externally from players. A good example is the Teamfight Tactics launch in early July, but I’ll get to that later. 

Operationalwork is stuff that our team has planned throughout the year, i.e. projects and features that we plan to ship so that teams internally or players externally, can feel an improvement in their lives.

Information Consistency

This summer, I primarily focused on operational work, with a little bit of emergent occasionally sprinkled in. A big problem I came across during the first few weeks of my internship here was the lack of information consistency we had on our team - a lot of information was all over the place, and was managed manually. There was no easy way to view every server in every datacenter across the globe, and see a complete breakdown of it. There was a lot of jumping across services and hunting for information that should be easily accessible. So my goal for the summer was simple: develop a consistent data pipeline for all of Riot’s servers globally and ensure that we had a single source of truth for all of our servers.

After my initial discovery phase of “ Where is every single place that Riot stores information about the servers?” I came up with a high level design of the two products I wanted to build.

The first product is an automated hook from our server provisioning services (which we’ll call those Imaging System 1 and Imaging System 2) to our inventory management service to create a single source of truth within that centralized management system.

The second product is a custom API Layer for our external services - like our preferred monitoring service, messaging service, and ticketing services - to use in order to bypass the complications that the inventory system has, and to streamline it for our needs.

Technology Interns at Riot Games

Automated Hook

The requirements for the first product were tough; I had to build a fully independent, automated service, that could be deployed anywhere on Riot infrastructure and give our team an accurate view of what hardware was in that datacenter, what the specs of the hardware were, how it communicated with other Riot hardware, etc. I then had to ensure that this was the only version of that hardware that had ever existed and that there was no duplicate somewhere in the system, so that we could ensure that we had an accurate view of what servers were being used where and for what purpose (like running League of Legends ).

I ended up building a container that lives in Docker/Jenkins and hosts this service. Every two hours, it cycles through every datacenter and pings every server on a specific subnet for that datacenter. After it collects the IP Addresses of the servers that send back an “alive” signal, it then SNMPwalks each of those IPs and collects specific information about that server. It cross references that information with the information hosted on our server provisioning tools (e.g. Imaging 1 and Imaging 2) and flags these servers as new or as previously existing. After all these checks, it does a variety of API calls to our server provisioning services to get a full picture of what this server looks like, creates a profile of this server, and uploads this server information to our Inventory System, which then serves as our single source of truth.

Custom API Layer

For the second product, I built a Python library that wrapped certain functions that we needed from the Inventory System into a single call. For example, trying to retrieve a server by name from that system can take up to eight clicks on the GUI, three API calls using the standard system’s API library, but only one API call using the custom library. My aim was to build for our specific functions, and reduce the amount of extra, unnecessary work my team was putting in to retrieve information.

Conclusion

I loved my time at Riot, and I especially loved the work my team did. My favorite moment on the Data Center team was probably the night of the Teamfight Tactics launch. I could write a whole article on how we discovered issues leading to long queue times and what my team did to help mitigate that and eventually fix it, but the bottom line was that they had a nearly 20 hour day at Riot, working as hard as they could, to ensure that players could enjoy TFT the way it was intended. Being a part of that experience of live fire-fighting was incredible, and truly cemented what Riot’s “Player Experience First” motto meant to me.

That’s a wrap!

Riot internships are all about helping players connect their love of games to meaningful careers. Technology interns dive deep into the code depths of projects in a wide variety of departments, whether that means working on League or R&D games, or contributing to Riot-wide developer tools or microservices and deployment products. The internship program also provides current Rioters with the opportunity to mentor a new generation of professionals, and the common thread through the intern blurbs above are appreciation for the Rioters who helped them along the way. 

We’re super appreciative of all the hard work the interns did this summer. Thank you, interns, for helping us shape the next decade of Riot games for players across the globe!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cyberwar

Cyberwar

Kathleen Hall Jamieson / Oxford University Press / 2018-10-3 / USD 16.96

The question of how Donald Trump won the 2016 election looms over his presidency. In particular, were the 78,000 voters who gave him an Electoral College victory affected by the Russian trolls and hac......一起来看看 《Cyberwar》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具