 All right, hello. Going to go ahead and get started. And as you can see, the title of this talk have fun the wrong way, which I hope we can all appreciate the intellectual depth that was required to come up with that pun and that name. So before I dive in proper, it doesn't show up particularly well in this image, unfortunately. But what this actually is, is a very nice photo, I believe titled, Perspective on Edinburgh Castle. It's through a park bench, which a person or two in this room would understand the significance of this to me. A couple of nights ago, I've been in since Monday. But a couple of nights ago, I was out having some drinks with some folks here, some wonderful people. A few too many drinks, and my generally terrible sense of direction kicked in. As it turned out, as I was trying to walk back to the flat, I've been staying. I became completely disoriented and completely lost. But fortunately, I did find a park. And in the wee hours in the morning, I spent maybe a couple of hours sleeping on a friendly park bench. So this is a wonderfully accepting and lovely city. And I've had a certain perspective on it since I've been here. But what we actually are to talk about. So what is wrong? Wrong was supposed to be Ruby and Pong. That's the idea, right? Ruby plus Pong. So an implementation of Pong and Ruby. Why would one do this? Well, one, I love old school games. And I've always wanted to write a game, and construct and create a game. Back in university, I dabbed a little bit with some 3D graphics stuff. But it's not really my cup of tea, kind of like the classic style. And had always just wanted to kind of do that. But in addition, sort of the mission statement, the goal was to basically, if this is the productivity button, to basically blow the productivity button off of my fellow Rubyists and fellow Ruby shops and consultancies and have these people playing Pong, right? So it was a networked game, a version of Pong. It was going to be so fun with leaderboards and whatnot, that we would all want to play it. And the company I work for, for a company called Edgecase, back in the USA. And we would simply ban it. And we would get a significant productivity increase just by dragging everyone else down. Would be great. No, not really. But really, I was just looking to do a fun implementation of a game to get cross-platform and to include some robust network code because I find that interesting. So if that goal was robustness, is wrong, robust, right? That's not Marvin Shakespeare, that's robot plus bust. It is wrong, robust. And the answer is no. No, it's not. I sort of, you know, I've failed to date to meet that goal. Right now, wrong is a nice client implementation, but it's not necessarily, as it currently stands, client server. I didn't hit that goal for this. But we are gonna talk ideally, you know, how that would be done and how it will be done, given a little bit more time. But so let's talk about, first, about drawing the game and how to actually draw something on the screen. Some people, some of us may be familiar with Gosu. Gosu is an awesome, that would say if my slide were aligned properly, that would say 2D drawing and game library. So Gosu is implemented in C, C plus plus, there's a version of library bindings for C plus plus and for Ruby. So it's a library for drawing simple 2D graphics and doing 2D gaming. So it has fundamental constructs of games and game loops. So 2D graphics can test and text, excuse me, accelerated by 3D hardware, has support for doing sound samples and playing songs and basic sound effects, and has input control parameters so that you can read input coming in from various sources. This is the example event loop that Gosu uses and in general, in varying levels of complexity, the core game loop that any game engine would use. And this particular graphic is ripped shamelessly from the Gosu wiki page. But in essence, you construct an initial window so you can construct some sort of screen buffer or something appalled upon which you draw on the screen. You read for some input events to see what external changes are coming from the outside that may be looking for key presses or for mouse movements, game pad movement. Run an update, so based off of the previous state of the game and given any new inputs that have come in, we're gonna do some logic, maybe move characters around, move, you know, pushing pixels basically. And then draw that and then just loop and repeat ad infinitum until someone quits out of the game. So an element that comes into effect here, let's say discrete versus delta time. So if the game loops, it's gonna loop at some effectively a frame rate, right? So maybe it's gonna loop every 60 frames per second, ideally, depending on the amount of work that you're gonna be doing that could go up and down if you're doing a lot of complex calculations. In this case, we're talking about Pong, and although it was, you know, a massive hit and a massive landmark and a wonderful achievement for all of us, particularly, you know, us gamers these days as well as people interested in computer engineering in general, in general, when it hit in 77, it was a big deal. Now it's not so taxing, right? But there are other games that are. So this issue starts to come into effect. So discrete versus delta time-based. Discrete would be, if we take an assumption that there are going to be 60 loops of my, 60 iterations of my game loop during a second or during a given time frame, then I'll simply update the simulated physics based off of that breakdown, right? So if I know that I want my character to be able to move 30 pixels over the space of a second, then we know given that, or to simplify the math for myself, so they should be able to move 60 pixels over the space of a second. Given one sixtieth of a second loop iteration, then they're gonna move one pixel per update, correct? Versus delta time, and delta time would be between the loops, between the actual discrete loops, I'm gonna check the elapsed time in milliseconds, and then I'm going to apply traditional classical kinematics and physics to time-based physics equations, right? And actually run those calculations in real time, which is for a game like Pong, if you're just implementing it simply on a client, you could go either way and the delta's gonna be simpler. Excuse me, I'm sorry, discrete's going to be simpler, but delta time might become relevant if you're going to be synchronizing with a server across a network because the clock rates or the rates at which the loops running or various other latency issues might skew if you're assuming a discrete time is the same on both sides. Okay, so let's talk about points, shapes, and hit boxes. So points is just kind of a reference to, in most simple 2D games like this, and in particular in Pong, we're just talking simple point-based physics, we're not talking about taking into account mass or density or anything other than just basic particle physics. So even in a game on the left, it's a little bit hard to see, but that's a Counter-Strike model, which is a popular first-person shooter who's played Counter-Strike at some point in their lives. Good game. And then of course on the right, we see another classic game we should all recognize which is coming from, which is the original Super Mario Brothers game from Nintendo. The physics of Mario are based around a single point in space. Even though Mario, of course, when you look at that model, it looks like it has height and width. Effectively, any physics calculations being done on that are being operated upon a single point. But Mario has a distinct shape, as does this more complex model, the Counter-Strike model. The shape that Mario is in, it does occupy 2D space, but it's distinct and different from the concept of a hitbox. So in some cases, you're going to keep track of the points in 2D space that define a particular shape, maybe a star, a rectangle, what have you, that correspond to what the element or what the object will look like drawn on the screen. But then separately from that, you can keep track of a hitbox, and the hitbox is used to calculate intersections. So there's no need to read through that terrible glob of text closely, but it's essentially a very simple check for doing intersection of two objects in 2D space given rectangular hitboxes, right? So if I want to do collision detection, say in the case of Pong, if I want to know that the paddles and the ball are going to run into each other, which is relevant information, given that we want the pad to hit the ball, right? Simply can check to see, are any of the points inside this 2D rectangular shape that represents either the pallet or the ball will a point in one be contained within the bounds of another? And this is just a very sort of brain dead simple check for that. But there are some things to think about. In this case, this is kind of illustrating that a simple 2D rectangular shape for a hitbox, also known as a collision mapping, might not actually lead to good results. Like in your particular game, if the shape, given the shape of your object and hitbox of the object are going to be different, and there's significant incongruity, then the player might see the visual discrepancy, oh, he didn't actually hit me, right? So this is kind of this drawing taken from a very nice discussion on hitboxes in general. It's kind of illustrating that, right? You've got a touche, and yet there's actually no contact. So for more complex, so a nice library to handle, let's jump back real quick. So this could be a concern. However, in Pong, in a very simple bare bones version of it, all of your shapes are rectangles and squares, right? So actually there is congruence between the bounding box and the shape, so you don't have this particular issue. So to that end, and the extent, when I implemented Pong as a client game as something you could play, just hand rolled bounding boxes and shapes and intersection logic, because it's so simple. But for more complex games, you might look at a library called Chipmunk, which again, if you've heard of Gosu, you've probably heard of Chipmunk in conjunction with it, which if you can't tell that image, that's a Star Wars scout trooper writing on the back of a Chipmunk, which I think is pretty spectacular. But as is the Chipmunk library itself. So it provides more advanced features, things that are fairly sophisticated algorithms for collision detection, in particular of large amounts of objects. So it is 2D collision-based, but it provides primitives, instead of a simple rectangular bounding box, it has circles, complex polygons. You can assign multiple bounding boxes to the same object, so you can have different and overlaid collision areas. And it's implemented in C, it's very fast. It's much faster than the native Ruby implementation would be. It uses a spatial hashing algorithm. So if I wanna run collision detection against in Pong, it's pretty simple. I've got a ball, I've got two paddles, they may hit, they may not on any particular iteration of the game loop. If I'm doing something more complex, let's say a game like this, right? This would be Galaxy, wait, Geometry Wars, thank you, which is awesome. But there's a lot, if you've ever played Geometry Wars, I don't know why I'm saying Galaxy Wars. There's a lot going on at any given time on the screen. There's just a much, much larger number of potential entities. And to check for collisions, technically, a naive approach is you simply, you have to actually check each individual element against others and say, do these two items intersect. And obviously the number of comparisons you need to do blows up very quickly as the number of elements increases. So Chipmunk uses a basically spatial hashing algorithm which is a first high level pass that can kind of rule out. If I know that this object is down at the lower left and I know I don't need to do any comparisons against things, they're at the upper right quadrant of the screen, right? Because they're not going to collide. So Chipmunk provides for things like that. A sort of a point related on this particular topic. So when writing, in particular, writing a 2D game or games in general, you're writing a game, not a simulation. Like yes, complex physics is simulated and you are simulating interactions at certain levels, but anytime you can do optimizations, anytime you can take a less complex route, do so. It doesn't actually need to correspond to the physical reality of what would underlie a real system. So this is just a very simple example of that. Unfortunately, I ran the Cardinal sin, I've run the text up against the left here, but the gist of it is saying it could have taken, and again, in a Pong implementation, it's not a big deal really, but it could have taken the route of, say, drawing bounding boxes around the top and bottom of the screen, for example, or something along those lines. And on every iteration, you could just check and see, because naively or just immediately you could think, what's the question I'm trying to answer? Oh, did the pallor run to the top of the screen or the board area, right? I don't want to let it run off. And so because you're thinking in terms of doing this collision detection, you might try to do that. Well, but you don't need to do that, right? This is again, this is a naive example, but all you have to do is check has the pallor exceeded what I know to be the upper bound to be, then I can move it back down, right? As opposed to actually doing some sort of collision and having it bounce off of or something like that, there's no need in this case to do that. Very simple example, but generally valid principle. So if we're actually talking about client server, some related issues to implementing a game over a network, right? So the goal of wrong someday here soon is to allow me to play a game of pong with you and feeling slightly fuzzy and happy about it because it's implemented in Ruby, but we can be in different locations where we can be across the team room or what have you, right? So some different ways, some issues that are gonna come up in that. So this, oh thankfully this one's not too far off the screen here. This is a very simple start at if I want to write on the server side, right? So if I wanted to write the server half of my client server implementation, some very fundamental socket programming stuff going on here, if I open up in this case and who actually, let me ask real quick, who in the room has done socket programming in Ruby? And of those that have who's done socket programming in C or C++ or Java, right? So there's a very high correspondence between the API and Ruby of doing fundamental socket programming as there is to the underlying C implementation, right? The underlying system calls, which is a good thing, right? I mean, sockets are not, they're about as abstract as you can want them to be given the performance characteristics and the low level details that you're dealing with, right? So here we just see that they're opening up a new socket and in this case we're binding to the socket loop back address and just doing a listen. So we're at that point open up, we've basically opened up a port, we've chosen this case, it's ports 7664 and we're listening for incoming connections. Now in this case, I've set the acceptable number of concurrent connections fairly low because this would be the naive case of I have a server but I'm really only looking to play sort of one game simultaneously at a time. The more interesting and eventual approach would be if you wanna choose to host two dozen games of Pong at the same time because there's just such this desperate need in your office to be playing Pong, you could do so. But right here, we're simply waiting to see that we've got two accepted connections. At that point, we're gonna kind of move on into a game loop, right? At that point, you've got two clients connected to you and you can start up a game, start passing game data back and forth. Similarly on the client side, and of course unfortunately this one's a little bit worse, but you're gonna connect on the same port to the server so this is gonna be the client reaching out and start receiving packets from the server. And in this case, if you could see a little closer. So we'll be just pushing these messages, these receive messages into some sort of buffer, right? And that buffer in this case will probably be something like a ring buffer. So ring buffer is just an interesting and useful data structure. It's fairly simple but it winds up being very useful in network programming and client server, programming in general and also client server games. So in this case, the ring buffer is a data structure that works effectively like this. It's basically, it's just like a linked list but of finite size or a, basically a queue of finite size, right? So if I can push cat on, I can push dog on, push boy on, if I go to push on fox, then we'll see that cat has been sort of bumped off the end, right? And why would this be relevant? Of course, yes, that's hard to see unfortunately, but what kind of, how is this useful? What's the underlying principle here? As messages are coming from a server for example, let's say you're getting continued updates of position data. So let me pause and ask a question. So between a client and server implementation of a game, which side of that does the game run on? It's a slightly trick question, but which of those two actually runs the game? Server, right, yeah. And for amongst multiple reasons, why might that be, one of those of course. The same principle that applies to web development, right? You can never trust user input, sure. You can never trust client input from your game because any enterprising gamer that wants to perform well might can just go, you know, might be sending malicious data, right? If, and in particular when it comes to Ruby, that's much easier task to be doing than it is say in a compiled C-based game, right? A large number of us would raise the hand on, yes, we've played Counter-Strike before, so we probably remember, you know, aim bots and wall hacks and all that, the good stuff back in the day that I never used, but everyone that played against me must have been using. So anyway, so that sort of provides the context. So if the server is continually pushing information up to the client, in the case of Pong, they're pushing it, right, well, paddles are here and the ball is here and they're moving at these speeds, right? And the client is spinning there and processing this input. Well, if something occurs such that, you know, the server is sending more information than the client is processing quickly enough, it's gonna start to get backed up. Well, you don't necessarily care. I mean, information expires and comes stale very quickly, right, you want the most recent position-based information, you want it to reflect, you want the client representation to reflect as closely as possible, the server representation. So a ring buffer is useful as a useful data structure or something like that because automatically, you basically get automatic expires, it's like cash expiration, okay? Related to these things, so TCP versus UDP, right? If you're gonna be talking over the network, you're obviously gonna be using a networking protocol of some kind and one may or may not be more well-suited than the other. So TCP, just high-level, you know, review for I'm sure most all of us are familiar, but you know, TCP is connection-oriented. There are guarantees, right, you know, message reliability and ordering in particular, right? So networks are unreliable by nature, right? And nodes go down, time-outs occur, we all have experienced this in various ways. But TCP as a protocol works to preserve, you know, packet ordering. And not only that, but it actually breaks down your message into discrete packets for you. The API for a TCP socket is a stream, and it looks and operates at a high level in ways that just look very similar to writing to a file, right, as a streaming API. So whatever messages you're passing into it, it will automatically handle breaking those messages up into distinct and discrete packets, ordering them, sending them off, and verifying that they all arrive and they arrive in the correct order. UDP, by contrast, does not do that. It's basically a fire hose that you shove information into and it gets sprayed out across the wire, but there's none of this control and reliability built into it. So in fact, there's no concept of even a connection, right? You're simply sending a datagram to an IP and port, right? And the listening socket on the other end that doesn't acknowledge you as a particular sender, it just sees data coming in. You have to actually manually break data into packets of yourself. So, you know, I might choose to make that 256 bytes. So I'm going to break my, you know, what would be a data stream into chunks of 256 bytes and shove them each across the wire. And I'm going to be guaranteed on the other side that I'll pull down a 256 byte chunk, but I don't know if I missed one, if they arrived or if they're arriving out of order, if it was dropped, I don't know who it came from. So essentially, for your application, you wind up having to recreate as much of the TCP features as your application needs. And this can be really useful for something like a first person shooter where all I want is absolutely the most up to date information about where all the positions, who's firing, who's doing what, as quickly as possible because it's this very fast paced, you know, Twitch gaming, right? And if I lose a few packets along the way, that's okay, as long as I'm kept up, you know, as up to speed as quickly as possible. But still that's going to imply that if you're writing, you know, you're networking in terms of UDP for your game, you're going to have to do things like simulating a connection, right? Because you need to keep track of which client is which and things like that. So that's kind of come back to that in a few moments, but that brings us to this concept of latency versus rewind. If anyone played the original version of, well, so, of Quake, the original networked version of Quake, right? So there was a big difference between Quake and Quake World, which is the next version that came out. And, well, multiple differences, but the biggest networking difference was that they switched between these two models. So the latency model is basically that, you know, the server is entirely canonical. The entire simulation, the entire execution of the game occurs on server side. And I, as a client, tell the server, oh, I've just moved forward, right? And then the server runs, you know, the next iteration of its simulation and calculation, and then broadcasts it back out to the client. And even I, as the client, if, you know, the one who, you know, had just sent this I moved forward message, doesn't actually respond to that until after it gets the response back, right? So what you have there is a significant latency between user action and the response and the reaction of the game itself. And you can perceive that, right? Depending on the game and how it's, and depending on the actual, you know, network latency and whatnot, it's gonna be more or less noticeable. This rewind or its approach is, is, is family different. You basically will have two, or up to end, concurrent simulations. So the client actually is running the simulation as well. It's a full-fledged version of the game, and it basically is doing predictive movement. You know, if it detects that you've told the game that, you know, you're moving forward, it's gonna go ahead and do that, and you're good to go. The problem is, you know, all of these actions are going on simultaneously and they all need to be processed. This still need to make the server, the canonical version of what's going on. So the server might find out, okay, well, player A was moving forward, but player B was just detecting an action, you know, in this case, let's say a first person shooter. So player B has fired at player A and in fact hits them and they've been killed there. So their character is dead this round. So now the client, you know, for player A, for me, has to be told this has happened. And now I need to drop the, what I've, you know, I had assumed that I'd gotten to walk these three steps forward. But in fact, I only got one step forward and I was hit, I'm done. And so now we're gonna need to do basically rewind what happened, process these new events that come from the server, coming from externally, and then rerun the simulation up to that point. And there's some really cool, I've got some links in here later, but Valve software has some cool white papers on how to handle things like this and actual interesting timing algorithms and how to compare deltas in time. But effectively, the better job you have this rewind out, you do have implementing this sort of rewind logic, the less you're gonna notice its effects, but you're still gonna be acceptable to the snap. It's kind of the term for it, which is basically when the player, instead of when basically events get rewound and you replay back forth, it's either you're gonna manifest in jumping or skips or you're seeing basically replaying of activities. But again, it's just a fundamental aspect that's a trade off of a network-based game. So this relates back to before that it's much easier to implement client server physics based off of a delta time algorithm because then you can compare timestamps and you can do this rewinding and things like that. So in practice, all serious real world client server games are gonna use that approach. All the things related to this, you're gonna need to implement some sort of custom protocol for your game. You're gonna need to do things like tracking positions, transmitting actions, simple control protocol, like I've joined the server, the game is starting, someone scored, we've met a win condition, right? All of these communications, you're gonna wind up with a custom protocol of some kind. And there's a couple of approaches you can take. Basically, and we've seen this before and it's no different than XML versus proprietary binary formats or what have you. The same sort of trade-offs exist. And historically, binary pack data is the more common approach of the two. And still, for most performing games, you're gonna wanna do that, but text-based approaches have some nice of these two of them, particularly when you're looking at debugging your own game and you're in the process of implementing it. It's great to be able to see a human readable packet and look, okay, well, I've seen what the state is. I've seen the x, y's of my various components and things like that. So a couple of approaches, there's a couple of good tools used to do this. One, I mean, obviously you can just be applying regexes and mashing off of strings that are coming in, but that gets sloppy and it gets ugly. Ragle or Ragell or however pronounce it is a nice, basically a text parser. It's a parser, grammar that is used in such software that you probably use or have used all the time, such as Mongrel or Thin or H-Percot used generated Ragle grammars to parse either the HTTP protocol in the former cases or actually parsing XML in the latter. The nice thing about it is that it's both very fast because the parsers and generates are very fast and it can target multiple languages. So an initial goal of wrong and one that it will meet eventually was to be cross-platform, right? So if I had a parser for C Ruby, for MRI, or sorry, if I'm targeting MRI or I'm targeting JRuby, could actually use Ragle to generate a C-based parser. This is essentially a native extension for C or generate a Java-based parser that's effectively a native extension for JRuby, right? But in the meantime, you could just decide to have it target an output Ruby, which is gonna run on the other side, right? And still be fast, but not necessarily be as fast as if you were targeting the underlying architecture. An entire alternative to that, which is very recent and just much, much simpler is this notion of tight net strings, which both of these things, I came across both of these things because I was paying attention what Zed Shah was saying, which he's obviously a very bright guy if we have different opinions on things or whatnot, but tight net strings are a much simpler text-based format that are sort of a combination of the two. It's sitting a compacted string over the wire, but a much simpler format. And it's worth checking out as a recently emerging idea for doing client sort of network communications, which obviously something Zed's been doing a lot of. So there's a list of resources that you're not expected to be able to read here. These slides will exist and are able to be downloaded so you can get there. You can ping me on Twitter if you so choose. So this talk was meant to be about wrong initially. It was the idea and did not deliver to the extent that I wanted to, but will continue to implement and we'll get out there. But what is the state of it currently? You can play it. You can gem install wrong and you can play to player on your machine. So please feel free to do so. And you can play back and forth and give me feedback. The physics as far as intersection and doing collision detection will not, that's not, it's not in a final state, but it's enough that if you get bored between talks at some point the next day or so, you can play the game and then feel free to give me plenty of critical feedback because it is very early. Yeah, so gem and stroll wrong, it is there. The future, in the future, which I guess roughly means like 1978 because Pong's been around for quite some time. One thing that, the goal on toward world domination of the Ruby gaming community, one of those things is to be able to do built-in server detection. So we'll actually write a service and have a canonical service out there that will be a listing of any different servers that are running. If you wanna have a local server running and in your office and you can pop up the client and people can play during coffee break or whatever. Do I necessarily, for the record, do I necessarily think that it's gonna take the world by storm and everyone's gonna be playing Pong and Ruby? No, but to me it's an interesting intellectual exercise. So in addition to that, in addition to server lookup facility, built-in leaderboards, right? So you can have a leaderboard in your office. You could have a, there'll be a canonical service with which you can register some name and you can play against each other across, you know, on different servers across the world ideally. But, of course, aside from simply being Pong, it's Ruby and, you know, Valve's, for example, let's say Valve Software writes Counter Strike in Half-Life and very successful and awesome, amazing 3D games. And they're very, very brilliant people and they spend a lot of money doing a lot of work to lock down cheating in their games, right? Well, even if I were that bright, I'm not gonna be investing the effort into locking down wrong from cheaters. So I was also thinking that at some point in the future have a bot division, you know, officially recognized bot division. So if you really care enough that you wanna hack wrong, well, just, you know, write your bot and it can play against someone else's bot. I don't know. That time may come, it may not. But the goal is to at least have actual, have the ability for people to, you know, play against each other across, you know, geographically disparate locations, you know, by August, why August? It may happen sooner, but there'll be a lovely Ruby conference in Madison coming up in August and I think I'm gonna get to go talk there too. So I have between now and then so I actually get the client, or sorry, the server half of the client server reputation. But other than that, so that's all I've got. So, you know, thank you. If you have any questions or slurs. What's that? Sure. Now the irony, well, assuming this doesn't blow up with you forever. No, it shouldn't. I'm actually really, really bad at Pong. So there's there. You can theory play. Thank you. Oops. Yeah, so I need to fix that one. Either way, I win. Thank you. If you want to see the code, it's github-mat-yo-ho-wrong. And right, that's a great question that I was started to avoid. When I started the initial version of it, I started TDDing and actually, I think this is a valid point. So I was TDDing and there's a goal. The project's broken up into a few different sections. There's a client, a server, and this intermediate of things that can kind of go back and forth because I was trying to take the approach of simulating it on the server and sorry, having it in both. And so that middle section where you're doing the physics and whatnot, that's very easy to test. Now it's test driving, but I found, I mean, I'll be honest, a lot of stuff was pretty new to me. So a lot of it was spiking and I kind of wound up dropping tests for the first run. So my intention is to kind of do another pass and to do more TDD on things that I can. As far as like integration testing on the clients, I don't have good answers for that one. I might try to find some sort of intermediate format that could easily be tested, like some sort of text-based format to just run iterations, which I think I didn't approach similar to that one. I did a Conway that I TDDed, right? So yeah, we'll see. Check the repo a month from now and see what's going on. I had hoped initially, but like three or four other talks came up between then and then. I don't think it even necessarily was more or less hard. It was just something, like part of the reason I wanted to do it was because there were several things I was curious about, but had never done. And I think it was just, it was more about spending a lot of upfront time wrapping my head and doing a lot of reading and research before diving in. So that took longer than I'd hoped. And I think that's it. So thank you.