All right, welcome everyone. My name is Jack Baker and I'm going to be talking about bugs in game engine. So as a little bit of background over the last few months, I found more than 10 remotely exploitable bugs while looking at two different game engines. And I'm going to be talking about four of those bugs today. So just to level set things, when I use the term game engine, I'm referring to the base software that most games are built on, built on top of. So if you're building a video game, you're probably not doing it from scratch. You're using a pre-made set of tools and software that are built to make that process easier. And that software is called your game engine. And so the popularity of many game engines means that a lot of games share the exact same bugs.  And games don't usually get security patches after release. Maybe like a bigger game will have some support, but like an independent release is not going to get patches a year or two years after release just to fix some security bug. So there aren't a whole lot of good statistics on what game engines are the most popular, but there's a general understanding that two of them are more common than others. And those are Unreal Engine 4 and Unity.   So Unreal Engine 4 is created by Epic Games. It's named for its roots in the Unreal series. It is open source. There are some licensing restrictions on how you can use that source, but for our perspective of just looking for bugs, all the code is out there for us to look at. And there are really two big notable games, I think, right now that are built using Unreal Engine 4. And those are Fortnite and PUBG. Unity is built by Unity Technologies. There are some open source components of it, but the core components of Unity are closed source. The core networking library we're going to be looking at is called UNET. And while I couldn't find a whole lot of like big games that are using UNET, there are countless amount of indie releases on Steam and everywhere else that are built using UNET. Now I should say that UNET is deprecated, but there are a few reasons that I thought it would be a good target anyway. The first is that UNET has not got an official replacement yet. Unity hasn't put out an alternative yet. If you're using Unity and you're doing multiplayer, you're either using UNET still or you're using third-party solution. Also, UNET still does receive patches and occasionally new features, even though it's deprecated. So the encryption API was added to UNET after deprecation. And so a ton of new and more importantly, I think, existing games use UNET. The bugs in UNET still have some value. So let's talk about multiplayer protocols. As game engines have evolved and as multiplayer protocols have evolved, there's really been a focus on two things. The first is obvious. It's increasing performance, increasing speed. The other is moving trust away from the client in order to prevent hacking. But as we're going to see, these can sometimes be conflicting goals. But to really understand multiplayer protocols, I think it's worth understanding the types of attacks that they're aiming to prevent. And I think the best example of this is to talk about the evolution of what we'll call movement hacking. So movement hacking is the process of manipulating the player's location in some way that the game normally wouldn't allow. In the old days, this was really easy because player location was just trusted to the client.  And this got more difficult as game engines got more complicated and trust was taken from the client and put on the server for player location. So the client can no longer say, I'm at XYZ. The client, all I can do is make a request saying, I would like to move. I'm moving in this direction at this speed and the server will update their position accordingly. This led to a new type of attack called speed hacking. So it's sort of the next evolution of movement hacking. Speed hacking, the goal is not to teleport necessarily, but just to move extremely fast. And typically the way this works is you send that movement request excessively fast faster than a normal client ever would. And more requests than means more speed. This was prevented by basically giving more authority and more context to the server. So the server should be able to understand what is a realistic distance that a character can move in a given time frame. And if a client is attempting to move beyond that time frame, it can stop that from happening. And so by giving this context to the server, we're able to prevent this type of attack. And this is really how game engines have evolved. It's been a constant process of moving trust away from the client and giving more authority, more responsibility and more context to the server to understand what is normal. So with all of this said, let's talk about some of the technical specifics of multiplayer protocols. Now, there's no real like publish standard of what a multiplayer protocol looks like, but there are a few things that are pretty consistent between different protocols. And the first of those is that most multiplayer protocols use some form of distributed architecture. And essentially what this means is each system, whether that be a client or a server, each system has a copy of every networked object, every object that's connected to the network in the game world. And actions between objects and between systems are performed through what are called remote procedure calls.  So this is really easy for the programmer because it's just like you're calling a regular function of your code. The difference is that that function is executing on someone else's system, not your local system. This is really convenient, but there's a lot of complexity that goes into this process on the back end. And so with this concept of the distributed architecture usually comes some sort of concept of ownership, where owning an object usually means having the authority to issue RPCs on that object. And so the way this usually works is each player has ownership of their character and maybe some associated sub-objects like your inventory. But player A can only issue RPCs on character A. You can't issue RPCs on character B and vice versa. Another interesting technical detail of multiplayer protocols is that they're usually implemented over UDP. The one major exception to this are browser games where you can't access operating system sockets, so you have to use something like web sockets. But when we're talking about desktop games, we're usually talking about protocols that are implemented over UDP. This puts extra requirements on the protocol itself because UDP won't do things like validate that a packet is part of a session that you've already authenticated or identify when a packet is either a duplicate or out of order. So the protocol gets more complex because it has to deal with these types of problems itself. So now that we've talked a little bit about how these protocols work, let's look at our first bug. This is an Unreal Engine 4 bug and it's actually a file pathing bug. So Unreal Engine uses its own type of URL and it uses this to communicate details between the server and the client. So this can be stuff like the server can send a URL that says, we're playing on this map. You need to load these packages or the client could send something saying, I'm joining the game. My player name is Jack and I'm joining with two other players who are playing split screen on the same computer. And so one of these URLs might look something like this. You start with the IP address of the server, then you've got a file path that corresponds to a particular asset. And then just like an HTTP URL, you've got key value pairs that are separated by an equal sign. So the bug here is pretty simple. If you use a malicious URL, you can cause a server or a client to access any local file path. It's not going to try to write to that file or even read from that file. It's just going to try to determine if that file exists. This is pretty boring on its own, but it gets more interesting when we start talking about universal naming convention or UNC paths. The UNC paths are special windows paths that are used to access networked resources as if they were regular files. So regular local files. Typically, UNC path will look something like this. You've got two slashes, then the host name or IP address, another slash, then the share name, then another slash, and then either the file name or the full file path. So using this crafted URL or one that looks like it, we can cause a server or client to connect to a remote SMB share. This is pretty simple. The one trick here is we do need to include the part that says dot UMAP, where UMAP is a file extension used by Unreal Engine. And by having this somewhere in the domain name here, we use it as a subdomain. We can bypass some of the filtration. And if we provide this URL to a system, it will cause it to make an outgoing SMB connection and try to determine the existence of this high dot txt file. And so what this does is it opens up the affected system to the world of SMB related attacks. Typically, when a Windows system connects to an SMB share, it'll try to authenticate with it. So this can be used for credential harvesting or authentication relaying. There's a whole world of different attacks using this. This can also be used pretty trivially as a server denial of service because the whole server will lock up as it's making this request. So if you cause that SMB connection to deliberately take a long time, you can lock up the server for a while. So this was fixed in Unreal Engine 4.25.2 with this commit. It's really easy to backport this. So if you're on an older version, this isn't too hard to apply. So this is a pretty fun bug, but let's talk about something a little bit flashier. And let's start talking about our first unit bug. So unit packets are packed in such a format that you can put multiple RPCs into a single packet. And that looks a little bit like this. So the first thing you've got is the packet header, which for our purposes, we don't really care about. But then you have each message within that packet. And each message consists of a 16-bit message length, a 16-bit value that I call the message type, and then the message body, the actual contents of that message. And then at the end of that body, you see you have the next message just concatenated on. The bug here, again, is pretty simple. If we supply a message size that's larger than the actual payload of our message, the actual body of our message, we can convince the server to act on extra data that's already in memory. So that looks a little something like this. If we imagine this is just a regular RPC, we start with the length, and then we've got the body, which is four bytes. But if we were to manipulate that length without actually increasing the length of our body, we'll see that all this other data that was already in memory, that's not part of our message, is now within scope of our RPC. So what's interesting about this is that this old memory actually comes from previous RPCs, not just from ours, but from other connections, from other players. So what we want to do here is we want to create an RPC that will leak this old memory to us, kind of like Heartbleed would. And so to do this, it's not enough to just cause the server to act on that old memory. We need to convince the server to actually send that memory back to us. And so the type of RPC that is really good for this is chat messages. So let's look at what an example chat RPC might look like. In this case, again, we've got the length and then we've got the body of the RPC, which is just made up of a string length and then a string body. But if we just supply that string length and we don't supply a body, then all the rest of that memory is going to be treated as part of that string. And if that RPC is accepted, the server is going to send back a message that says there's a new chat message and here's its contents. And those contents will contain data from previous RPCs. But even in the absence of chat messages, there are some other RPCs we can use. Movement or spawning a new object are both good ones because they typically involve giving some form of vector, some location as an argument. And we can use this to leak either eight or 12 bytes depending on whether or not this vector is 2D or 3D, if it's a 3D game or a 2D game. And then there's always game specific RPCs that we could potentially use for this. We just have to get creative. So let's talk about what we can leak with this. Now, there can be a lot of things. If you're doing authentication over UNET, this could be passwords. It could also be private messages. It could be game specific stuff like player locations or player actions. Really, anything that gets sent over UNET by the client could be leaked this way. And this was fixed pretty recently at the end of May with UNET version 1.0.6. So I went kind of quickly through those first two, but I really wanted to save time for these last two bugs, which I think weren't going into a little bit more technical depth. This first one is another Unreal Engine bug. And I really like this bug because it is a universal speed hack for Unreal Engine. So we talked just briefly about this, but movement in Unreal Engine is what we call server authoritative. That means that the client cannot just say, I'm at XYZ. Instead, the client has to issue a movement RPC and ask the server to update the player's location. And so this movement RPC has two important parts to it. I am oversimplifying this quite a bit, but I only want to talk about the parts that are actually relevant to our bug. So the first argument is the movement vector. And this is a vector that says the direction that we're moving and the speed we're moving at. And the second argument is a timestamp that just says we sent this RPC at this time relevant to client time. And so the actual math here is that the server will calculate what's called a movement delta by taking our provided timestamp and subtracting from it the last valid timestamp that we provided to the server. And then it's going to take our movement vector and multiply it by our movement delta to calculate the actual movement that should be applied. And so if the server can properly validate that both the movement delta and the movement vector are sane, there's not a whole lot we can do to manipulate our movement. Now we need to make a slight digression and talk about floating point. So when I say floating point, I'm specifically referring to IEEE 754. And this is how most computer systems represent rational numbers, numbers that can be non-whole, such as 1234 or 12.34, anything with a period in it. And so what's interesting about floating point is it has some special values. Floating point can be used to represent infinity, either positive or negative. It also has a special value called not a number, which I choose to verbalize as nan. And for completion's sake, I should say that nan can be positive or negative, but it doesn't typically matter. So these special values usually are the result of some undefined mathematical operation. So if you do any non-zero number divided by zero in floating point, you get infinite, either positive or negative. If you do zero divided by zero in floating point, you get nan. Similarly, if you do square root of negative one, you get nan. Now nan is really the more interesting of these two special values, and it has a couple of properties that are really unique. The first is that any affirmative comparison against nan will evaluate to false. So we can compare nan to zero in any way we want. It will always be false. If you do nan equals equals zero, false. Is nan greater than zero false? Is nan less than zero false? We can even compare nan to itself, and it will still be false. The other thing is that nan has a tendency to propagate. And by that, I mean any mathematical operation where nan is an operand will evaluate to nan. So nan plus one, minus one, times two, divided by two, these are all nan. And no matter how complex you make this mathematical operation, if one operand is nan, the entire thing will become nan. So this brings up the term nan poisoning, which is a condition where the unique properties of nan cause some sort of intended effect. So let's look at the following code as a really simple example of this. So in this case, the programmer is trying to ensure that the floating point number nan is between zero and 100. The problem here is that if num is nan, both of these conditions will evaluate to false because any affirmative comparison with nan will evaluate to false. So nan will pass these validations and we will end up acting on nan in some way, assuming that it is a legitimate regular floating point number within the range zero and 100. So nan poisoning attacks are pretty rare because it's typically difficult to actually introduce nan in the first place. You don't usually have an opportunity to divide by zero or do square root of a negative, square root of a negative number or anything like that. These aren't common bugs, but when we're doing remote procedure calls, we can use any argument of the correct type. So if the RPC calls for a floating point number, we can give it any floating point number including nan or infinite. So going back to our movement RPC, there's only one argument that is a floating point number and that's the time stamp. So what happens if our time stamp is nan? Well, to figure this out, we have to look at this mouthful of a function. U character movement component is client time stamp valid. So this is a lot to look at. So we're going to go through it together, but let's assume that our time stamp is nan because that is the value we've provided to our RPC. So we get to this first check, which is intended to ensure that the time stamp is greater than zero. And because this is an affirmative comparison, this will evaluate to false and we just skip right past to the next line. Then we're going to calculate our delta time stamp. And this is done by subtracting our provided time stamp with the last valid time stamp that was received from our connection. But in this case, it does not matter what that last valid time stamp was because our provided time stamp is nan and nan minus anything will always be nan. So delta time stamp evaluates to nan. Then we get to these next two checks. In the first case, again, we're going to pass right by because time stamp is nan and this is an affirmative comparison. And for the next check, delta time stamp is also nan. This is another affirmative comparison we pass right by and our time stamp is considered to be quote unquote valid. So this is all written in such a way that just by pure luck, nan will just pass right through and be considered valid. And so the next thing we do is we generate our delta time using nan. So our delta time is calculated again by subtracting our subtracting our last valid time stamp from our provided time stamp. And because our provided time stamp is nan, delta time will evaluate to nan regardless of what our last valid time stamp was. Now the server will use our delta time to attempt to apply our movement. But this is where we've run into our first issue. There's one last sanity check here to ensure that the delta time is greater than zero. And we will never pass this check when delta time is nan because it's another affirmative comparison. So our movement is not applied. And even though our time stamp was considered valid, we don't go anywhere. But we're not quite done yet because we've caused another value to be poisoned. We've caused server data current client time stamp to become nan. And this is the saved version of the last valid time stamp because our time stamp was considered valid. Even though we didn't apply our movement, nan was shifted into that current client time stamp variable. Now we need to look at that is client time stamp valid function again. So we're looking at the same function again. But this time we're going to assume that our given time stamp is not nan. It's not any special number. It's just a regular floating point number greater than zero. So we bypass this first check. We get to the delta time stamp calculation. And again, we're going to do our given time stamp minus our last valid time stamp. But this time our last valid time stamp is nan. So delta time is still going to calculate as nan. Then we get to these two comparisons, which just as before we're going to bypass because server data current client time stamp and delta time are both nan. So we pass these checks and again, our time stamp is considered valid. So on this second RPC call, any time stamp greater than zero will pass the validity check. We could say it's 30 years in the future. It doesn't matter as long as it is greater than zero. And our first RPC call use nan as a time stamp. Our second call will always pass the validation check. Unfortunately, our delta time is still going to calculate as nan because our old client time stamp was nan. So still nothing happens. We haven't moved an inch. But fortunately, we've poisoned one more value now. So while all this is going on, the server is trying to determine if our time has drifted from server time. Essentially, the server wants to make sure that we're not doing anything tricky or that we're not our clock isn't drifting. And it does this by independently calculating its own delta time and calculating an error rate, a difference between our delta time and the server's delta time. But because our delta time is nan, our client error is also going to be nan. And then this client error is used to build up our cumulative value new time discrepancy. And this value is used to detect when we have drifted too far from server time. This is used to detect speed hacking attempts. It's also used to detect when a client might just be lagging too much. But because our client error is nan, when our client error is added, our new time discrepancy value also becomes nan. And because new time discrepancy is what's used to determine a difference between client time and server time, once we've poisoned this value, we can essentially disable the server's ability to detect a time discrepancy for our connection. And so when we actually get to the point when the server would attempt to detect a time discrepancy between our time and server time, this check will never pass because new time discrepancy is nan. And more importantly, no mathematical operation will ever cause new time discrepancy to become a regular floating point number again. It is stuck as nan. So at this point, because we've neutered the server's ability to detect a time discrepancy, we can now pull off like an old school speed hack where we just speed up time in order to move faster than we should. And what this allows us to do is it allows us to move significantly faster than our built-in limitations would ever allow. So I've oversimplified this process a bit. I know it's still pretty complicated. But what I've come up with as not the most efficient, but the most straightforward way of exploiting this is to just send RPCs in groups of three where the first RPC, the timestamp, is nan. The second, the timestamp is just slightly greater than zero. And the third, the timestamp is some value well in the future. And every time you send this grouping, it will move you forward some amount. And because the server cannot detect a discrepancy anymore, you can do this as often as you want. And the only real limitation is how quickly you can send those RPCs and have them be processed. So saying all that, let's actually look at this in action. And I think you can probably imagine what this is going to look like. But I worked hard on it, so humor me. This is our first demo. This is built on a stock Unreal Engine 4 game template. All I've done with it is I've enabled all the speed hacking protections. I've opened up the game world just a bit so we can run around some more. And I've modified the client to actually pull off our attack. And this is filmed from the server perspective just so that you can see that this is actually happening server side. It doesn't just look like we're moving fast client side. So in the background, we've got our hacker. As you can see, he's vibrating. He's very excited to be here. And what we're going to do is we're just going to get out of the way so we can get a good view of him running. And in just a second, we are going to see him blast off. And he's gone. Okay, so that is a little bit faster movement than intended, even with the speed hacking protections on. I really like this bug. I love floating point bugs. And the great thing about this one is that it actually does something other than just being like a denial of service. I also think that this type of attack can apply in other ways. I've looked at other games that use floating point in its RPCs and you can usually get some sort of unintended behavior by using these special floating point numbers. It's not always useful. It doesn't always do anything for us for an attacker, but it usually does something. I should also say that this does still apply to unity. But with UNET, it doesn't limit your movement in the first place so you don't have to go through this complicated process to speed hack. So with that out of the way, let's talk about our final bug. We're going to go back to UNET and this is a session hijacking bug. So UNET uses a protocol level process to authenticate incoming packets because UNET is implemented over UDP so you don't get the benefits of TCP where a stream is already authenticated and you can assume that a packet is part of a stream that you've already seen before. So packets are not validated by their source IP address or their source port or anything like that. They're only validated by values within the packet itself. So knowing this, it is at least theoretically possible that someone could hijack another player's session totally remotely, totally over the internet. We don't need a man in the middle. We don't need to be over LAN or anything like that. And that's the plan here. So when UNET validates an incoming packet, there are three important values it looks at. The first is the host ID, then the session ID and the packet ID. And these names don't mean anything on their own. So let's look at each one in detail. So the first of these values is the host ID. And this is a 16-bit integer that's used to associate a packet with a given client. Host IDs are assigned sequentially starting at one. So the first client to connect gets host ID one. The second client gets host ID two, etc. Now, host IDs aren't really intended to be a secret. So in a way, we can sort of just ignore them. It's also really easy to enumerate the host ID of another player. If we're the second player in a game and we get host ID two, the other player is probably host ID one. If we're in a battle royale and we're host ID 47, the other host IDs are probably one through 46 and 48 through 100. Things get a little more complicated when we talk about the next value, which is the session ID. The session ID is the primary authenticating secret of a connection. And the session ID is randomly generated by the client when they connect. And every packet received for that client has to have the correct session ID or the packet will be discarded. There are a couple of problems with the session ID though. The first is that the session ID is also a 16-bit integer. It also can't be zero. This means that there's only 65,535 possible session IDs. There's also no penalty for incorrectly guessing a session ID, other than the fact that our packet will just be discarded by the server. So we can easily brute force the whole range of session IDs even over the open internet. It doesn't matter. It doesn't take long at all. Now, we don't really need to do this part, but there is one more thing we can do to narrow down that search even more. So session IDs are generated with a function named unet get ran not zero. And what this function does is exactly what the name says. It gets a random number and ensures that that random number is not zero. And this is used for session IDs because session IDs cannot be zero. But so the way this function actually works is it takes the end result, that random number, and it orders it with one. It essentially ensures that the least significant bit of that output will always be one. This has the effect of ensuring that a legitimate unet client will only ever generate an odd numbered session ID. Technically, a session ID can be any 16-bit value other than zero, but a legitimate client is programmed to never actually generate one unless it's odd. So this reduces the possible session IDs down to 32,768. So 50%. So if we know the host ID and we can guess the session ID, that's all we need for our spoof packet to be accepted within the context of another player's session. But there is one more hiccup, and that's the packet ID. The packet ID is an integer that's incremented with each packet sent by the client, basically a sequence number, and again, it's 16 bits long. So what the packet ID is for is it's used to detect duplicate or out-of-order packets because, again, UDP isn't doing this for us. It's also used to determine the rate of packet loss. So if the last packet ID received by the server is one and the next packet ID it gets is 1,000, the server is going to assume that it's lost 9,998 packets in the meantime. So if we can guess the host ID, I'm sorry, determine the host ID and guess the session ID, what can we do with the packet ID? I guess a better question might be what happens if we just send a random packet ID. So let's read Unity's documentation on exactly this. So according to the documentation, there are a few conditions. If the new packet ID is greater than the last packet ID plus 512, we're going to disconnect the session because we've lost too many packets. If the packet ID is more than 512 behind the current packet ID, we're going to discard it because it's too old. We don't want it anymore. If the packet ID is in a list of packets we've seen recently, it's a duplicate, let's discard it. Otherwise, if none of these conditions are met, we're going to accept and process that packet. So there are a couple interesting things about this. The first is that if our guest packet ID is greater than our last packet ID plus 512, the connection will be disconnected. And I want to emphasize that this is not our connection we're talking about. This is the other player's connection. So this is useful because it means we can pretty trivially kick other players off the server. However, it would be a lot more interesting if we could bypass this check and inject a packet that would be actually executed within another player's session. But reading the documentation seems like the odds of this happening are pretty low. Guest packet ID must be last packet ID plus or minus 512, which doing the math gives us less than a 7% chance of success. That's pretty bad. But when we look at the actual implementation, it tells a slightly different story. So packet ID validation is done by the function unet replay protector is packet replayed. In practice, this function actually does not discard packets that are more than 512 packets old, like the documentation said. I spent a lot of time thinking that I was just misunderstanding or that it was more complicated than I thought. But no, the logic for discarding old packets just isn't there. Instead, old packets are accepted as if they weren't more than 512 packets old. So unfortunately, it's a little more complicated than it sounds. We can't just use packet ID zero every time, even though that would be the lowest packet ID. And the reason for that is that the server has to account for cases where the packet ID overflows, goes from FFFF to zero. And this happens pretty often over the course of a game. So instead of just directly comparing the numeric value, the server has to keep like a rolling window of packet IDs to determine if a packet is old or new. And so doing the math here, we have what's very close to a 50-50 shot that a packet ID will be accepted. Most of the rest of the time, our packet is going to cause the other player to get kicked out of the game, which is still pretty useful. And I say most of the rest of the time because occasionally, we'll guess a packet ID that was actually seen recently and it'll be seen as a duplicate and just be discarded. Okay, so let's look at our second demo. And for this, I had to use an actual game, not just something I came up with myself. And this game is called Streets of Rogue. It's very cool. You should buy it. And I do want to emphasize that this is not a bug in the game itself. There's nothing wrong with how this game is programmed. This is a bug in UNIT. But even still, what we're going to do with this bug is we're going to inject packets to cause two other players to do actions that we tell them to. So this little guy in the red shirt is me and these two identical-looking people are target players. And what we're going to do is we're going to inject packets. We're going to see them do a few things. First, they're going to say hi to everyone. Then we're going to kill them both. Then I started to feel a little bit bad, so we're going to resurrect them both. And then we're going to kill them again. Okay, so this is a pretty simple demo, kind of stupid, but it demonstrates a few things. The first is that we're able to inject packets that are actually accepted within the context of another player's session. The second is that we're able to do it consistently enough that we're able to do it for two players at the exact same time, even though those two players have different host IDs, different session IDs, different packet IDs, everything. And finally, we're able to do it consistently enough that we're able to inject multiple packets into each player's session. We were able to inject a packet to say something, pause, inject another packet to cause them to explode, pause, and then do this four times, injecting four different packets. So this bug is pretty reliable. And as I said, even if you lose the coin toss, all that happens is you kick another player out of the game, which has its utilities on its own. So let's talk about remediations for this bug. This is considered to be an architectural weakness with UNET. The actual fixes that would be required to prevent this entirely are not going to happen. The only mitigation aside from moving away from UNET is to actually encrypt UNET. Unity does provide a reference implementation that does a decent job of this, but it's not complete. It doesn't do key exchange for you. So to an extent, you are still on your own with this. And I have looked a lot, but I've not found a single game implementing encryption over UNET. And I should emphasize that this is a mitigation. It is not a fix. Encryption is not complete. If an attacker can bypass this encryption, these bugs still exist exactly the same as they exist now. Okay, so that was my final bug. So let's talk about some future work. I don't think I've found all the bugs, even in the components that I have looked at, but there are a few components that I haven't looked at at all that I think are worth looking at. So both of these protocols have other transport modes. I think the most interesting of these are WebSockets, because that's what browser games are going to be using. There's also third-party networking plugins for some of these engines. Photon and Mirror are two relatively common ones for Unity. And there's just other engines, GameMaker Studio, Godot, stuff like that. Finally, I want to thank a few people. Both Epic Games and Unity Technologies, their security teams were absolutely fantastic, super communicative, kept me up-to-date the entire time, put up with a lot of me, which can be very annoying. I have nothing but good things to say about both of them. I also want to thank the artist who made the background art for my presentation, Grigoreen, he's awesome. And finally, all the scripts, everything I've put together for this work, I've got up on GitHub, I've got POCs for most of these issues, I've got libraries for interfacing with Unet and Unreal's protocol. And I hope that if I can ask anything of you, is that you go, you use something that you've learned here to go get banned from some video game.


