 I don't have a routine. I feel pressured now. This might be the most boring talk. I hope it's not, but I at least don't have a routine. I also don't have any entertaining gifts, unfortunately. Thank you. So thank you for the introduction. My name is Katherine West. My talk is called Using Rust for Game Development. And since, you know, probably a lot of you here aren't game developers, it's what you can learn from it, even if you're not a game developer. So the genesis of this talk is that, you know, on IRC or Discord, I keep getting these questions. You know, like, how do you make a game from scratch in Rust? I've tried, and maybe I run into, you know, the bar checker complaining. Or I try to make a game and I need internal mutability. Or maybe it's just like, they've tried this and they seem skeptical because they think that the bar checker is very limiting. And I think that these are all kind of, these three questions are kind of the same. This is like beginner fighting the bar checker. This is intermediate fighting the bar checker. And this is advanced fighting the bar checker. But, you know, obviously in case it's not clear, because I'm here at Rust conference, at RustConf talking about game development, I don't necessarily agree with these. But the trick is, is that, you know, you want to give an answer to these kinds of questions. And, you know, sometimes a small answer is not appropriate for a question that's about what happens when you grow something beyond the small. So this is my medium sized, oops, I've already messed up. This is my medium sized answer to this question. So before I go any further, I'm going to have a lot of code in these slides and I really am sorry if people in the back can't see all these slides will be available online. But even better than that, I actually didn't write this talk for RustConf. I actually wrote a long form blog post of this. This is way longer and probably way more interesting. So I'm going to make that blog post public at some point in the next week. So if you don't like my talk, you maybe will like my blog post. And if you can't see, then the blog post will be there. So I think the real reason that I get a lot of these questions is that Rust does make certain patterns more painful than others. This is a great thing. This is probably my favorite feature of Rust, is that it gives me this pressure not to do the easy bad thing. And what I found is, is that these things that Rust kind of guides me to do are oftentimes the easiest generally, whether or not I was written in Rust. And the reason I know this is because I had to learn it the hard way. And I didn't have Rust help. I'm going to talk a little bit about the game I worked on at a charcoal festival called Starbound. I'm going to pick on myself a lot. I made a bunch of mistakes, and I'm going to talk about them. They're all my fault. It's easy for me to pick on myself. And what I should have done, like one of the things I should have done is I should have used something like ECS design. And maybe if I'd used Rust, it would have given me the right guidance and pain to maybe pick these better designs faster. So if you don't know what ECS is, don't worry, because we're going to build one. So this talk is going to be working through a game engine from the simplest it can possibly be to something that I think is appropriate for like a mid-sized project. And it's going to have an ECS in it. And the reason that this works so well with Rust is that Rust tends to reward data-oriented design. And I might be using this term a little bit different than a lot of people use it. If you go look up on the Wikipedia page for data-oriented design, you'll see a lot of things about like cash locality and cash behavior and performance and things like that. But I don't actually think that that's like the most important part of it. And I think it has a lot more meaning for a language like Rust. But I also think that these are kind of two sides of the same coin, but we'll get into that. So what is a game? What is a video game? It's simplest possible form. You make a game state. So this is obviously very, this would be very complicated and large. And your entire game fits in this one structure, right? So players, entities, monsters, whatever goes in this one structure. And then you go into a loop. So a video game, you take input, right? Like from a controller. You take input and then you run a bunch of code to update your game state based on the input you got. So if you're jumping, you press the space bar or jump button and your player starts jumping. Then you render what you got and you wait for the next frame. Repeat. Shoot. Sorry. Okay, I need to stop clicking that. So then you repeat. So this is the simplest possible game. You can't even call it an engine. You make a game state. You run a bunch of code over it. Repeat. So what can we learn from this ultra simplistic example? One, I don't necessarily recommend you make a game this way. But you could make a game this way. I promise you can make a game this way because I've seen them. I've seen these source code to unreleased games that are written this way with procedural, ball of mud, 12,000 line, world generation method. I want names, but I've seen them. And if you did it, you'd have minimal problems with the borrower tracker. And a little bit of thought will tell you why. One of these procedures that you run over your game gets complete and total mutable access to the entire game state. So if you have complete total mutable access, you probably aren't going to run into problems with the borrower tracker because you borrowed everything. And one of the things that the Rust compiler will do for you is it will allow you to split borrows. So you have a structure with a bunch of fields. You can borrow one field and then mutably and then borrow another field mutably. And as long as it's a public structure, Rust understands how to deal with that. But it has some downsides. Yet, everything is in one giant, morally global mutable mega-state, right? Everything is procedural. You could very easily end up with a procedural ball of mud where your input system, and this is supposed to make the player jump, but I could just lean over here and just mutate the physics state because nothing is stopping you since you have everything borrowed at once. So there are some downsides. But it's important to keep this in mind because we're going to try and improve this. But we know this could theoretically work, and we're going to improve on it. We're going to improve on it. First, we're going to improve on it in the wrong way. And we're going to do it with the example of Starbound because I did it. So we're going to try object-oriented design in order to improve our game engine. So what are the principles of object-oriented design? There's the single responsibility principle, which says, okay, you should take your data and you should bundle your data up into logical components, and then you should take the functions that operate on those components and call them methods and bundle them together, and it's called a class. Encapsulation says that the data that you bundle into a class should be private by default and you should lean towards privacy because somebody else might mess with your data and you might have to mess up invariance and then you should lean towards accessing that data through members. The abstraction principle says that rather than depending on having one class spawned on another, you should lean towards depending on some kind of abstract interface or like in C++ it would be like a pure virtual interface. And this is related to the last point, minimal coupling, which says that if you shouldn't couple the design of one class to the design of another and instead interact through these pure abstract interfaces. So let's try this and see how it works. Spoiler, it doesn't really work that well, but we'll get there. So this is kind of a, sorry if it's hard to see back there, again all these slides will be available. This is kind of like the smallest possible star bound game state. So you have three entities, and if you don't know what an entity is because I'm using all this game dev jargon, like if you have a world, everything that's static doesn't move, you can't interact with it, you can't do anything with it, but you can maybe stand on it, that's your static data and then anything you can interact with in games is usually they're all entities. So in this case, our entities are the player, the thing you control, monster, which may be like a little critter going around that you shouldn't touch because it'll damage you, and then maybe some NPCs that you can talk to. And then we have our game state, which is the struct that we made that's going to contain the entire state of our game. One thing to note here is that our game state is like a list of all the non-entity data that I talked about, and then this list of entities, and we've got this, if you don't know C++, I'm sorry, but hopefully it's not too distracting, we have the shared pointer to void because we don't know what to put there yet. So we have entities as an NPC, a monster, and a player, so the type system's kind of in our way, so we have a shared pointer to void, and we're going to improve this in a sec. Another thing to note from this example is that one of our internal states, I have this, I'm going to use this a lot, and this is going to end up being important, but there's this entity index type, and this is actually really interesting because this is super common in C++ game engines. In fact, I've never seen a systems programming language kind of game engine that doesn't have these, and this is a C++ example, so you might think like, okay, well this field is player ID, so server is a multiplayer game, you need to keep a list of all the entities that are a player, but why don't you use a pointer? It's C++, we can live dangerously, let's keep a pointer, and the thing is that nobody does this because it's wildly unsafe. If you keep internal pointers, they will become invalidated and your game will crash. So nobody in the systems game development environment that all game engines have this, it's either an ID or some kind of index or something to identify entities, and this is important because we have to do this in a lot of places in Rust, but it's the best idea, and it has other benefits like, from network serialization, it's hard to serialize a pointer, but this is very common in all game engines, and this is kind of an important thing. So we had this shared pointer to void, and it really sucks because we can't do anything with shared pointer to void, so let's make this, we're doing object oriented design, so let's make this interface called entity. So let's very quickly think of all the things that will be common to every single entity. Position? I'm done, I can't think of anything else. In our example, all of the player, the monster, and the NPC all have velocity, they all have physics, but maybe you have a stationary entity. It turns out that, maybe you have a stationary entity. It turns out that there's not very much in common, almost at all, so Starbound actually has an entity interface like this, and it's filled with a bunch of methods that all return basically option because some entities don't have them, and then you're kind of defeating the purpose of having an entity interface, but anyway, but one thing we can do is we can give them methods. So we're trying to do object oriented design, so let's say that every entity can handle input state, so like a player might start to jump up to space, and then every entity has to update themselves, and they have to take in the rest of the game state, so like if you're a monster and you're tracking a player, you have to know where the player is, and you have to maybe know where other monsters are, so you need the rest of the game state, and then they can render themselves somehow, and we're not really going to talk about that. So then the player, you know, implements this interface, handles input, handles update, handles render, monster and NPC are exactly the same, and now we can keep a shared pointer to entity instead of a shared pointer to void. Okay, this seems like it could work. New requirements start coming in, so you make a video game, you're constantly going to be experimenting, constantly going to have new requirements. Monsters can track players because they know the player's position, but they should track, because it's a multiplayer game, so that's okay. All right, well, health is private to you player because we're practicing encapsulation, so that's okay, we'll make an accessor. Not a big deal. More requirements start coming in. The monster should not go after players who are marked as admins, so start by on some multiplayer game, we have this concept of admin, it's not super important, but okay, we can make more accessors. Not a big deal. Well, admin is admin. No problem. Okay, well, we have to continue on with development, so let's, we need monsters to start damaging players, so if a player gets too close to a monster, you know, it has some kind of geometry around it, and if the player's geometry overlaps with the monster geometry, the player should be hurt. Well, okay, so let's think about this. Where do we put it? We're doing object-oriented programming, so we should take our methods and apply it to the place that it makes the most sense to go. So I guess the players damage themselves. But you could also say that monsters do the damaging. They're the thing that does it. There's not actually a great place to put it, so we can pick one. We'll just pick player, players damage themselves. The player will check its own bounding box with that of the monster, and it will decrement its own health. If we did it the other way, then monsters would have to have access to player's health, which, like, mutable access, that doesn't seem right either. So we'll do it this way, and we need more accessors because, you know, now the monster has this damaged region and the player has to check it, so one more accessor, it's not a big deal. So, okay, so you're making a stealth game, and if the player walks next to a monster and they're on the ground, the monster might react in some way. That's no problem. We'll add an on-ground accessor to player. Except we've been practicing good object-oriented design, and our player has this sub-object, right? You know, we know that we shouldn't use inheritance. We've been told that a lot. You know, inheritance is by wish-you-use composition. So we've composed our entities with this physics object, and only the physics system knows whether you're on the ground. Not a problem. So we'll add accessors to physics, and we'll add an accessor to player that just passes on to the accessor to physics. And as you can see, you kind of need more and more of these, and then the more you do these patterns like composition, the more accessors you need. How many could you possibly need, really? Well, this is the four real actual starbound player class copied from the starbound source code as of the latest version. You can tell that we've been following good object-oriented programming, and we've been using a whole bunch of these pure virtual interfaces, because, you know, you shouldn't have something dependent on the player, necessarily. You might have something else that can emote. So, you know, I think there's eight or nine of these. There's actually more like 35 of these interfaces inside starbound. There's a bunch of them. So let's just quickly go through all the accessors in this class. So, yeah, you got to know how much energy the player has, you got to know how much breath they have, their protection, whether they're forced nude. What kind of game did I make? Okay, that's four-something. I know it's four. It's four like the player creator or something like that. And if you're nude, you know, you just want to be nude. Keeps going and going and going. Oh, whether they're an admin or not. It was up there somewhere. I don't remember where it is. Is admin, yeah. Oh, whether they're dead. That sounds important. Okay. Okay. There's like 400 lines of these. The number of methods to the player class is like 200 lines longer than the number of members in the player class somehow. I don't know. This one's great because this is some like sub-object for like the ship's AI. And we just gave up. And now like, oh, you can get it, you know, immutably because it's an accessor and also we need to mutate it and I'm tired of writing methods. So we just, like, there is literally no difference between this and just a public member. So this is, don't do that. Don't make games that way. But we're going to try this in Rust anyway. And we're going to see what we can learn from it. And what you're going to find is that this kind of design fails quicker. And there's a reason for this. Okay. So we start with the simplest object-oriented C++ version. We have an entity interface. And, you know, we have our game state list of entities. We're going to translate it to Rust. And now we have an entity trait. And the list of entities. Very quickly this vector of option, you know, we have a box entity because it's a trait. So we can't keep a direct copy of it. But also just, you know, like there's this option here because I'm keeping a vector of entities. So, you know, you might remove an entity and the option becomes none. Then you allocate a new entity. You have to go find a none to put it in. And then the rest are going to. So, okay. Right away. This is already wrong. This is already broken. So an entity writes to update itself, right? Gets a mutable reference to self and a mutable reference to game state. Even if it got an immutable reference, this would still be a problem. Because an entity gets a mutable reference to self and it's also inside the game state. All of our data in the whole game is in this game state. That's mutable aliasing. You can't do that. So right away we try to make a method and it doesn't work. So there's a bunch of ways you can solve this and most of them are really bad. You could do things like remove the entity and then call update and then put the entity back. It doesn't work. The best thing you can do, which is bad, is to try interior mutability. So we have ref cell. Now all of our methods take immutable references to self. And this sucks if you try to do this. You go on IRC and you say, why do I need this? Rust sucks. I need ref cell. You're fighting the borrower checker. It's helpful, but not necessarily helpful. If you've ever found yourself in that situation, this talk is for you. So it gets worse. Say you have this entity trait that's pretty sparse. This is something that actually shows up in Starbound. We have a tag system. We have everything in Starbound. We have this tag system where you can get a list. Maybe in Starbound it's like a list of strings. You have this tag method. I've unallieded the lifetime, so it's clear. But this borrows the whole entity, no matter what, and returns a reference to tags. So already this is a problem. That's a lot of borrowing. And maybe if you want to get the tags from an entity and then do some operation that involves mutating the world, maybe that's a problem. And it's even worse because if you need internal mutability, you can't return a reference. You have to return a ref cell ref type. This is really hard. It gets worse. The larger your game state, the more you borrow. So we have this game state. In Starbound it's like a 2D block-based building game. So we have a method like this in our version of this where you get a block based on some integral index and it returns a block. Well, unallieded, this borrows the entire game state. This is a problem. You don't want to have to call a clone. You don't want to borrow that much. But the fact that everything is private is getting in your way. And this is worse with traits. So I mentioned this because Starbound has this as well. We have our world class, which has a million methods in it. And there's a world server and a world client. So if you're the server, you talk to a world server, you talk to a world client, and they both implement this interface. And I'm trying to improve the performance of Starbound. I've been doing a lot of perf work. I need the world to return a reference. Everything is terrifying. In C++, this is used after free. In Rust, it borrows absolutely the entire game state and then you can't do anything and you're forced to clone it. So it's not very helpful. So what are the takeaways for this? This is kind of common knowledge in the games industry, actually. But in case you haven't heard, object-oriented programming kind of hurts more than it helps. And this is kind of insidious because in a game, you have these objects and it calls out to you, right? What's an object? Well, player, that's like a physical object. Monster, block, right? And these sound superficially appealing, but if you keep going, it's actually quite harmful. Most of your concerns end up being cross-cutting concerns. Things like damage or collision, right? Multiple entities collide with each other. It doesn't make sense to have a collide method. And this bad design that we talked about fails quickly in Rust. You start getting pain points. It's like the Rust compiler is telling you that maybe this is not a good idea. And one of the takeaways is that I find it helpful to force myself to think about things as just data, like to think in a data-oriented fashion. And by that I mean just concentrate on the representation of my game state and don't conflate the methods that operate on the data with the representation of the game state or the state of whatever you're doing. Okay, so this is a bust in Rust. Let's go back to the beginning. So this is kind of a Rust version of our first data model for Starbound. You have a player entity, you have a monster entity, you have an NPC entity. They all have some physics. So we're not going to do the shared pointer to an interface thing. We're not going to do box entity. Let's instead use an enum. So this is all of the kinds of entities we have. And then in our game state, we just keep a vector of these entities, which are either player, monster, or NPC. Then we have this basic update structure that we had before. So cards on the table. This is fine. If you're making a game jam game, probably stop here. If you want to use an ECS, that's fine, but this is not so bad, especially for small things. Yeah, your entire game state is available to everything, but it's actually fine. So I wanted to mention this because I'm going to explain a couple of steps beyond this and each one of them might be a stopping point if you wanted to make a game engine home scratch. So one thing I wanted to point out, though, is that see this physics system? So the physics system needs to go up, go through every entity that has a physics member. But here, there's this enum here. So all entities have physics. Or maybe even most entities have physics. We add more entity types. But the physics system needs to understand every single entity type. It needs to match on the enum and then see, it needs to know which of them do have a physics type here. It won't compile. And you have this, like, it's less, it's not that easy to, like, deal with a subset of entities that have the same state. We can fix this, though. We can unify our entity type. So instead of having an entity be an enum, we can take every member state that an entity has and we'll just shove them all in one base structure. So, you know, most entities will have physics, but, you know, this isn't, you know, option physics. And then entities might have health. And then we have all these, like, player, monster, NPC, like, types that were there before. But we're just, like, if you're a player, you have the set if you have a monster out of the set. So this is useful. But what's even more useful is if you generalize this a bit. But before, you know, a monster had a current target, right? Like, in this slide, you know, a monster can be aggressive towards a current, towards some kind of player. Well, here, we take this out, and we make it its own little state. So we've just kind of generalized our fields a little bit. And sometimes you can't do that, because, you know, we still have stuff that only makes sense for a player. But this is kind of interesting to think about because it's a give and take, but this allows you to express more states than the previous one. In this, you can express an aggressive NPC. And maybe that's good. Maybe you would want that. But you can also express an aggressive player by setting the player to, setting that, this field, and then also setting the aggression field. And maybe your engine just doesn't know how to handle that. Maybe that doesn't make any sense. Maybe aggression only makes sense. Like if you have an AI system and the player's not run by AI. So it's important to remember that this is powerful, and you can do this, but it's a give and take. So you can go further. And this is usually the part where, if you're reading an explanation of what an ECS system is, they focus on. Because they look at this as purely from the lens of performance. So this is kind of confusing. So let's go back. We have this entity structure with all these optional fields. And then we have this vector of these entity structure. We're taking it, and we're saying that instead of having a vector of structs, we're just going to have all of the vectors for each of those parts in one top level structure. This is kind of called the array of structs to struct of arrays, transform if you're into data-oriented programming. And we're going to do this transform. And we're going to do one more thing where we give all these parts a more informative name. We're going to call them what they are, which is components. So if you've been paying attention, we now have entities and components. And our systems are just these procedures that operate on a game state. So we're getting close. So let's see. What are the takeaways so far? Thinking about the structure of your state is really powerful, and not thinking necessarily always about the methods you've decided to apply to that state. Another takeaway is that you can do a lot with vex and indexes into vex. The reason I mention this is because I see this a lot, actually, not just in game programming, but you need to make a graph structure or something and you need to update it. When you go online, people give you advice about using arena allocators or RC and internal mutability. And those are great. Those tools have their place. But seriously, seriously, seriously, just put your nodes in a vex and use indexes. It is so easy. That should be the first tool you reach for. However, it still has some problems. So if you do that, and I do that, when you get an index to something in your vex of members or your vex of nodes or whatever it's a graph, and you delete one, that index is still valid. And if you think about this, this is kind of like use after free. If you can think of your indexes as pseudo pointers, if you free a node, like if we freed an entity and then used an entity index to get that entity, hopefully we would notice that it was gone. But if we allocated an entity right afterwards, then we might not notice that it's gone. We might get a random other entity. It's safe, but it's not great. We can solve it. So this is a pattern that is super common in the game dev community, and I don't think enough restations know about. So I included it in my talk. We're going to talk about generational indexes. And a generational index is just this index we were talking about, like entity index with one extra field, which is called a generation. So if we had an allocator for such a thing, we might allocate a generational index, and it would give back like a zero with a generation of zero. Allocate another one. It gives back one with a generation of zero. Deallocate the zeroth index. Allocate another one. We get back an index of zero. So all of our indexes are small, and we can store them in a VEC. But we never repeat, our invariant here is that our generational index allocator will never return an index that is ever equal to a previous one. I mean, unless you make this generation overflow 64-bit ins, but if you can do that, your game technology probably has cloud in the name, so you probably have bigger problems. But these are useful because they solve this use-after-free problem of indexing. And to go a little bit further, we just need one more thing, which is this generational index array, which is just like an associative array of these indexes. So instead of a VEC, it's just like an easier version to deal with than VEC of option of T. You can set values based on generational index, and if you get them out crucially, if the generation must be up to date. So if you have an older generation, you get back a none. So putting it all together, instead of having these VEC of options, we're going to have this entity map, which is just a type def of this generational index array, and then we're going to rename entity index to entity, because it's no longer... We don't have to worry about it anymore. There is no such thing as an entity anymore. So entity index, entity ID, we're going to call this index entity, and we're going to keep all these entity maps of all of our components. This is an ECS system. Takeaways. Generational indexes are awesome if you don't know about them. They solve a whole lot of the problems of regular indexing for doing this internal mutability. There's a crate for it. It's called Slotmap, and I couldn't use it in the talk, even though I really, really wanted to, because it's missing a key feature. You can't, in Slotmap, allocate these indexes separately from using them. You have to bundle them all up, and it gives you kind of a structure, but you can't allocate the indexes separately. If you're the author of the Slotmap crate and you're at Roscomp, I am super sorry for calling you out on stage. But, you know, I have another version of this. I can release that as a crate. That sounds like a threat. If you don't make this change, I'm going to release a competing version. I didn't mean to sound like a threat. So, okay. One more set of transformations, and we're done. So, dynamic typing. Everybody loves dynamic typing. You know, it's great in very tiny quantities. But first, we need a type. We need this structure called anyMap, which is great because there's a crate for it, and it's not missing any features, and I don't have to call anybody out on stage. So, an anyMap, very briefly, is a map. It's like a set of types where you can store exactly one of any type. So, you can put a T in, and then if you put a T in before, you can get it back out. It's just that simple. So, this is where we were before, and we can see every time we add a component, this list is going to grow, and what actually ended up happening is we're going to list all of our types a whole lot, and we're going to find a pattern to deal with that. But just in this one instance, all of these components, adding to this list can get tedious, and every time we add, we are potentially touching a dependency of every single system that we have. So, let's give them an anyMap. So, the type system stops being helpful when you use dynamic typing. So, we're going to have these entity components, but they're just these collection of entity maps of type T. And let's go even further, and we'll keep all of our non-NST data in an anyMap as well. And we'll call those resources, and we'll change our name from GameState to something more informative, where we'll just call it what it is, which is an ECS system. We have these anyMaps, the type system stops being helpful. So, you know, you might have an interface like this. So, you can get our component out based on some type T. You can get a resource out based on some type T. And we've added these component and resource traits, which I'll talk about in a second. But this is an ECS system. So, you might balk at this, like, sudden introduction of dynamic typing, and you would have a point, kind of. This is not vastly better than what came before, until we add one more pattern. And I don't... This is common, like, outside of Rust, but I actually don't know what to call it inside Rust, so I'm just going to call it the registry pattern. So, we're going to make a registry for all of our components. We're going to have this structure called a component registry, and we're going to call register component for every single component that we have in our whole game. And we're going to do the same for resources. And, you know, this... We have to keep a bunch of stuff in here, like, you know, box functions, and it gets kind of gnarly. But if your component has... If this component trait has methods on it that allow you to, like, load from a file, what you end up being able to do is being able to have these methods on your registry that says, hey, go take an ECS and put every component that we know about in our game in the ECS. And then you can also do this where this, like, load entity, say your component trait knows how to load from a JSON blob, you can say, hey, go load an entity into my ECS based on this config file. So this might be, like, some kind of array of all of the components, and then it gives back the entity after adding them all. And you can do the same exact thing for resources where you set up all your resources and then you can load any given resource. So then, because everybody loves globals, we can take all of our registries and put them in a big, top-level registry. I'm joking, but this is actually fine, because, I mean, this is kind of, like, enterprise-y, but, you know, you might have seen something like this even in, like, Java, where you have, like, a list of all your types, and it's super useful to be able to say, at any moment, like, you have this global registry that knows about all the types in your game. And then you can have these functions here, like load component registry and load resource registry. You can put those in, like, if you have a component slash lib.rs that lists all your components, put the component registry right below it and you can list all your components, right? So, this is a super useful pattern. So what are the takeaways here? Dynamic typing is powerful and useful, but obviously you need to be careful when you use it. But if you can use it in just the right place, you'll notice that this has kind of solved the last problem that we had with our super simple game design, right? So you declare all these systems, right? And they, you know, we had this problem where every time we added something to our ECS state, we would immediately be messing with the dependencies of every single system. But now a system can only know about a part of our game state if they know about that type. So you can just look at the import list for any given system and see whether it depends on something. And it breaks up this, everything is depending on everything else, not. If you need dynamic typing like this, usually you'll have something like a type registry to go along with it. This pattern is super common. In object-oriented programming, you might call this a component factory, but if I think if I said I component and component factory, I would rightfully be left to head to. But oftentimes these patterns in object-oriented programming are really complex, and any map kind of simplifies this. You know, if you're going to have dynamic typing and downcast, just kind of own it. Just put everything in an any map. And any map is awesome. So let's just wrap up here really fast. Let's see what time it is. A little over. I didn't talk much about the performance implications of things like ECS systems, and I kind of did this on purpose because I've read a lot of really confusing ECS explanations, and I think that they can kind of lose the larger point, which is that it's very useful and powerful to just stop thinking about objects sometimes. And I also think that, you know, you have things like data-oriented programming, which are classically just thought about just for the reasons of performance, that this is actually kind of more important for Rust because Rust has this focus on like ownership and borrowing, and then the data-oriented design can both make for better performance and also for an easier time with the borrower checker. And critically, these are kind of two sides of the same coin. I apologize if you've heard a lot of this before. I feel like I'm kind of retreading old ground, like especially in the gamedev community if you hang out there or are interested. You know, a lot of these things are kind of considered common knowledge like ECS design and the fact that object-oriented programming is overrated. If you have heard them, I hope that this is maybe a simpler explanation that you've gotten in the past. And I hope you've, you know, the very least found some good design ideas or some good features that you may not have known about or may not have thought about otherwise. And also just to finish up, there's a lot I didn't talk about. I didn't talk about all the great crates if you actually want to make a game in Rust. You know, don't write your own ECS system, go use specs. Specs will do all this, and it'll schedule your systems and it'll have great performance, and you know, don't make your own ECS system. But also there's other ways to make games. You don't have to use an ECS system. You can use the actor model. There's lots of great ways to make games. I was not able to talk about all of them. And that's it. Thank you very much.