Great, so I want to welcome everyone to October's Houston functional programming users group. And we today have with us John, Kevin or Johnson, who is one of my favorite speakers. He always has fun and interesting and smart presentations. And I was telling my wife, last night over dinner, I was like, Oh, yeah, John's gonna, you know, speak and she was trying to remember like, Oh, who is that?  I'm going to like, you know, mess it up and I really hope there aren't any sociologists in the audience. And so I laughed and but it turned out that his sociology was spot on. So, with that, I will turn things over to John. Welcome. Thank you so much. So, my name is John I actually did do a functional programming user talk about sociology, believe it or not. I have kind of a reputation I think for doing strange talks, or talks that kind of make you have to start somewhere other than functional programming. So, a little bit of background about me. I did study computer science in college. I was an English and history major. I had a whole career in some other kind of development before I discovered that I could make more money doing software development. And it was fun. And I've been doing that for a long time now. And the most fun I have is when I do F sharp. I'm just going to be honest. For the last couple of years, I did a project in F sharp. I really enjoyed that that came to an end. I changed employers. I went from improving to GCC like some of the other people here. And so now I'm doing mostly C sharp in real life. But I still love to dabble in F sharp.  And it was the ugliest F sharp I had ever seen. Now, that wasn't my fault as it usually is. Jared can tell you I usually write on the code. But this was ugly because of the situation. So let me explain a couple of things. The best open source PDF generation software in .NET uses sort of an old faculty with, you know, builder kind of approach. If you've done some C sharp, you probably see that the problem you run into there is that every other line in your F sharp code has to have it ignore. Because the C sharp builder things always return an object so you can keep, you know, dotting on to the end of it to do stuff. And that was a little frustrating. And just generally, there's a lot of dotnet applications that are designed for C sharp because most people who are in the dotnet world do C sharp. That's pretty reasonable. So I decided that it would have been a lot nicer. If I could have had a different interface into this application. And that's basically where it stood. And then on the project I was working on, it looked like we were going to have to create Excel files. Everybody know what Excel is? Yeah, yeah, okay. This is a test to see if people are awake because I know you know what Excel is, right? Okay, so I assume everybody knows what Excel is.  How many people have ever had to generate an Excel file or a PDF file from code? Raise your hand. Yes. Yes. Yes. Okay. How much time was it? Exactly as much as you think it was. Exactly. It was, it was more fun the fifth time I did it. Yeah. Okay. So I was looking around for a library that might let me do, you know, create Excel files. There's a ton of those at dotnet, right? But they're all C sharp based. And I ran across this little project where a guy named Kit Eason had put together a wrapper around closed XML. Closed XML is a really nice dotnet library for creating Excel files. The name is kind of a play of open XML, anyway. Closed XML is okay, but it's, anyway, what he did was he put a list processing front end to it to make it look like Elb. Anybody done any Elb? Anybody familiar with Elbish? Yes. Okay. So it'd be really helpful to the extent that you're familiar with Elbish, which is a way of doing HTML, but like with lists. And so I thought that was really cool. Too bad nobody's ever done that for PDFs. And I thought, well, maybe I could do that. I thought before I saw this little FS Excel, which if you want to go look it up on GitHub, you can. Before I saw that, I thought that would be a really hard thing to do. And I looked at his code and it was easy enough that I even contributed some features to that. I thought, like, this is a great idea because most of these things that create Excel or PDF files, they're complicated. Would everybody agree with that? That they're complicated? Because PDF and Excel have lots of features and they, you know, there's just a lot of stuff. And most of the time, you don't really need to use all of the features. So that was what got me started down this path. Now, when we clear that this is not going to be the PDF library that you're going to use if you need to do really complicated PDF. Like, that's not what it's designed to do. My experience, the PDF files that I've generated programmatically fall into some fairly simple categories. So invoices, most businesses like to send out invoices because that's how they get their money, right? And so that's one thing that you do. The other thing is quotes, you know, that's the other end of the pipeline where it's like, hey, here's what we can do for you. And in my experience, there's two things that are pretty common.  And what gets really interesting is when you have to have multiple pages and you're not sure where the page breaks are. So that's actually kind of hard of PDF. Does anybody know what PDF stands for? Portable document format. Very good. Some people get that wrong, but F is for format. So it is okay to say a PDF file. That's not redundant. Can't see anybody was worried about that. So the history of PDF, it's actually a subset of PostScript. Everybody familiar with PostScript? PostScript is a programming language to do what? Anything you want. It's a turn complete. It is turn complete. Also a lot of other things, but don't do anything you want with PostScript. It's designed. It's fun. But dots on a page to put black dots on a white page. You can do a whole lot more than that. So because they're really sort of focused on printers, like in their model, they tend to be focused on doing pages, which is fine if you have one page or if you know exactly where your page is going to break. But what I've discovered is frequently you end up with a situation where you have a variable number of paragraphs for your terms and conditions or whatever, right? And so what you need to be able to do is say, okay, put the stuff out there, you know, and you figure out where the page pagination goes. Has anybody ever written any code that tries to paginate stuff? I've actually had it. Yes. Yes. It's terrible. I mean, it's like, it is a legitimately hard problem. However, there is a library.net called Migradoc that was written like 20 years ago.  It actually generates all sorts of you can generate other stuff too, but most people use it with its associated PDF library called PDFSharp. The fact that there's libraries called PDFSharp really makes it hard to do an F sharp PDF, you know, like the Navy gets all messed up. Anyway, that's a whole nother issue. So I decided that this was going to be a cool thing to do. Now, how many people think it turned out to be too hard for me to do? Well, Jared, you're wrong. It wasn't too hard for me to do. Well, you might be right. I didn't answer yet. We don't know what you're going to say, Jared. But seriously, so in the model that I'm going to show you, there are no pages. That is, it doesn't create a page. What it does is you have some session level stuff you do. And then you have a document and then you have sections. And every section starts at the top of a page and you stuff things out of the section and you let the underlying library figure out where the paid breaks go. I will probably not expose it through my UI, but it will let you do windows and orphans and all sorts of other crazy stuff on your pages. But anyway, there's that. Now, does everybody kind of understand the problem I'm trying to solve? It's not everybody's problem. Probably not even most F sharp people problem, but it was my problem and I wanted a solution. So this is what I came up with. Now I'm going to show you a little bit of code. And then I want to sort of talk about the philosophy and the choices I made. And what I want to convince you of is using nothing more of functional programming than discriminating units lists and pattern matching. And an underlying library that's pretty powerful. You could do all sorts of really crazy stuff. And I actually like this as a programmer's interface. And what I mean by that is I like the whole idea that we call Elbish for reasons that I guess they came up with it first of just using a bunch of lists to tell the system what to do. Okay. Let me see if I can wake my. Yeah, there we go. My mouse seems to go to sleep quite a bit. So I'm going to try to share my screen. There we go. Can everybody see that? Perfect. Okay, so for some reason, Visual Studio Code just blew up on me today. So we're going to do this at LeapPad. Everybody's favorite tool, right? Okay, so I want to start off down here. So a list in F-sharp is inside of brackets. And if you put the items in the list on the same line, you put a semicolon to separate the list items. But generally what you'll see in a lot of lists is they'll break it out, you know, on different lines. That way you don't have the semicolons. People who write in F-sharp like to avoid as much needless use of punctuation as we can. Like the reason we don't do C-sharp is we ran out of semicolons. That's a joke. Now, so this list right here, right? This is actually a list for the document. There's some commands. Like I want to use this as, you know, the page sizes letter. So the default unit thing is kind of important, but it's really an underlying library issue. The underlying library was written by some people in Germany. And for some weird reason, they use like centimeters and millimeters to measure things. What's up with that? They're sane and not crazy like we are.  Everything, the way the underlying library works is everything you do that requires a measure, but you have to specify what unit you want to use. We're going to use itches everywhere. We're going to do letter.a4. So here's my section.  And then I send this list to a function called ridder's PDF. And then I say, I'll send that to a file and I give it a file path. And it generates a PDF. And later on, I'll like change this and show you that it really does generate a PDF. So that's what I want people to see. Now, obviously a real document is going to be a little more complicated than this.  But let's sort of go up and see all the ugly code that goes into this. Okay, so can everybody see that okay? Is it big enough and everything? Okay.  So that's a bunch of opens. If you're familiar with F sharp, they just, those are namespaces that we're going to use.  Is everybody generally familiar with the idea of discriminated unions? I mean, you can kind of figure this out. It's a thing that can be either this or that, right? So, you know, it can be inches or centimeters. The page size can be a four letter. Now, the underlying library supports a whole bunch of other page sizes that I'll throw in there at some point. I wanted to keep this fairly simple for now. And so, you know, I've got some stuff like margins. Oh, by the way, you'll notice that all the numbers I defined as doubles that actually makes life a lot easier in C sharp, or excuse me, an F sharp, and it fits with the underlying model of the library that I'm using. They actually prefer to get things in as doubles. You know, there's alignment. Like these are these are the kinds of things, right, that you worry about when you're formatting a document, right?  That's pretty straightforward, right? Then you can format things, right? And this is so one of the options for a lot for format is an alignment of alignment. If you've never done that sharp, that looks a little weird, but it's okay, trust me. And this just says that this is the name of the case in the discriminated unit. This is it's type, right? So the rest of these are doubles. I've got a text item that can either be a style or string. I'm actually going to change this. I've been through about 27 versions of this design because I keep iterating over it. Eventually I'll quit and release something and I'll let you guys know when I do that. You know, so there's section items and now this is this is actually the thing right here. Okay, so at the top level, there are items that are, you'll recognize the page size, the default unit, right? From what I showed you before.  And then every number that you give me where it requires, you know, that is a measurement will be in that unit. This is from the library. This is not the F sharp unit. Just so everybody knows that. There's a little bug in the library. So we do this PDF. What does that do? Well, it does a little housekeeping. It creates the document from the underlying library. I use a mutable here. So this is all code that once it works, you don't really have to look at. So I'm okay with using ignore and mutables and all that stuff. I mean, that's the whole point of what I'm doing is to put all that code here so that I can use it without looking at it. Right? It's all about my aesthetic values. That's all this is about. Nothing fancy. So for all items, you just march through and you process them and you do a match. And so if it's a page size, right? Well, you match it with either letter or a for all the other ones that will put it eventually, right? And you see, we've, you know, this is how you set a value, which in C sharp, it would be an equals, but that's not how we roll an F sharp. So you're going to set the page format to letter or the page format to a for any questions so far. No, it looks very clean. And especially when you showed how it was used book. I think I get what you're saying. Like how it's used perfect anyway. Right. And so then you like, okay, it could be a default unit. Right. So that case, we're going to take that mutable and send it either to inches or centimeters, right? And then every time we need to do that, we'll come down here and do this, this little bit here. And so we don't have to have a lot of repetitive anything you just say, okay, yeah, take your default unit and set this value to that. So now we're going to go through the section and a section just has is a list of items, right? Of its own kind. Right. So, so this is kind of interesting the way that our library works, we need a mutable to represent the section because we're going to create a new one. Every time we process this in a regular document, you're probably going to have multiple sections. So then we walked through the items. Right. So I came up with the notion of what I call a spacer. There's about 27 different ways to actually accomplish this with the underlying library. And this is what I mean by I want to hide some of the complexity. You have to make some decisions, but the most common thing in the world is you're going to put like their logo at the top of the page. And then you're going to put the title of the document or, you know, the quote for whoever, you know, and you're going to have these things. But you don't, you know, they're going to show you what they already have. Right. This is what always happens. It's like, oh, we got this out of our word processor. We want you to do that, but drive it from the system. Everybody familiar with this scenario is like, oh, we created this in this little app that creates PDFs. Right. So they'll have, you know, and it'll be like two inches and then there's the next thing. Right. And so I wanted to have a way to say, okay, I need some space. Right. So I called it a spacer. You sit at a double and the most convenient way to create that with this underlying library is just to add a paragraph. So that paragraph actually has no space of its own because I didn't add any text or anything to it. But I say, oh, there's a paragraph there. And there's this much space after it. So I get that much space. And this is, if it's an image, basically you give it a path to an image. Now, I will say that I thought about doing this a couple of different ways, but I decided what worked best for me. And since I'm really, really truly writing this just for me, if it's useful for other people, that's cool. But I had a thing I wanted to do. But basically you just, you give it a path to an image that you have already gotten to the right size. I don't want to try to do the imagery sizing or that nonsense. Get that out of the way with a tool that doesn't write because you're going to use the same images over and over and over. So, like, get the right size and, you know, all that stuff in a decent tool to get that done, right? And then, you know, I have this text that has, so text actually takes a list of text items that can either be a string or a style. That's basically so you can embed a bold or something inside your text that makes sense to people. Now, there's a whole lot more to this in the real world and the real version that I have. But for this, I wanted to keep it simple enough so that I could explain each section. But basically what you'll see is it just be more of this kind of stuff where at each level, you're going to process a list of items, which might have items, a list of items inside them and you're going to process them and you do the same thing over and over, which, you know, it's interesting in the sense that you have to figure out how you want to do it. But now that I've shown everybody this and hopefully you understand it reasonably well. Well, let me stop here. Any questions? But by the looks of it, it looks like a little lisp interpreter, right? Like a small like eval loop. A little bit in it. Yeah, yeah. I mean, that's what I'm associating it with. I remember writing something similar like from Haskell. The matching and like the types and the... Don't tell me anything about Haskell. I promise people there would be no Haskell talk. It looks very similar. I don't like... I don't do math. I don't understand category theory. I'm not really clear on what a bone ad is. I'm not even sure how to pronounce it. But here's the thing. No, it's not that complicated. That's what they all say. But let me tell you, when I look at the world, I see lists of stuff. I see discriminated unions like everywhere. That's just like in records, which I don't use in this, but you know, I could have. But to me, the entire world is made up of those things and functions that do stuff to those things, which is why, you know, I kind of enjoy this way of looking at the world. I never... I'll be honest with you. I've been doing this for over 30 years. I still don't get object-oriented programming. It just doesn't make any sense to me. I can do it. I mean, they're paying me to do it at GCC. I do it all day long. But like, I don't know. It just... Yeah. You do know this is being recorded. Oh yeah, I know. We can fix it in post. No, no, I'm going to make sure that... No, like, I spent most of my career writing C-Shark. Well, I did some VB too. But in fact, I think the reason I liked VB was that it was less OO. You know, I also... I'm perfectly fine doing imperative kind of stuff. I think that, you know, whatever works. Anyway, but I do tend to see the world not as objects, but as stuff. Records, DUs. And I just, like, to me, pattern matching just fits with my brainwork. So that's that part of it. So when you say that you see the world not as objects, how literally are we supposed to take this? So, okay, so I write business apps, right? Yeah. Okay, so, I mean, like, an invoice is an invoice, okay? If the invoice ID of this piece of paper and the invoice ID of this piece of paper are the same, everything else about them better be the same or somebody's going to jail, right? Rructurally, equality is what the rest of the world calls equality. But seriously, if you look at, like, things in the business world, like, you know, very few of those things have, like, behavior. Like, my favorite one is, like, oh, we have this object and we're going to have a save method on it. Like, have you ever been in the office? Like, documents don't save themselves. Somebody picks the thing up and stuffs it in the file cabinet, right? It's something that you do to it. Like, everything is a function. So the whole thing about sociology was my argument that functional programming is just Max Weber's theory of bureaucracy. It's like, you have these, you know, think about the DMV or whatever your least favorite bureaucracy is, right? What you do is you go in there and you have a piece of paper and you hand it to somebody and they do stuff with it. They might hand it to somebody else. And at some point, there's maybe some sort of reaction like, oh, you're going to take that driver's test or something like that. But in general, the way I view the world is a lot like what you see right here. It's like, okay, the things I could do something to it. Well, let's take the classic because every business likes to get paid, right? So your message of payment, right? Credit card, cash check, whatever. They don't have anything in common other than they're a way for you to give me money, right? That's a discriminated union. Now, we can fake that in the old world. We can have an eye payment interface and like, that's cool. And I do that when I'm writing C-sharp, that sort of stuff. But I just kind of see the world that way. And so this sort of simple functional programming has always appealed to me. So I was hoping, I was just really hoping that you were making a report about your perceptual system, like that you don't actually see objects. I was holding out for that. I don't think that's actually the case. I just don't have to model those objects in my code very often. Like, what is the classic OO example? Like, it's the animal, the dog and the cat and all that stuff. It's like, have you ever written anything like that? I mean, some people do. I mean, if you're going to write a game, you're going to have lots of, you might have lots of objects to do stuff. Like, I totally get that, right? I mean, that makes perfect sense. But in the business world, I think kids today, I always have to say that, kids today, like they suffer from not ever having seen a bureaucracy before computers. Like, I'm old enough to have been where I had to go to the Social Security Administration. Well, they had computers, but most organizations didn't. And like, you would actually fill out forms in triplicate. Has anybody filled out a form in triplicate lately? I haven't, but used to do that all the time. I mean, that's perfectly normal all the time, right? And why did you do that? Because you might have three different people who needed to see that document and process it in some way. Right? Which is the interesting thing is they needed sort of to be the same. They might get different stuff done to them, but they started out the same. Anyway, that's enough of me going on about my inability to understand object-oriented programming. Let's get back to, go ahead.  You don't pet your, you know, file of order, class, family, you know, pet. See, I don't have a pet. I actually live with two cats right now. Because my daughter moved back to stay with me for a few months and she brought a cat. And so I'm getting used to the fact, you know, there actually are animals, but I kind of like cats because they're jerks. Like, they don't expect you to be nice. They might like it if you are, but they don't really expect it. There are some kinds of pets that really expect you to be nice to them. That's a little bit too much trouble for me. Like, the cats are okay that I ignore them. Anyway, that doesn't have anything to do with programming. I don't know. Well, the full taxonomy is like a class hierarchy. But you might deal with an individual rather than its class of thing. Yes, that's probably true. But so one of the things I find interesting about discriminated unions is that the organizing principle for a discriminated union is what you use it for. Right. It's like the only thing that has to connect them together is that you're going to use it for the same sort of thing. Right. You're going to deal with it that way, which I think actually models the business world a lot more than people would like to think. I've always, I've always amused when I'm reintroduced to a giant code base with that creates DTOs everywhere. By the way, Peter, I have a few things to say about Quick Start. Wait, look, I have no memory of last week. So I will not take the blame for anything. Peter's work at Quick Start is some of the best programming work I've ever seen in terms of just sheer usefulness. But into the tip to solve it and solve it and solvable is a hard problem. She's at the PowerShell script that starts like 80 things. Yes. Awesome. Yep. That's all my best work at PowerShell, which is why I need to learn some raw other stuff. PowerShell is very much a functional programming language. It may be the ugliest functional programming language in the world, but it is a functional program anyway. I've gotten totally off track. Here's what I want to talk about. Before you do, can I jump in just some clarification on the distinction between strings and styles? Oh, okay. Just sort of content-wise, what is sort of being modeled there? Okay, so I'm probably going to change the name of style, but anyway. Actually, I got from the underlying library and I'm gradually like taking away the things that were the weirdness of the underlying library sort of surfaced into my model. But the idea is that you can do some formatting, right? And so what I'd like to get to is a point where, let's say you have font information. I think I took the font business out here, but with a font, you have the name of the font, the size, the color, all those things you can describe with a font. If you have a font item that's at the document level, for example, that should be your default font for the entire document. If you have one at the section level, you're saying, okay, this section is going to use this font. If you have one at a paragraph level, it's for that paragraph, right? If you have it in the middle of some text, it's for just the stuff that's in between it. Does that make sense? And so you want to be at, if you go back down here, I'm going to break my code, but all the way down here. So you want to be able to say, hello world, and then have some font information, and then some more text, right? Just kind of like you would do markup in Markdown or something. I haven't quite figured out exactly how I want to do that. That's why it's not implemented here. But generally, you know, I want those things. Right now it says the style is a string because the underlying library lets you define all that stuff and give it a name. And you can use the name instead of all the stuff, which is kind of a nice feature.  And so I'm going to call that inverted as an example of something I actually did in one of the documents I created because they wanted to have like a strip of black background with white text on top of it, which by the way, they only did that because they weren't printing the documents. It's really interesting to me that PDF really started out as a way to describe how something should look on a printed page. And now in most of the PDFs we generate, nobody ever expects to print them. Like they're actually designed to be viewed on the screen. In a consistent way. Right. So it's effectively like printing. It is. It's like printing to the screen. Without paper. Without paper, that's right. Don't have to smoke trees. We have moved forward a whole generation without print, without paper. All right, I'll stop. Valid point though. I have a question here.  Will the style apply to the one on the right side or the one on the left side? It's going to be the one on the right side. Mostly because that's easier to implement. Yeah. The idea, the idea I'm going with is, like, you should be able to tell by looking at this part of the code, exactly what the style applies to. I've just, I've gone through, I think, three or four different versions of making that work and I haven't really decided on what looks best. It's actually, to me, that's one of the more interesting problems of doing this is how do I make it so that it's easy to apply specialized styling to a particular string. Everything is easy until you get to the part where you need to make that one word bold in the middle of the sentence. And then you have to make some decisions about how you want to show that. The rest of it's all pretty straightforward. It really is. Maybe like the string can have another attribute style, like, you know, classic HTML format. I thought about that and I might end up there, but like I said, this is still very much experimental and I'm playing around with how I want to make it look. At some point, I'm going to put this up on GitHub if I can figure out which one of my now five, yeah, five GitHub identities I want to associate it with. You know what's a really strange though. I have two different ones that are associated with blinds account, because I was there as a consultant three or four years ago. But anyway, on the topic of you haven't decided how you want to make this look. Have you thought about, since this is sharp. Have you considered making this a computation expression. Great question. Yes, I did. It is too complicated for me. No, seriously. Yes, I mean that's a that's like a sharp. That's an obvious question to ask, right? Make it flat. Why not make it a computation expression. And my answer to that is, I really like these lists. I really do like doing something as simple as a list, because I can explain to almost anybody how list works. It's a very natural concept. We make lists all the time. And, you know, yes, I can tell the computer nerds that yes, it's a single link list, but you know, I can tell my six year old granddaughter that it's a list. And she knows basically what I'm talking about. Right. They have listed kindergarten or first grade. Right. They know how that is.   Now, there are some things that like, you can't really do this way that you really have to do. Like, you don't have to. But the guy who wrote the x86 processor, the similar code processor as a computation expression. Has anybody ever seen that? That's the weirdest thing in the world.  And there's a guy wrote an F sharp computation expression that will take those and execute. Think about that for a minute. That's right. So yeah, if I was going to do something like that, I'd probably do a computation expression. But in this case, the value here, it's interesting when you go to do this. If you think about how the underlying library like this works, the order that you do things in is really important. Like, if you add the text, before you do the other thing, the text goes higher on the page, right? I mean, it is really, it actually matters. And lists are perfect for that, right? I played around with using some, I thought it might be useful to use a record for something like a font setup. But even within that, it didn't work out very well because it gets a little ugly if you don't want to specify every single thing. So this actually, it's kind of nice because you can specify as many or as few of the things about a font as you want, and it will change just those things. And it actually, in this case, I mean, I use records for all sorts of things, but there was no place for records in this one. It didn't work out that way. So I thought that was kind of interesting in doing that. So like I said, I don't know, I mean, there may be an audience of one for this, but I'm going to build it anyway. I may never even get a chance to create PDF files in a sharp again, but I don't care because the best thing about this was I had so much fun doing this. Now, granted, you might think I have a strange idea of what's fun, but it is actually pretty rewarding. I would recommend if you want to see what the technique can do, go look at FS Excel on GitHub because it's really kind of a slick thing to be able to generate Excel files with just like, oh, go to this, go to this cell and write this data there. And it's really, that's what got me interested in doing this is just how slick that was and how easy it was to add features because the guy who did it originally, I did a couple of small features because I was interested in it. And then I realized I needed, you know, to work for what I intended it for, I needed to be able to take an existing spreadsheet and stick some data at the top of the worksheet. Don't ask me why I just needed to do that. So I just built that functionality into that app and it was easy. I was amazed just how straightforward it is to do something like this. So that's really pretty much all I wanted to talk about. I hope this made a little bit of sense and people can kind of get the idea.  In other places where you want to wrap something, you know, you have some really complex underlying library that you want to use and you want to make it just a little simpler for make the easy stuff easy. Now, I don't, I don't have any intention of doing anything, you know, really complicated with this. But, you know, I can keep adding features to it if I want, because there's tons of features. I'm going to add footnotes, because the library does a great job with footnotes. Anybody ever tried to do footnotes on multiple pages of PDF? That's not easy. I'm just going to say that this underlying library will actually it'll take a bunch of text, paginate it for you and then put the right footnotes with the right page numbers and everything. Where you want them, which is, like I said, I, I've had to do a little bit of that in my life and it's just, it's, it just gets tedious. Outputting text in a reasonable way is much harder than most developers think.  Yeah, that's easy, but make it look nice for what people expect from a real document. That gets, that gets tricky. Anyway, any questions? Yeah, so, I was going to remark. Anthony mentioned something about the ability to write custom computation expressions in F sharp. I've tried doing something like that. I was actually inspired by as JCJ said that the x86 interpreter written in F sharp. And what I realized is I would get halfway through writing a computation expression builder. And then realize it was just easier to write it as a list interpreter, like JCJ has written and I think actually underlying that x86 interpreter is a list interpreter. I don't think it, I think the computation expression builder is mostly syntactic sugar on top of a of a function that just accepts an instruction as as input and mutates a state object. Okay, if anybody notices any change in my behavior, it's not because my boss showed up.  It's been a while since I've written a competition expression.  And then that's one of the methods on the objects. So then so you can talk all of that away. Like you can keep it a list processor. And then the API that you expose is just the do notation. Exactly. Yeah, I've seen lots of really useful things in computation expressions. It's like, I'm going to take the part that you saw that interprets the items, make that into a library that people can then use. And that hides all of that nasty and like, it's okay that it's there I just don't have to look at it. You know, another thing that I was thinking of looking at the way that you have the list, you know nested inside one another is, it kind of reminds me of. I think it's the giraffe view engine. Have you seen that. I've seen that it's been a little awesome delicate but I want you to talk about it doesn't look a little bit like that too. I wonder if there's anything, you know, there that you could borrow.  Yeah, I would prefer not to get too deep into the testing, but I do like the way the way you can do things with F sharp in terms of it. It forces the indentation right the indentation matters. And for the way I'm doing the list, I think it works really well. I think it looked to me it looked visually like, okay, I am, I am constructing this document in the following order, and I can see the hierarchy of where of what's going to apply to what that way. It just it's one of those things where it's like it's really it's not complicated. It's fairly straightforward. I just like I just like the way that looks, I liked it ever since I first saw the sharp implementation of Elmish I was like, that's really nice just because it doesn't look so much like a program as it does look like a document of some sort. Now, it's not going to look like the document you're building because you don't see the image, you know, it's not in that way at all. It's sort of the, okay, this is these are the components of the document that you can tell the order and what they're going to do. And one of the things I don't show it here is you could write F sharp code in the middle of all that too. If you look at FS Excel, you'll see some of that where they do that.  And that has been a fairly common requirement that I've seen where companies that sell either complicated stuff or complicated services, right, depending on what you decided to buy from the list of terms and conditions varies quite a bit.  And being able to generate that document and let somebody else worry about how many pages it is and where it goes but still be able to do footnotes is super handy. I can't say enough about the quality of the underlying library as much as I hate the interface.  But I endured the pain because it did so much stuff that I didn't want to do that I required.  You ever thought about that? So sometimes people want the page numbering to be from the very first page to the end of the page. And other times people want the page numbering to go from the section, right, with the section, which I don't want to do that. I want to let somebody else do that. But my, the way I get joy out of programming is figuring out the things I want to do and the things I want to let somebody else do. And I'm perfectly okay with the people who like to do different things. That's what makes life, you know, interesting is like, there are software developers who like to do that stuff. Like they like to get into the nitty gritty and make it all work just that way. That's not my approach. There are some things that I get that way about programming, but not that many. Okay, so any other questions, comments. So could, could you go back to the actual code? Sure. And talk a little bit about how closely does your interface map on to the underlying libraries interface. Okay. So that's, that's like the question I struggle with in a way. So it's probably too close right now. It's closer than I want it to be. And what I mean by that is the easiest way to build this is just to take, you know, the, like the add paragraph, right, and just expose that. That's not how I wanted it to work. How I wanted it to work was I have a pretty strong vision of how I put PDF documents together. I guess I've done that enough to know how I'm going to go about it. And that is, I see it very much as a, I want to set up the document default. I want to set up the page defaults, the margins or whatever. I really like the idea of having reusable styles, which they allow you to do in a very word processing sort of way. You don't see that in the code I have here, but basically you can define a style, give it a name and then reuse it anywhere in the document. And I've gone back and forth about whether I should expose that as part of this. In general, I tried to abstract away the library's way of doing things when they are sort of what I think of as kind of the generic thing to do. That's not that, there's not much of that because I think they, I kind of like most of the way they did things. The biggest thing I wanted to abstract away are the decisions about the 27 different ways to accomplish the same thing. Because there really are a lot of a lot of different tasks. There's many different ways to approach the I want two inches of blank space here. Right. And so it's like, I don't want to have to think about that. I want to figure out whichever one works most consistently and can be applied in the, you know, in any level of the processing of the list and use that. Right. So a lot of what I was trying to do here is just like, okay. They expose this very word processor looking interface. Has anybody ever looked at like the word processing markup language that word uses? Like, you don't want to do that. I understand why they did, but like that's not at all what I want to do. I want to say, okay, look, if you want to apply formatting or if you want to apply styling to part of your text, it should be, it should work the same way. Whether you're doing it for the whole document or for just this word. Right. And that's really what I'm trying to go for there is to get it to basically a simplified version of what they offer. And I understand why libraries end up this way because everybody wants another feature. And these guys have had a long time to build this. Although they haven't really added very many features. It was a commercial product for about 10 years. And then they went open source and at some point it got hard to add features. And the thing that really broke everybody was not at core because this library and most other PDF libraries used a lot of windows drawing functions to get the graphics in. I don't show any graphics in here. I'm going to add the graphic pieces later. Mostly not in graphics so much as tables because most of the documents I ended up doing the tables. But anyway, so people have struggled to replace all that functionality in a way that's cross-platform. But this particular version of the library that I'm using has done that by incorporating the six labor image sharp stuff. That's this particular namespace in the LL but they have a ton of stuff for image processing. And so it's taken a while for the library to work effectively in .NET Core. There's also a bunch of things that you would think are minor but actually you have to figure out how you're going to deal with them. The default place to look for your fonts is different on Linux or Mac or Windows. So you don't see it here because I haven't gotten to it yet. But there's this whole bit with a font resolver or letting people just hand you a directory where you're supposed to find the fonts. But that has to be the same for the entire session for weird technical reasons. Anyway, did that answer your question or did I just ramble forever? Both. No, thank you. That was helpful. The other question that I had is, do you happen to have a more complete document that we could take a look at? I'm just curious to see if it's more complicated. I do, but I can't share it. Okay. It was work I did for some of you guys. I had hoped to have time to create a really complex document of the kind I've done before. And I'll be honest with you, I just ran out of time. So are there any other questions or is it time to move to the unrecorded open mic? We're not supposed to talk about that part. I forgot. We'll fix it in post, right? Exactly. All right, so I'm going to thank John very much for this talk. It was very, very interesting. And I am going to stop the recording now. And thank you so much. This is great. I really appreciate it. I hope you all had half as much fun as I did.


If you want me to continue with my work, it is crucial to support the channel via Patreon. Moreover, make sure to subscribe to Bobby's Perspective on Rumble. All the links are in the description box below. May Allah bless you all. Alright guys, welcome back to the channel. If you're new Manus Bobby guys, today a very different video. I found a profile on ex formerly known as Twitter that is called Torah Judaism. Torah Judaism is a page dedicated to Orthodox Judaism and it discusses the Zionist state of Israel. On this page you can see how Orthodox Jews stand up against the Israeli government and how they do not agree with the Zionist ideology. Why the opposite? They are in full support of Palestine. Before we jump into the video guys, as always if you enjoy my work over here leave me a thumbs up, subscribe to the channel if you haven't already and check out the links in the description box for further support. And now with no further ado, let's have a look. Alright, here we are on the page Torah Judaism. Let's get through their posts. The Orthodox Jews write, The Zionist state of Israel reminds us of Nazi Germany. Zionists oppress Jews in Mea Sherim, the anti-Zionist neighborhood of Jerusalem. Just as the Nazis raided ghettos, the Zionist police constantly raids Jewish neighborhoods and inflicts violence on Jews. Israeli police regularly raids the Jewish neighborhood of Mea Sherim in Jerusalem and detain the Jews. Let's play the clip. So I have to say that I understand both sides here, the Orthodox Jews and the Israeli government, believe it or not. Orthodox Jews do not subscribe to the idea of a Zionistic Israel. They do not believe that Israel is supposed to be taken by force. They believe that God has to grant them this land and in the moment they're finding themselves obviously in an occupation by the so-called Israeli government and therefore they do not consider Israel being the legitimate Israel, the legitimate promised land. Now from the Israeli perspective, I have to say that I understand their view as well. No, I do not agree with Israel in itself. I do not agree with the idea of Israel. But nevertheless, now that they have their government structure in place and within their government structure, you do have outlaws, so to speak. You have people that stand up against the government. Of course, the Israeli police will persecute them. It is the same all around the world. You see it in Europe. You see it in America. When people stand up against their, quote unquote, government, it is seen as treason and therefore the government will infiltrate those groups. Let's proceed with this clip. All those Jews brought us. In the time when the world is suffering in Turkey, in Syria, with this terrible earthquake, such sorrow and pain, and there has to be any pain that they're oppressing the Palestinian people with the expansion of settlements, with this is a created dachma, a created catastrophe. And we as Jews are crying about this and telling the world that our religion as Judaism does not permit to occupy or to stay out, have the state of Israel is against our Jewish religion. There you heard it, man. And yet again, those are orthodox Jews. So we have to take their word for it. Yes, and this is what I said in the beginning, believe it or not, orthodox Jews usually see the state of Israel as blasphemous. They see it as an attempt of men trying to trump over God, this truly what they say. And therefore they are taking the secular state of Israel. They do not see it as legitimate. Why did they make a whole court? I'm asking why they made a court system when we had the Torah? What's wrong with the Torah? You know, it's a Christian rebellion against God. Why? It's because they don't want Judaism. They want nationalism. They hate God. They hate the religion. They want the nation. You cut yourself off from Judaism by by affiliating with God. Exactly what I just mentioned, orthodox Jews stand against the secular government of Israel. And this is a parallel that we can find amongst other religions as well, Christianity and Islam. If we look into orthodox Christianity, orthodox Islam, we see as well an opposition to secularism, to liberalism and so on. And it is extremely powerful that we have orthodox Jews standing up for the truth, because it shows the hypocrisy of the state of Israel. It shows that it's a purely Zionistic, nationalistic, ethnocentric movement that has nothing to do with the original religion of the Jews. You cut yourself off from Judaism by by affiliating with Zionists. Okay, so the Jews that are Zionists are not part of on the side. Is that your opinion? I would be much more happy if they would have gotten no Patagonia than Palestine. Back to Israel. They're on the side. So the committee is two crimes. We're not going to war.  The defiling the land, the power... You cannot blame the consequences of the wars just on the Jews. Shalom. Do you want to have a conversation? I don't know about that. There's no peace with what we call the language. Why would we want the world to know? Because we are Jewish and because we are true to our religion. We are in total opposition to the existence of the Zionist state of Israel. We have a message for this Nakba day, for this 75 years of occupation. We want the world to know that what is being perpetrated against the Palestinian people, the oppression, the subjugation, the terrible cruelty is not in the name of our religion. It is not in the name of the star of David. It is not in the name of the Jewish people around the world who are true to the Jewish religion. Because we are Jewish and because we are true to our religion. We are in total opposition to the existence of the Zionist state of Israel. I call it Zionist state of Israel because it is Zionist. It is not Jewish. Judaism in the Torah forbids Jews to have our own sovereignty, our own entity, since the destruction of the temples and Jews true to the Torah, never aspire or try to have our own state. We also are forbidden to kill or to steal. The whole concept of taking this land away from the Palestinian people it is totally antithetical and it's contradictory to my religion, Judaism. All right guys, in this is it for today's video. I find it extremely important to share this with you and ideally you will share it further as well. Share their page Torah Judaism on X slash Twitter with the world as well. So hopefully many more people will make the connection that true religion stands alone and is not connected to nationalism, liberalism, secularism or in this case clear Zionism. I truly believe that it's one of the greatest battles of our time to truly understand what adherence to religion means and to not intertwine all of those concepts. I for example grew up in the West. I identified as an Orthodox Christian. However, was I really an Orthodox Christian growing up? Surely not. I had no clue about my own religion. As everybody growing up in the West I was influenced by all the outside sources and the major driving force here is of course secular liberalism. Seeing that the church is not part of the state, therefore not really putting too much emphasis on God but rather on so-called science. Moreover, we have a very liberal worldview in the West of course and you start taking those things for granted. You see them as normality. It is of course totally normal as a 13-14 year old in Germany to go out, party, drink alcohol, smoke, cigarettes. Just explore the world by yourself. That is normal and still you will identify as a Christian. That is an anecdote. That is something on a personal note. However, in the greater scheme of things we see how destructive it can be of course because nowadays when we look at America we identify it as a Christian nation. Does America truly adhere to Christian values? Of course not and the same can be said about Muslim countries too. The same has to be said however about Israel because when we look into anti-Zionism amongst the Jews we find certain arguments. So here we have a list that some Orthodox slash anti-Zionist Jews hold. Masianic beliefs. Some Orthodox Jews believe that the establishment of a Jewish state should only occur when the Messiah comes. They view the modern state of Israel as a secular and political entity not the fulfillment of a religious prophecy. For these Jews the secular nature of Israel and its founding principles might be seen as incompatible with their religious beliefs. And that is exactly what I just mentioned and it makes sense once you understand that secularism opposes true religion. Just as secularism in Europe removed the church from the state, secularism in Israel or any other country for that matter removes the religion from the state and therefore it cannot be the prophesized promised land of the Jews if that land now is secular. Zionism versus religious Zionism. Orthodox Jews who reject the state of Israel might do so because of a distinction between political Zionism and religious Zionism. They may support the idea of Jews returning to the land of Israel but disagree with the political and secular aspects of Zionism. Yeah so that's basically the same argument. Historical concerns. Some Orthodox Jews have historical concerns about the way Israel was established including the displacement of Palestinian Arabs and the potential impact on Jewish Arab relations. They may feel that the founding of Israel was not done in a way that aligns with their ethical or moral views. Yes and this is their right because guess what? If an Islamic country kills innocent civilians this is not in accordance with the religion.  So every time that happens this organization goes against Islam and this is ultimately what we're talking about because the question is do you adhere to your religion? So in other countries this question might not be so prominent not so important after all as it is in Israel because the whole claim of Israel is based upon the religion. If you look at people such as Ben Shapiro you will see the claim that Israel was promised to the Jews. That is the promise of the state of Israel. Never again. The Jews will not stand by. They will not be murdered. They will not leave their biblical homeland. They will not surrender. They'll be strong and courageous. Chazakh b'amat as Joshua said three thousand years ago. So it is a religious claim after all. However the state of Israel is not a religious state. It is a secular state. So that would mean it is not the fulfilled prophecy and if it is not the fulfilled prophecy it would imply that Israel is an illegitimate state and that would then further imply of course the absolute atrocity of this occupation because it has absolutely no grounding to stand upon. There would be not one single reason for Israel to occupy the poor Palestinians. All right guys but this is it for today's video. I hope you got something valuable out of it. If you liked it leave the thumbs up. If you haven't subscribed already guys please do so and if you want to further support this channel via Patreon for example all the links are in the description box below. Thank you so much for your ongoing support guys and as always may God bless you all. Much love and peace.