How to Build & Deploy an Online Tic Tac Toe With Ktor And Web Sockets

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to an amazing new long video today in which we'll build something very amazing what we'll build will be an online tic-tac-toe game which you can play with your friends in real time so if you finish watching this video then you will definitely know how you can build your own backend using K tour so that is a kotlin back end which I just like the most from all back ends because I'm just so familiar with cotton as you probably are as well then you will learn a lot a lot about web sockets so that is just used for this real-time communication between different devices between your an Android device and a back-end server and you will learn a lot about how you can actually take the server and deploy it so you can make it just available to anyone and you can actually play it with your friends instead of just in your local network and of course you will learn a lot about jetpack compose to build something like this this custom grid here and yeah just to give you a quick demo of how this game works I have my two devices here and if I open the the app on my other device where the first device is currently waiting for player X then you will notice now it says Nexus X so play X actually has to play and if we then click there you can see player X actually made a turn now Nexus o now we need to click here obviously which is the other player so we could say okay let's make it here then you can see it will appear in real time on the other device as well now we can make our turn here again and player oh let's say here player X I don't know um so yeah let's say we actually win so now play racks is at a turn again so we go in here and then we can see player X actually won then after five seconds the game will simply restart and now we can yeah just Play Another Round Here of this game and this will of course work online so you don't need to have both your devices next to each other you can play this with your friends who sit in another country so all this will be stuff you will learn in this video and I know this is a long video but I can really just recommend to really watch this till the end because these long videos are the ones that teach you real development because here you build something that actually does something that is actually fun to play and that has an actual use and like these 10 minute tutorials are great to don't just a simple concept but these long videos really dive deep into specific topics into building an actual app and also how to connect it with a with a back end we can deploy the back end to make something really functional and also just watching a tutorial or such a course is not enough I would really recommend and that at the end of this you extend this app and I will actually give you a homework after this so I'm just a recommendation what you could do to extend this to improve this app so you can also practice on your own after this [Music] and before we actually jump into coding there is one more thing this video is actually sponsored by hostinger you know I don't often have sponsored video content on my channel but when I do you can be sure that I can fully stand behind that and that is the case for hostinger who already sponsored two other videos on my channel in which we also build cater servers together with an Android app and right now since it's Black Friday again it's this time of the year again they have an amazing offer where you can get a web hosting plan for just 249 a month for four years so this is a really affordable hosting plan so you will get a free domain with this you get unlimited traffic you will get SSL certificates and what I like the most is that they actually have 24 7 support like we all know this when we have a problem then the support hotline or the support email contact is not available at that at that time so it might be midnight or it might be Sunday but here you will really get that 24 7 support which I think is really important for web hosting so with this offer you will really get everything you need to build a solid website it here you can click the claim deal add a card and you can actually save an additional few bucks here by using a discount code by scrolling down and clicking on have a coupon code and here you need to enter Philip 7. so if you apply this then you will get an additional discount here on this already quite affordable price however if you actually want to follow through this video and deploy your cater backend which we'll build here so you can play this game with your friends and not just in localhost then we are not interested in this shared hosting instead we want to go to yeah I want to go back and go up to VPS VPS stands for virtual private server and that's basically just a remote Linux machine which we can control where we have root privileges and that is what we will need to actually run our cater instance on so in that case we would need to Simply go to VPS click on start now and here we have different options depending on how big and how many users we actually um I mean users or app actually has but we will get to this later data so we will be totally fine with this vps2 option you can also choose this one if you're just caring about testing this app but as soon as you have an actual user base I would recommend at least this one but we will get to setting this up later in this video when we actually build something that we can deploy before that and let's actually jump to another website where we can set up our K2 project and that website is star.cater.io because if we want to create a keto project we need to do this while this website if you have IntelliJ ultimate which some people might have then you can also do this directly within an Android Studio within IntelliJ but if you don't have this what most of you will probably not have then we need to do this with this website and here we first of all need to enter the name of our project let's say Kate war or let's just say Tic Tac Cho and we can adjust the project settings here a little bit in the website let's change this in my case to field coding change this to whatever you want we can choose the Kato version we can change the engine and we can change the configuration here we want to choose okon file so that's just more like a a Json style configuration you can say doesn't make a big difference but I'm just used to this so let's pick that and we want to add plugins so plugin in terms of cator is just something just a certain feature a back-end should have you can see this could be authentication if you were to implement that we don't have this here but this could be a sessions this could be routing we need this so we click add that just means that we can Define routes where our app can connect to we want to make sure that we have content negotiation which will make sure that we can actually parse Json data in this on this backend we want to make sure that we have call logging so whenever a client or app makes a call to our backend we will see a log message basically for which route that was and things like that and if we scroll down we want to make sure that we have cotton X serialization that will just be the Json Library we will use two powers to and from Json and most importantly websocket websockets will be needed to actually handle the real-time traffic the real-time communication between our backend and our app because as soon as we actually click in a tic-tac-toe field we of course want to send that information immediately to all other devices in this case just to yeah these single other players device and if that's done we can click generate project which will simply download a zip file for us which we now need to yeah open and extract and then open the extracted folder in IntelliJ so please do that extract it in IntelliJ you can simply click open select the folder and then I'll see you back in IntelliJ so here I am in IntelliJ I opened this project that I just downloaded we can close this tab here Gradle sync was successful that looks good and then let's explore this product structure a little bit before we actually write our first line of code you can see it looks very similar as an Android project we have Gradle in this case it's the cotton version of Gradle if we open our build.gradle file then yeah we just have our basic configuration here we will put our dependencies in here and we already have some of the default dependencies you can see websockets was added for example because we included that feature on the startup caterer i o website and things like content negotiation content serialization all that stuff we don't need to worry about this we can be sure that we have what we need here the interesting part is now in the source directory Main kotlin and here we have our application KT class file rather so here we have our main function which will be obviously called when we launch this app and it will call our application module behind the scenes and it's still indexing here which is why the highlighting doesn't work doesn't matter here we just configure everything all of our plugins all of our features in K tool and if we take a look in these functions for example configure sockets then you can see we get in here in another file that was already created for us with some default code default config here we actually don't need this but in case you you want to find out how you can actually use these plugins these features in K2 then yeah it's quite helpful if they actually already add some default code for you so in this case here we install this websockets feature we configure it for example we configure a ping period to find out if a client is still available and we configure a timeout and things like that then here we have a routing Block in which we have a websocket block to actually Define a route where we can connect you for this real-time traffic so this will essentially just be an HTTP request that is done once by the client and after that was successful the client and server will then yeah just have an ongoing connection where both parties can send data to each other at any time but we will get more in detail into this when we actually get to writing the code for our tic-tac-toe logic let's remove this routing block for now actually not just for now we will remove it and take a look at our plugins folder here we also want to take a look at monitoring this is where our call logging feature is set up so how our logs will look like for the client requests we have our routing file here we just have a very basic sample route which will respond with hello world we can also get rid of this um actually just the let's just get rid of this get request that was specified here and we can take a look in the serialization file in which we set up kotlin act civilization where we can also get rid of this routing block here we don't need this we just want to make sure that we have this installed content negotiation block with the Json function setup and it was interesting before we start with this is our resources file where we see our application config so that's just yeah some basic server config most importantly for our Port so here we can specify which Port our server is running on let's actually go to application and run this for the first time by clicking on this little play symbol clicking run application and then it will build this and hopefully everything will go fine you can see it's now running and it does not crash otherwise you would see an error okay it does crash address already in use that is a common crash because there is already a program running on my machine here that is using this exact Port so if you see this as well you don't have to see this if you don't have a program running on that Port you won't see this but if you do then you can simply change the port in your application config file for development for example 28081 relaunch this and then if there is no program running on that Port we should not be getting that error as you can see so that's it for the IntelliJ Cato round trip let's actually jump into coding and get you the exciting part by starting to create a little package for models so in our root package we want to create a models package and then here we will put yeah basically our whole games Logic on the one hand we want to represent the current state of our game in one single data class and then we will keep on taking that instance of that data class and send it as a serialized Json to our client so they can also just update their corresponding local state you will see how this works so we just create a new file in the models package called game state and we want to make sure that's a data class in here we want to annotate this with serializable since we want to be able to Powers this to and from Json and if we use carton axialization we need to annotate these classes with a serializable so now we need to think about what really belongs to the game stage so what is really the state and what do the clients need to know about the current state of our game first of all they need to know which player is currently at the term so who is next so Val play return and I will represent these players here by a simple character which will be an X if it's player X and an O if it's player o and which will be null if there is no player at turn right now so we set this to null initially or we can actually also set this directly to X so we just say x always starts well then we of course also need the information which kind of sign is on which field so how our field really looks like and we can do this with a two-dimensional array so valve field is an array of an array of characters of nullable characters and we can then fill this field with yeah either axis O's or nulls if there is no player who made the turn yet at that field we can then also create a little helper function which will create an empty field for us so you just get a little bit of of an understanding of how that will look like so right here we could say function empty field which will then return an array of an array of nullable characters we can say that returns an array of and here we will put in three array ofs as well which we initialize with another values so just like this that is our simple tic-tac-toe field where each field is free and if player X for example would Now set the x in the middle position we would replace this with an X here and that is how we will represent the game State we can then assign this empty field to our array by default and then go on what else do we need we need the information who is the winning player if there is a winning player so well winning player it's also a character and if there is no winning player yet this will simply be null so if this is not null we can be sure there is a winning player and it is set to either X or o we then also need information whether the board is actually full so then it would be a draw so if the yeah just if there is no winner but the board is full we need to make sure that we actually relaunch the game because there is no other turn to be made and the last kind of value we need in this game state are the connected players so we just know when everybody connected and when we can start the game which will be a list of characters and set to an empty list by default you can also see that we get a little warning here because um IntelliJ wants us to generate some equals and hash code functions because that's not done by default for this array field for this two-dimensional array so we can just press Alt Enter and click generate equals in hash code by considering all of our other fields clicking next clicking finish and then it will generate some cryptic functions for us just to make sure that if we compare two games and objects that we really get the same behavior as we would have with the data class that does not contain such a field cool and that's all we need for our game State we will have the exact same class client side since we will then be simply updating this game State server side and then whenever this was updated we will put it into Json format and send that Json string to our clients which will deserialize it and then update their local state what else do we need well we of course also need to have some kind of way for the clients to tell our server this is where I made my turn and that is what we'll make with another model so in our models package we will also have a make turned class which will be a data class and with this class we will simply be able to tell the server at which position the client made their turn so Val X and Val y and we also make that a serializable and that's already it for this class so in this case the client would create this class would serialize that into a Json string and send that to the server which would then simply update the game State here cool so now that we actually have our states and our actions that the clients can actually perform the next thing is that we Implement our actual games logic so the core of our application you can say and this would of course be implemented server side because client-side these clients could theoretically manipulate the data they send and then manipulate the the game's logic so things like that should really always be done server side because the server can really validate in the way they want to in the way it should be validated so here in models we will also create another class which is called tic tac toe game which will represent a simple game here in our app and here we will really be spending most of our time at least server side and first of all we want to have a private valve for our state which will represent our game State and this won't be very different from what we know from Android we will use immutable State flow here which we initialize with an empty game state here we don't need this public version of the state because we don't really need to access this outside of this class so we're just fine with the State field but what we also need is a private Val player sockets so that is now websocket specific but if a player connects to our websocket route then we kind of need to store or keep a reference to the connection between client and server and this connection is in the end called a websocket session and we can use this connection to just send data to a specific client at any time and we of course need to keep track of these as long as a certain player is connected to our game and we will do this with a so-called concurrent hash map which will map from characters to websocket sessions from current hashmap because we just want to make sure that we that we can access this hashmap from different threads that's a very likely thing that is going to happen on a server environment that we access things like that from different threads so you want to make sure that there won't be any race conditions and concurrent hashmap makes sure that that's the case and then we say okay player sockets for player X is actually this soccer connection and for player o might be a different websocket connection and now we can easily use this map to send a specific data to a specific client and for now we also want to have a private Val in game scope which will just be a protein scope running in supervisor job plus dispatchers.io so that will just be our current scope we will use to launch proteins inside of this class which we can then also use to cancel certain coroutines very easily cool so what do we actually start with to write here I want to start to create a function to connect a player so as soon as one of our clients actually makes the request to our websocket route we want to call this function with the websocket session that was actually passed to this route and then we will return a nullable character here for whoever actually just connected now we first of all want to check okay is there actually already a player X in our room because the logic I will choose here is that the first player who will connect will always be player X and the second one who connects will always be player o so we first of all want to check is there already player X so is player X and we can say okay that is equal to stated value connected players and if that has any any values where it is equal to X we can then check if that is play racks and if that is the case we want to set the player equal to let's just do it on one line I want to set the player to O So if we already have player axles connected and else we set it to X then we can update our state our game state so state that update and first of all we want to check if there is already a socket for this player so if player sockets that contains key and the key will be player and actually want to check if that's not the case so if there is no key yet for this player so if there's no no existing connection no existing session we want to assign such a session so in that case we want to say player sockets at the Key of player is equal to the session we passed here and what we also want to check before is if there is actually already a player connected because right now we could potentially connect three players into our room um if yeah like if the third player would connect this line would simply reach untrue so we will jump into this else block and make the third player X again which doesn't work in tic-tac-toe of course sorry first of all I want to check if the state value connected player contains the player already if that's the case uh come on we want to return null here so then we just then we will just close the connection from where we will call this function cool then after this if statement here we just want to update our state so we can Excel the existing game State using air.copy and here we can say connected players is equal to it connected players plus our new player and then after this state update block we can then return our player that actually connected and that's already it for our connect player function which will work just fine and if we have a connect player function we also want to have a disconnect player function which will be next so right here we want to have a function disconnect player um the player will be passed in form of a character again and in here we just want to say player sockets player sockets that remove I want to remove the key player and we also then want to update our state so state that update and here we can just say it.copy and connect the players is equal to it that connected player's mines outplayer so we just remove the player that disconnected from our existing list and that's it for the disconnect function that's much simpler than this connect function here now next we want to have a function to send something to all players and specifically we want to send our game state to all players so let's create something called broadcast and we want to broadcast our game state and the way this will work is we will simply use our player sockets and we can use these websocket sessions to send something to those specific sockets so we can say player sockets that values for each here we get a reference to the socket and then we can say socket.send and we need to send a so-called frame so a frame could either be something text based which we will use here so we will send Json data or a frame could be something could be bytes so if we were to send a file or so so in here we want to construct our frame or we can also directly pass our string which we'll actually do here and to Simply pass parse our game state to Json we use a Json Dart encode to string and we simply pass the game state and we get an error here because that's this pen function so we also need to make broadcast a suspend function and then here in our init function so when we create this class we just want to make sure that we use our state and we can say dot on each so on each emission we simply want to call this broadcast function and we can then say a launch in our game scope so what this line will do is it will say on each emission so whenever this stage changes we call our broadcast function with that corresponding state so you can see we pass the stage of this function and you turn the state changes and we will broadcast it to all players to just make sure nobody will miss any updates we launched this in our game scope which will then just make sure yeah that we have a cool routine where this broadcast function can run in cool but that's not it yet we need to write a little bit of more code for this class the next function we will write is a function called finish turn so whenever a player actually makes a turn we will call this function for a specific player and for a specific position where the player actually made the turn so in here we first of all want to make some assertions that we don't really finish a turn if we don't want to finish a turn so first of all if state that value dot field at the index of Y and X if that is not equal to null so that means there is already someone who made the trend at that position then we of course want to skip this so we then don't want to make another Cross or another circle on that specific field or if State value winning player is not equal to now that means we have a winning player then we don't want players to be able to make more turns so in those cases we simply want to say return here and we also want to check if um State value player at turn is not equal to the player who actually makes this turn and finishes this turn then we also want to return because only the player who is actually at turn should be able to finish their turn so then we can say we got the current player which is State value player turn and we then say stated update and in here we first of all want to create a new field so now that the player made the turn at the specific field we want to of course update our Fields data so we say Val New Field is equal to it dot field that also and here we get a reference to that field and we want to say Fields um at the index of y and X is equal to our current player so we just update the specific field to our player who made that turn and then save that in New Field then we also want to check if the board is full so Val is board full is equal to New Field dot all so we check for all Fields so we would get a reference to an array of characters so basically this will be executed for every single row and then for every single row we want to check we want to check all again and check if it is not equal to null so if all fields in our new field are not null that means there is no empty field anymore so our board is full and if that's the case so if the board is full we want to say start near Round delayed that is a function we will create which will simply start a new round after five seconds and apart from that we want to update our state so we update it to it copy we want to say the player at turn is now if current player is equal to X then the new player at turn will be o and else it will be simply X than the field will be updated to our new field is board full will be updated to our esport full Boolean and our winning player is well here we also need a function to determine if there is a winning player and if so who is the winning player now we'll call this function get winning player and if there is a winning player we can say that led or rather also and also call our function start neuron delayed because yeah if there is a winner we also want to start a new round after five seconds so those two functions are functions we now also need to create in this class but I think these are the last two that we need to make our at least our game logic work and then only the connection stuff with our websockets is missing so let's actually go here on this function press Alt Enter and create that function start new round delayed and all we really want to do here is we want to remove this and we want to scroll up and create a reference for this cartoon job which we'll use to delay this um yeah starting this new round so we can say private VAR um delay game job or so which will be a job nullable and null by default and then down here we first of all want to say okay the we want to cancel this job so if there is an ongoing job we want to cancel it and then we assign a new job to it using games gamescope.launch here we want to say we delay this for five seconds and then after these five seconds we want to update our state so state that update and then here we say it that copy well who is the player at turn we want to start with X so X will always start at the rounds the field will be game state dot empty field the winning player will be null is board full will be false and the connected players will simply remain because nobody disconnected so now we already have this function and the last function we need is get winning players we can also out enter on that and create that function and this function will return a character in another book character which will be uh yeah exactly the player who won or null if nobody won and I will say that I will actually paste this code because it's super repetitive I will explain what happens there but as you can see if I paste this it is very unreadable but it's yeah that's basically how we evaluate if a tic-tac-toe game is won by a specific player so for example we check if field 0 0 is not equal to null and then if 0 0 and 0 1 is the same same kind of player and if 0 1 and 0 2 is the same player so this would correspond let me think I'm probably to the first row so the first field this is the second field and this is comparing the second field with a third field so if this condition is true we know that the first row of our game is filled with the same player in that case whoever is on field zero zero will be the winner and so on so we basically just go through all the different combinations we could have with a tic-tac-toe game to to have an active winner and then yeah just return that here whoever is at that specific field if all these conditions fail we simply return null so then there won't be any winner now of course you don't need to tap this by hand you will find all the code for this project down in this video description so you can also just copy and paste this function here because I don't really see a reason in typing this off there's not really a lot of value in that okay so the last real thing we need to do here for our backend is the route where our clients can connect to to use or to communicate via websockets and let's just do this in our root package here create a new file called socket route make that a file and the way this will work in K2 is we will simply make this an extension function of a route so we import this and say let's just call this yeah socket for example and then in here we will actually get a reference to our game so our tic-tac-toe game and then we can say okay we have our route here we specify the path where our clients will connect to which I will just set to a slash play where they can play the game and then we say websocket you already saw the syntax in in the pre-configured function from K to work and yeah we just use this to to create a websocket connectable route you can say and in here you can also see that we get a reference to a default websocket server session and that is exactly what we need to send something to our clients and to receive something from them so if we say this a DOT incoming then this is a received channel of frames so as soon as a client sends something this receive channel will be triggered and if we want to send something to our clients we will use this out out going instead you can see this is a send channel so we can send something into that channel for example our game State updates so we first of all want to get a reference to the player that connected and that will be equal to game.connect player and the session that we connect to or that yeah the player connected with is simply this and if that player is equal to no that means that there are already two players connected then we simply want to close this connection so we want to avoid or yeah just make sure that not three or more players can connect to a single game you can pass a close reason so the client knows why the session was closed and we can say uh close reason codes that cannot accept for example and we can say two players already connected or so and we return out of this websocket function if that's not the case we want to have a try catch finally block so we just want to catch exceptions here and we want to have a finally block and the way this will work is we will now go inside of this try block and we say incoming dot consume each which will now be triggered for every single piece of data our client sends here we get a reference to this Frame and such a frame will now just contain for example a Json string with information where a player made that turn and if that frame is a text frame so it contains just text then that's a frame where we are interested in and we basically now want to extract the action that the player wants to perform so in this specific case here because we have a super simple game there is only one action that could come from a client which is to make a new turn but usually in more complex apps you would have more different types of actions for example if you would want to also allow players to manually restart a new round and don't automatically do this after five seconds then you need some kind of button in the in the client app where the clients can then click on and then also need to send some kind of action so there are different types of actions we potentially would want to parse from and that is what I will what I will create a little help function here to extract a certain action so here we pass the Json message that was that arrived from our client and in this case we will just return a make turn but if you would have multiple actions you could adjust this function a little bit to support maybe like a generic type and Powers it based on whatever that message is and the the format of our messages will be something like this so we first of all will specify the type of action so we want to make a turn then a hashtag and after that we have our Json data so in this format we will parse our actions so we first of all want to check if that what that type actually is and based on the type we will then parse this Json body so first of all the type is message Dot substring before that hashtag so just the the string before a hashtag and then we have the actual body which is message substring after or hashtag and then we can return if the type is equal to make turn then we want to Simply take our body and say Json decode from string this one here and we want to decode our body import Json and else we just we should make turn with let's say minus one minus one just an invalid version this will never happen because we only make sure to send these make turn actions from our client and then we can scroll up go in here and say Val action is equal to extract action and the message is frame that read text that is how we get a reference to the text that was sent from the client to the server and then we can say player a DOT oh no actually game dot finish turn because when that action is received we want to finish the turn for that player the player is this and action Y is action.x and action.y just like this and that's already it and what we need to do in this incoming consume each block here but what actually happens if the client disconnects how do we detect this your server side in this case this try block will actually throw an exception because this channel was closed then in here we simply want to print the stack trace and then finally so in all cases when the stride block finishes we just want to make sure that we disconnect that player that previously connected and that's pretty much it what we need to make our game work right now this function is unused we need to set this up we need to call it explicitly to tell our server that we have this this route to yeah just set it up like that and we do this in routing so we open our routing file and in this routing block we can simply call our socket route and we pass our game instance which we can also pass as a parameter here just to follow dependency injection a little bit and then what we call this configure routing function which is in our application KT file here we can create our instance of our game so just say that's equal to tic tac toe game and pass it down here and then in our game whenever our state actually changes we just make sure to broadcast that new that new state to all of our clients that are actually connected to the game so all of our player sockets and that's it for the server for now we will later actually get back here when we want to deploy this to hosting or VPS instance because then we also need to set up a little bit stuff in our Gradle file but for now we are fine and we can test those locally and the way we set it up right now so let's actually just run this once and hope there is no error and I also hope I didn't make any any other kinds of errors here which will yeah yeah probably see when we play this for the first time when we also build the Android app but you can see our server just uh runs fine and there are no errors there are no exceptions which looks good so I would say and um suggest that we now start building the Android part of this project which is also quite cool and exciting so here I am in an empty Android Studio project and all I really already set up are the dependencies and Gradle configuration you will find the initial source code Down Below in this video description on my GitHub so let me just quickly show you what I already set up here um first of all these two class paths here for the hilt Gradle plugins or you'll be using dagger Hill for dependency injection and we will also be using the kotlin serialization library for Json parsing and then in build.gradle app I added these three plugins this one is also used for a dagger this is used for dagger this is serialization again and if we scroll down here we have our dagger dependencies we have dagger navigation compose which we actually don't need here by the way and we also have the view model compose dependency to get a reference to view models in that big compose and what's important is K tour so cater is also a client-side library which is comparable to something like ok hdp so basically um the yeah whatever drives retrofit or yeah you could also compare to retrofit just a different kotlin implemented HTTP client we can use to connect to a websocket route with retrofit we can't do that which is why we use Cato here and for that we also have the core dependency CIO which is needed for the for websockets web sockets which is also needed for web sockets we have logging and kotlin civilization here to also be able to parse staff to Json let's click synchronize now and in our Android manifest the only thing I added is internet permission because I like to forget that so if you have that I would say we go back to our backend and actually just copy over to classes that we also need client-side and that is on the one hand game State and make turn because these classes are of course used to to communicate and therefore we need this in both these projects so in our root package let's create a package for data and here I will be only using presentation and data packages so we won't be using domain or so because I feel like that's very unnecessary for a rather small project and in data I will paste these two classes so on the one hand we have game State here which looks exactly the same as server side and if we take a look here to make churn then this will also just look like server side I'm not sure why the syntax highlighting doesn't work but it seems like it's still analyzing so I have no idea what currently happens and also if I type game State here it doesn't recognize that so it seems like I successfully broke my Android Studio so I will need to invalidate the caches and restart this so I'll see you right after that so I am back and now it seems to work so my game set is recognized I need to just update my packages here also probably in make turn let's do this I'll enter and update that and then I could access it here I assume yes so what do we now want to start with your client side client side I think we actually need to write less code since all we no actually not less code the drawing code is quite quite large but it's very a lot of repetitive code that we will write there to draw our draw or check to show canvas now we first of all want to start with the fun part and that is how we interact with our backend so in our data package I want to create an interface a little abstraction for a real-time messaging client that's at least how I call this abstraction for our websocket client let me attach to git and here we will have three functions that a websocket client actually needs on the one hand we will have a function get game State stream this function will be shown a flow of type a game state so whenever there is a new game State coming in from the server this flow will essentially trigger and then we can also update our local state then we want to have a suspend function send action and the action in this case is just make turn I talked about these different actions you could potentially have server side but in this case it's just make turn and yeah we can send this from our client to the server as well of course and finally we will have a suspend function to close the connection so in case the user yeah just leaves the app we want to call this then in data we want to create an implementation of this real-time messaging client which will be called ktor real-time messaging client so the K toward specific implementation of this we will implement this interface and we also want to make sure that we have our client here in the Constructor which will be an HTTP client you can see this comes from K2 which will then provide from the outside then in here we need a reference for the current connection for the current session I also talked about that server side it's a very similar client side here so that's just a websocket session nullable and not by default since we didn't connect yet but then we can press Ctrl I and Implement all these functions and here for our get game State stream function which is probably the most important function since here we will be getting all of our game State updates here we want to return a flow so in this flow Builder we can now kind of take our HTTP client connect to our websocket route and then listen to all these events that are coming in parse them to game State objects since they have obviously coming in as Json and then yeah just trigger this flow that we return here so later on in our review model we can then update our local state so our composing y will be redrawn so in here we want to first of all set our session equal to our client dot websocket section which will return a default client websocket server session a websocket session without server and in here in this Builder we can set the URL for this so we now need to specify the URL where we want to connect to for this websocket connection and that is simply WS so it's just a protocol just like HTTP just that this stands for websocket and the IP address in my case is still a local one of course since we haven't deployed yet which is this one in case you don't know how to get this you would need to go to your Wi-Fi settings and then it will actually show your IP address or if you're using Windows you need to go to your CMD type in ipconfig and then it will also print your local IP address which you can then connect to so you could play the game with yeah anyone who's connected to your Wi-Fi we also want to connect to the play route which we configured on our server then after that is done you can see that's suspending function which will suspend our flow after that is done we can use a recession to listen to events to incoming messages so we will be getting all these game states by saying session a DOT actually let's assume this is not null here because at no point this will be now we can say session dot incoming which will be the received Channel we we get here with all incoming game State messages and we say receive as flow as it actually consume as flow consume as flow so we that way we get a flow of type frame and as you know a frame can either be like a file or it could be some text in our case it'll just be text and now we want to transform these frames because obviously a frame is not what we want to emit here in our flow no we want to emit game States so we first of all want to filter is instance we only want to receive text frames we don't care about any other types of frames so we use this function and now we can say map not null Json dot decode from string use this string function here and well which string do we want to decode we want to decode it which is our text frame without retext and I think we need to also specify yes we also need to specify the return value of that Json function since otherwise we will get any object so we need to say those are all game State objects and that way we have a flow as you can see of type game states which will trigger every time there is a new message coming in from the server it will automatically map that to an actual game set object by using our Json decode from string function and now we want to take all these game States and send these into this outer flow so we can do this with emit all and we simply pass in our game States and now we have a yeah a function that goes over 10 lines and all it does is already it it listens to websocket messages to real-time messages it contains all the mapping code to map these two actual games and objects client side and that will work so it's it's really not a lot of code here with Cato I really like the library uh let's go on with send action that's even less code so in here we just want to get a reference to our session and get a reference to the outgoing channel so that's a send Channel and here we can call the send function to send a frame this time it will be a text frame again and the text that we want to send in here or we actually could also just send the string I think no here that doesn't work frame.txt and the string we want to send is make turn since we only have that action then hashtag followed by our actual action pass to Json so we want to say Json decode no actually encode to string um and code to string this one here and pass in our action just like that and then that is already the function to send something to our server and finally the function to close our connection will simply be session Dot close and then we say session is equal to null again and boom we have a working websocket client for our client to yeah connect to our server to communicate with our server in real time and now we only need to use it to update our local state and draw the corresponding game UI but you see since we've obviously implemented all the game logic on our server the client doesn't need to implement that code and that's of course on the one hand a lot better because the server can do the validation which is much more secure but on the other hand let's say you would have an iOS and an Android app which both work with your single server then you would only need to implement that logic at a single place on your server and not in both your app's code bases I hope that makes sense let's go to our root package and create a presentation package and in there we want to create our tic tac toe view model like this that will just be a normal Android view model we annotate it with Hillview model since we want to be able to inject dependencies in here we use the inject Constructor Constructor like this import inject and which dependency do we need here well we need a private valve client which is a real time messaging client so in here in the view model we can now use this client to listen to game State updates and also send actions to our server so what kind of states do we actually need here in addition to our game State we also need some client-specific states that the server doesn't care about and as on the one hand oops on the one hand that is is connecting so we can show a progress bar that's immutable State flow false initially and we also have a public version of that like this and we want to have a private Val connection error or rather let's call it show connection error so if there isn't is a connection error we will we will show it also mutable State flow of false show connection error is show connection error.s stateflow to just expose the immutable version of that state flow cool how do we now get in our game state state flow here we will do this by saying valve state is equal to client a DOT get game stage stream and here we can say that on start so as soon as we start collecting the emissions from that flow um come on we will we will actually call this onstart function and in here we simply want to say is connecting is not true so now we start to connect and then we say on each disconnecting value is false so on each will just be called on each emission and yeah as soon as there is an emission we obviously want to say okay we start connecting so then we just know that this this function actually finished which is suspending so as soon as we reach this code then the yeah on each will be triggered and we we know we successfully connected then we want to say catch because our cage your client could potentially throw an exception for example if we can connect if the server is not reachable we can catch these exceptions with a catch block by um yeah catching our throwable here and we can then say our show connection error that value is our variable he is a connect exception so if our authorable is a connect exception we know we couldn't connect and then we want to show the error else we don't we can also put this on a single line I think like this and last but not least we want to say that state in view model scope view model scope sharing started while subscribe and I will explain this of course after I tap this line and game State just a default state so what happens here is we effectively with the sketch function all these other transformation functions we just get a normal flow reference however in view models we usually want to use State flows because we want to reflect UI state we want to Cache the latest value show that also on screen rotations and we do this with state in so with state in we convert the normal flow into a state flow we use the view model scope to launch that flow we use sharing started while subscribed so all of this stuff will only be executed if there is actually an active subscriber so if it is actually if the UI for this screen is actually visible and listens to to this state here and this is just the default state for our game and now the only real function we need on this remodel is if the user makes a turn because then we need to send that to our server and all the other things are already reflected by the state which we get from our server so we can say function finish turn and we can make the turn at a specific X and Y position of our tic-tac-toe canvas and let's also due with this validation we did server side also client-side so if state of value fields at our y and X index is not null so that means that field is not free or if State value winning player is not null so if there is already a player who won then we simply want to return so we don't even need to send any kind of data here else we want to send that action to our server so we save your model scope that launch and we use our client to send an action and the action is make turn at a specific position which is X and Y here and last but not least for this view model down here in on cleared so when the user leaves the screen when the screen is popped from the back stack then the model is cleared and then we just want to make sure that we close our connection so we just say client Dot close you can see how light overview model now is with this viewmodel we make sure that we map our whole game State and we make sure that we show a connection errors that we show a connection progress bar now all that's really missing is using or yeah creating our UI drawing all these things based on this states that we have here so in presentation we want to create a new package well let's not create a package let's just create a composable here called Tic Tac Toe field so how will we create this rather complex composable because it's quite custom of course to draw this field for our tic-tac-toe game we will effectively use a canvas for this so with the canvas we can we have full Freedom when it comes to drawing shapes and lines on our screen and that is exactly what we will use so there will be a lot of math involved now we will basically specify a relative coordinates from where we actually want to draw a line to where where we want to draw a circle maybe if we click on a field and things like that let's go through this step by step and then it will be all clear first of all composable tic-tac-toe field and that will need a bunch of parameters on the one hand it will need the game state that we want to show here in our field then we want to be able to pass a modifier from compose like this we're going to be able to choose a color for player X um which is a color equal to color let's say green by default we have a player come on player o color which is color.red let's say and we also want to have a function on tab in field which will be triggered if yeah if the user TAPS in a certain field it will give us the X and the Y position of that field and this won't be the actual pixel coordinates no this will actually be like 0 0 for the field in the top left it will be 0 1 for the field in in the middle like the Middle Field of the first row and so on and then in here we are going to create a canvas here for this canvas we're going to pass or modifier and on a draw is very important since that's where we get ref get reference to this draw scope um we can actually also get rid of this and just specify it like this so we have one last indentation so with this draw scope we get a and we get access to a bunch of functions like draw overall draw line um draw Arc we get a size of the canvas so all very relevant information that we need to be able to draw something and to draw shapes on our screen so how will we do this and how will we start first of all we would actually like to start just drawing the the basic um yeah the basic game field like with our horizontal and vertical lines if we open our emulator um and open the tic-tac-toe field here you can see we first of all want to draw these lines and these lines before we think about drawing actually the X and O's here at the corresponding positions so how will we draw these lines um I will actually make a little helper function here for a lot of these things first of all that will be a private function that extends draw scope and it is called a draw field and in here we will now draw these four lines so the first one will be the first vertical line and we can say draw line the color for this will be color.black then we need to specify a start off set so an offset in terms of canvas is always just a position on the screen and here we also work with pixel values not with DP values so we say offset at a certain X and Y position so where do we want to start we want to start right here and since the size of our canvas can differ we need to kind of calculate the relative size it's basically at 33 percent based on the width of our canvas where we want to start this in terms of X position and the Y position would simply be zero so what we can say is we can say x is equal to size that width so size refers to the size of our canvas that's to the width of our canvas whatever that is and we multiply that with one divided by three F so just yeah multiply that with one third to get the X position of our um of our start offset for that line the Y Position will simply be zero and now we also need to specify the end position which is another offset at this time it's actually size that width multiplied with two divided by 3F so we actually get to this x position here which is two-thirds of our whole width and then our y will be simply zero again then down here we can say we actually have a stroke width somehow thick our line actually is let's say that is three a DOT DP dot two pixels so we just say this line is 3dp wide and we can use this two pixels function here to convert that to pixels since as I said Canvas Works with pixels and we also want to set the cap to stroke cap.round and that will just make sure that these ends here are actually rounded and not straight and I actually made a mistake here this y offset actually shouldn't be zero it should be a size dot height because we obviously want to draw from y being equal to zero to the whole height of our canvas so this would be y zero and this would be y yeah equal to size dot height and now we successfully drew our first vertical line and we can also actually set up a little preview here to so you actually see what happens take tag show field preview make that a preview and now with electric eel we finally got that real-time preview and then here we can say tic-tac-toe field we assign the default game state we could also change that a bit and not care about these values so if we say the player turns X that's fine we can say the field is let's say an array of array of let's say x no no duplicate that twice say now just that we have some kind of sample value here and we see that we actually can draw something like this and let's also set this one to now like the whole last row then the rest should actually be fine so if we now open this split tab build and refresh we should be see we should be able to see nothing because we didn't call this function yet but in the canvas if we do this like draw field and in the preview composable we also say that we want to show a background show background true then we hopefully get to see our very first line that we are drawing on the screen um if we yeah we probably also need to assign a size for canvas so we say modifier is modifier.s size 300 DP for example and it will reload and oh there's our line that does not look as I want because yes I confused this already with the second horizontal line so this shouldn't be two-thirds it should be one-third and then you will see that the line will actually be a straight line just going down and now let's get to the next line by copying this pasting it here for the second horiz second vertical line and here we just need to change this to two-thirds two-thirds and the rest should be the same as you can see now we have our second line we can then paste it for our first horizontal line this time these things are actually swapped so here this would be the Y value and actually size Dot Heights and X would be zero f and here we swap this so the size that width times 1 3 would be size dot height times one third and the x value will be sized at width it should be let's see if that looks good yes that looks very good we can then copy this again paste it below this will be the second horizontal line and this time we again started X being equal to zero and we just change this to two thirds here and then we already have our tic-tac-toe field as you can see it will does it update I'm not sure if it already updated or if I'm missing something let's rebuild this year maybe there is an issue with the preview which I wouldn't be surprised about yes okay it actually looks perfectly fine now so now we have our helper function to create our very very simple field and now the next thing is that we also create these functions to draw an X and an O so let's do this right here private function drawscope dot draw X and here we also want to be able to specify a color we want to be able to specify the center of this of this ax here so basically yeah just the center offset and we want to be able to specify the size of that X which will be a normal size and we set this equal to a size of let's say 50 DP to pixels and 50 DP to pixels so that's just the default size we could also make this relative based on the on the size of each cell but I like to keep it at 50 pixels here for Simplicity of 50 DP actually so how will this work how can we now make sure that we draw an X at the corresponding position that we passed here at the center so we want to say draw line essentially an X is just two lines drawn in the right way the color will be our color that's still very simple but now it gets a little bit more difficult we want to specify the start off set the x value of the startoff set will simply be our center.x and here we now want to subtract half of our sizes width um so if if here's our center for the X then our first line should actually start right here so we want to subtract this amount of of width and that's exactly half of a width so we can say minus size dot width come on size or width divided by 2f and we do the same for y so Center dot y minus size dot height this time divided by 2f and then we can also specify the end offset this time it's Center dot X plus half of our sizes width and Y will be centered at y plus size that height divided by two and then again we can set our stroke width to 3dp pixels and we can set the cap to stroke cap round and that will be like the first kind of line for our X we can copy this paste it below for the second one the first X Position will be the same as the X position for our first line about the Y Position will differ here we will actually add our at half of our height and the end position for X will now be um yeah it will also be the same but here we will subtract our y position or we will subtract half our height to get to the final y position so if we now just go ahead uh go up here and say we want to draw an X the color will be player X color and the center will be offset let's say x is size that width times 1 divided by 6f so 1 6 since that is the the center of one field and Y would also be sized at height times 1 divided by 6f and then we should hopefully see something and yes that's wrong so it's again either an issue in the preview or in my function let me rebuild this very quickly and if that seems to be an issue in the function let's scroll down um oh no now it's actually correct you can see the ax is exactly where we want it if we say we have um 3 6 then the cross should actually be right in the middle I think um so let's build and refresh that that's exactly the center of our screen um yes so that looks quite good let's now also do this for our o so private function draw scope draw o has a color actually just the same type of values here and then this time we don't need two draw calls instead we only want to draw a circle color is color Center is Center we can just pass this here the radius is our size that width divided by 2. and the style is important here we want to set stroke so we just don't draw a filled Circle instead just a stroke Circle um the width again is 3dp to pixels and the rest can actually stay the same so if we swap this draw X call with a draw o call then we should be able to see yes a green circle here so now we can actually draw all shapes that we really need here now all that's missing is that we are actually able to tap on the field call this on tap and field function and also be able to draw the um draw the yeah all these shapes at the same at the specific position where they should be based on our field that's contained in our state so what we'll do is instead of this we will say state dot field for each indexed here we will get a reference to each what is that each y value the players are not needed um and for each field so this will now be done for each each array in vertical Direction so in y direction for each of these we want to also have another loop so we just Loop over each field I want to say State fields at y that for each indexed this time we have X and our player at that position so now we execute this for every single field in our state and we want to decide whether we want to draw something at that position or not and first of all we can get the offset where we would want to draw something if we want to draw something there which is equal to first of all x times size dot width times 1 divided by 3 and no worries I will explain this right afterwards and we add size that width divided by 6 on top of that so that's probably the most complex part here we basically try to find the field based on this x value where we want to draw this so if x is let's say 0 then we are talking about this about one of these first Fields here we now want to get to this Center position to to this x position here so we multiply this x which is zero here with size and width so this whole thing will then stay zero and we add sized at width divided by 6 here because we care about the center of each field and since since we have three fields and all these three Fields have a center which yeah we would kind of divide into three into two parts for each field we need to add like half of each Field's width on top of that which is just yeah one-sixth of our width very complex I I hope that gets clear it's not easy to explain but we basically want to do the same for our height so size that height times one third plus size and height divided by 6 f and you will see that this will work just like magic so we can now check if the player is equal to X we want to draw an X at that position with player X color else we want to uh we're not at else yet the center will be our offset the size will remain and you can already see we see an X right there in our preview and else if the player is an o we want to say draw o at like with a color player o color and Center will be offset and now we should hopefully see our our whole tic-tac-toe field State yes so if we scroll down um to a preview then here is our game State and that is exactly what is now reflected here in our field so if we now Swap this for example with an X we should be able to see an X here after this uh rebuild maybe we need to reboot manually yes there we go so now we effectively have the option to assign either an x on o at any of these nine positions here so the last part is if a player clicks somewhere in our field to find out which field they clicked in and then call our callback function here with that corresponding X and Y value and if a player Clicks in in any field here then we will actually get the result of that by using pointer input passing through here because we never want to re-trigger this input and then here we can say detect tab gestures we will get an offset where the player basically clicked and this offset will now contain pixel coordinates however we we don't want to pass pixel coordinates here to our callback instead we want to pass for example 0 0 if the player Clicks in the top left field we want to pass 0 1 if the player Clicks in this field so we kind of need to map this click offset to these coordinates um to this yeah for our two-dimensional array and we do this by saying Val X is equal to 3 times actually it's dot X so the X position of our click offset to integer and we divide this by our size that width so since we have an integer division here it will always round down which is exactly what we want here so let's say the click offset would be somewhere here at 20 percent then we would multiply that with 3 because we have three fields in our novel width and then just divide this by our whole sizes width to get to either 0 1 or 2 depending on where the player clicked and then we can do the same for y replace this with Y and this width height and then we can say we call ontap and field with X and Y so I hope this all this math stuff got somehow clear if not really don't worry this is quite complex and I also didn't just sit there and write this down as if this was like a super simple I also needed to experiment a bit with that and play around what does which and yeah but I prepared this chord obviously and I hope I could explain it somehow understandable so now we want to go to main activity now we finished our tic-tac-toe field and now we just want to call this and make sure that we observe the state from a review model and assign it to our tic-tac-toe field to reflect the game State and I just noticed that we do need the dependency that I removed the Hilton one do I still have that here um no so I need to paste this again since we obviously use the penalty injection with hild in view models so we need this navigation compose dependency so we just synchronize this add this dependency back and our main activity we can say we can first of all annotate this with Android entry point and here we get a reference to our viewmodel which is equal to Hillview model and the type of view model we have is a tic-tac-toby model of course and then in here we can listen to all of our different states we get from The View model on the one hand of course most importantly our game state which is valid state by viewmodel Dot State collect as state so we get it as a compose State we can Alt Enter to import this then get the is connecting state by viewmodel is connecting collect as state and we can get the show connection error state by viewmodel show connection error collect as state just like that so first of all if we should show that connection error then we want to show a box with that specific error so modifier filmic size we make sure that the content alignment is Center and in here we're just going to show little text where the text is couldn't actually the server or so and we make sure that the style is equal to material theme that's what is a typography error do we have an error text here I am not sure then let's at least just set the color I think that is what I want to do so color is material theme colors all right yes and we'll also return here since then we don't want to compose anything else um other than that if we don't want to show a connection error so if there is no error we want to have a box in which we will put in our different composables on the one hand we want to set a modifier here of the modifier filmic size we want to make sure that the content alignment is Center and in here we will have a column so this column will basically just contain this text here it will contain of course our field and in the bottom we will also have a text when a specific player one so what we're going to do is we're going to say modifier is modifier dot heading if you want to add some top heading of 32dp so the text is offset a little bit and we want to say we align this column in the top Center and inside this column we will check if state connected players contains our X or rather if this does not contain that player then we want to show a text that we're still waiting for that player so text will be waiting for player X font size will be 32 SP import SP and else if our state connected players does not contain or player oh then we will show the same text just with player o so depending on who is missing we will just show corresponding text and then below this row so this row is aligned in the top Center so just for these texts here then in the center of our box will be our tic-tac-toe field which will now come below this row of this column of course and that will be right here tick tack to field the state will be our state and when we tap in a field we want to save viewmodel finish turn just like this and we also want to pass a modifier so we can say modifier is modifier fill Max width we can say that we want the aspect ratio to be one F so we just have a square and it just fills yeah a square shape size kind of and we want to give it a bit of padding of 16 DP from all directions one thing before this actually we also want to pass some other values Maybe let's not change that one thing before this tic-tac-toe field is we also want to check if like who is next um so once two players connected then there will be this next is O text and we want to find out who is that or rather show who is currently at turn so if State let's put that on two lines actually if State a dart connected players that size is 2 so if there are two connected players and the winning player is now so if there is no winning player yet and the board is not full then we want to show this text who is next so we can say text is if State DOT player churn is X then we say player or just X is next and else we can say o is next and the font size is also 32 SP want to make sure that we align this text properly and the top Center so also just right here and then what else is missing actually only the message when someone won or when there is a draw and that we also show this as a text and we also want to show progress bar when we connect so below this we're going to have another if condition based on our state if state DOT is both full or we have a winning player then we want to show a text and that text will be when stated winning player if that's X we will say um X or player X1 if that's o we say player o one and else there is no winner so we can say it's a draw we can set the font size to 32 SP again I'm going to attach a modifier of um let's say padding we assign some bottom padding to just offset this a little bit we can say we want to align this and the top actually in the bottom center this time and that's pretty much it so the last thing we want to consider is showing our progress bar which is also very simple so if our state is connecting oh actually if we just aren't connecting that's not containing our state we want to add a box that just fills our whole screen to modifier dot filmex size go to background of white and Center its content and then in here we're going to have a circular progress indicator now that's everything for our compose code this should be our whole UI the last really last thing we need to do for our app is we need to set up dagger Hilt so in our root package let's have a DI module a DI package and in that set up our app module where we will provide our dependencies such as our client and our I think that's it just our client we annotate this with module we say we install this in the Singleton component so we just make sure all these dependencies in this module live as long as our application does and in here we're going to say function provides real-time messaging client which will need an HTTP client return on real-time messaging client and it will just return a cater real-time messaging client so that's the specific implementation which choose here we need to make sure to annotate this with provides and with Singleton to just yeah allow a single instance of this throughout the whole applications Lifetime and before this we need to provide our K2 client so we again oops have a Singleton Singleton [Music] provides and we provide our HTTP client here like this and this will be very simple we can just say which one HTTP client this green thing here the engine is CIO that is what we included as a dependency since that supports websockets and in here we can just configure that the same way we did that server side so we can install specific features and we want to install the logging feature and we want to install the websockets feature like this and that's it for our module we need to also create an application class to set up dagger Hilt so no root package let's make an tick check Joe app make sure that inherits from application and we annotate this with healed Android app so that is nothing new to those who watched my Hilt Basics course if not of course I went through this a little bit faster but I really recommend the my Basics course about dagger Hilt I'm where I talk about in detail about everything in hild so in case you're curious how this works then simply watch this video just fill up lagner dagger Hill course or so in YouTube and you will find it in our manifest file we need to add our application's name tic-tac-toe app and I'd also like to add uses clear text traffic and set that to True since we don't use any um encryption here so we don't have an HTTP an SL certificate for our server you could of course set that up with hostinger that is absolutely not a problem but just to not make this too complex here I will leave it at HTTP and we're also not dealing with super sensitive user data here so since we don't have anything authentication related but of course if that would be a real app I would always make sure to have an SSL certificate so you actually don't need this option to allow like ws and HTTP traffic so I hope that is everything we need to implement here and I will say we just try this out and I will run this on both my devices on my pixel and on my emulator that is currently running here with my other app so we can just um yeah I'll just delete this while Gradle is building oops because sometimes I have these memory issues with my emulator if if I have too many apps installed yeah I just have one but let's install this here as well and right now my pixel says couldn't connect to the server what's the issue with that well if we take a look in IntelliJ we see that our server is not running so we already see that our error message is working perfectly fine because we could not connect to our server let's make sure we actually run this [Music] um and obviously we also I think we're missing the port so right now our server is running it is running on Port 8081. um yeah you of course need to remember your Port if you if you change that or not and then in our cater real-time messaging client here we want to also append the port so we're going to say 8081 and then relaunch this on both of my devices and you will see X is next that looks quite good um and on my emulator we also see the same I don't know who's X but if we click in a field [Music] um then that seems to work so we can play Let's click o and then we have X again um like this go here go here and let's say uh player o is now winning click in here actually here and on the sales player o1 and that seems to work just fine um no X is next we can play the game again let's see what happens if x wins right here then it says player X1 that looks very good and let's also try it out what happens if we have a draw um let's create that here um I never know where to click like this then here x oh X um oh and x and then it says it's a draw so everything seems to work perfectly fine um we can play as many rounds as we want obviously only two players can use this app which is like part of your homework if they click extend this so you can so that can players can actually create rooms where they can play so that's yeah your app can be used by more than two people but that is definitely a start and you can yeah that's a great homework to actually extend this so you can play this with more people can play this than just two people but we're not done yet so our game is working obviously but we still want to deploy our back end so right now we can only play this via localhost with people who are in our Wi-Fi but usually you want to play with your friends who are somewhere else and not at your home so how do we do this now that we that we get a server on hostinger and deploy our backend there so that we can play this game with anyone on this planet so what do we all need to do to finally be able to deploy or back back into a hosting VPS instance the first thing is we obviously need that instance so we need to go to hostinger now and right here as I already showed you a little bit at the start of this video you make sure to go to VPS VPS hosting and then you will get to this page and I personally I choose this vps2 option which is perfectly fine to start and also for your first few users if you really just care about deploying this once and testing it the UPS one option is fine and of course if you have more users then you need more more storage more RAM and all that stuff but you would then simply go to select here for vps2 and then you will get to this page and of course don't forget to apply my discount code here click on have a coupon code again and enter Philip 7. and you will get an additional discount here depending on which plan you actually Choose You could also just do this for one month to try it out but of course it's um you will save more money if you do it for a longer period once you did that you will get to this hostinger web handle and here you will have this option for this VPN server plan too where we can set this instance up that is what we want to do now we click on setup and click Start now and now we need to select the location for this obviously this should be close to where your users are for me that will be in Netherlands but wherever you live you should use whatever is closest to your place or to your users location click continue here we now need to choose an operating system for our VPS instance so in the end that's really just a computer that we can access remotely later on using SSH so that's just the command line tool to send commands to a remote machine such as this VPS instance here and we need to yeah now install an operating system for this we have the option to have an operating system with control panel plane operating system or a game server game server is obviously not what we want here now we want this control panel to just have some additional options we don't want Cent OS which is all Linux based here instead I no I don't want to choose that we want to click I want to choose a different control panel we want to choose an Ubuntu instance um that is what I usually use for my VPS servers so here we have a bunch of Ubuntu versions I want this Ubuntu 88.04 with a weapon so we select that click continue and now we need to set a VPS hostname and a password which we then need to connect to that VPS server so for example here I will choose Philip lagner dot Tic Tac Joe but choose whatever you want um this is not too important here what the name of that server is we need to assign a password which I will type here then we want to add an SSH key so what is that an SSH key is basically something we push to our server once and then we can we also have a private key of that which is local on our machine and with this private key we can easily connect to our server without needing to enter our password and this will be important because later on in IntelliJ we will set up Gradle tasks that will allow us to automatically deploy our backend to our VPS instance so we don't always need to kind of manually send our jar file that we will generate to our server relaunch the server there no we will set up a script that will automate all this and to be able or to to allow Gradle to automatically connect to our instance it's very helpful to have such a key and the way we will get this key is first of all we want to specify the name of that which is default we want to launch a terminal tab so uh terminal terminal and in Windows you would use CMD or rather I would recommend to use git bash on Windows because that's more like a Linux terminal I'm not sure if the command we use here is available for the normal Windows CMD or Powershell but with gitmash it's definitely available so then this terminal tap will open you can make this a bit bigger and we want to make sure that we are in our actual users folder then in this users folder this will also be the same for Windows we want to CD into dot SSH and this is a folder which which contains all these key pairs these SSH key pairs that I talked about so if we click LS here now then we see a bunch of keys for different servers that I accessed in the past and for everything that now happens inside of our terminal I actually prepared a little helper file a little helper gist which is not for this video but it was for another video where I created the K2 server in the past so I can just drag this over here or we can go back in our terminal and we can just follow these steps to finally be able to deploy our back end and we will it will differ a little bit and then what is mentioned here because um back then I I think I used windows and some things were done differently but let's just do this together and you will see what we what we actually need to do and what what each step does so here we first of all um so we don't want to download git bash since I am on Mac here we want to generate an SSH key pair with this key n command so we say SSH Dash Keygen m p e m which is the the type of key and the algorithm we want to use for this key pair will be RSA so you don't really need to understand the details here it's just important that you can create this keep here we then click enter we need to specify a file name now in which we want to save this key and let's just choose Tic Tac no Tic Tac show press enter we could also protect this key with an additional pass race we don't want this because then we would need to put this passphrase in our Gradle file or we would need to kind of read it from somewhere else let's just ignore this um the the key already serves kind of as a password and then yeah after that it will be done we can clear this with Ctrl L and we can list our files here so now we should be able to see okay I misspelled this but whatever um we can see our tic-tac-toe the one without an extension is the private key that's really private you shouldn't send this to anyone with this private key anyone can access your VPS instance this key with the pub extension is the public key which is really public you can see this everyone can see this um nobody can really do anything anything with that that's the key the server will now get so the server host is public key and whenever a client tries to connect with a key the server will actually only allow um yeah private keys that work so in our case since we have a working private key we can then easily just access our server with that but if a client does not have a private key that fits to this public key then it won't work so what we can do now is we can say cat and we print out our public key this is the key that we now want to copy I'm going to copy it until this last equal sign so without this pattern name here copy this and actually go back to our hostinger dashboard and simply paste this here so that way we just set it up to work with our private key we then click save and continue click finish setup and then we should see this progress bar which might take a few minutes and after that is done I will see you back so there we go that was successful let's click on manage server and here we get all the information that we need to know about our server this is our dashboard and whatever you need to for example relaunch a server change anything like the password for example this is the place where you need to go however what's interesting to us now is this IP address that we were assigned this is the public IP address that we will now use for our app to connect to the server but before that we of course kind of need to take our cater back and package it into some kind of executable format which for Java for Java Frameworks or Java languages is a jar file so we need to take this Java push it to our Linux machine to the server and run it there and then it will be accessible for our app as well so what we want to do is we want to take this IP address copy this go back to our terminal and here we now want to connect to our server using SSH as I said so SSH is just a tool to run shell commands directly on our remote server because somehow we need to send commands to it somewhere we need to install stuff on our server since we don't have a visual interface for that so what we'll do is we'll say SSH and we want to specify a key which we will use to connect to this that is the private key that we now generated and we can specify that with Dash I the name for that is this one here which I misspelled and then we refer to root that is the user as which we want to connect and since we have root privileges we want to use these and then we say at now we specify the IP address which we can paste and that should be everything so if we now press enter then we can type yes press enter again and now we can see we are on our server so now whatever we we write here in our terminal will not be executed on our local machine it will be executed on our server so we can clear this again with Ctrl L and the first thing we want to do is we want to write apt update so that will simply update all the dependencies for specific programs so we can just find all yeah all these programs that we need for example we want to install the jdk so the Java development kit that we can actually run our K2 server later on here on this Linux machine when that is done we can clear this again and then we want to install Java we can do this with apt and get install default jdk press enter and that will take a little moment we need to type a y for yes press enter and then it will simply install a Java here on our remote machine when that is done we want to clear this again and I want to move my other browser window here into this window again and check what is next so the next thing is we need to open this sshd config file because we need to add this line into this file which I mentioned under 0.9 and again you can find this file also down in this video's description because later on the tool we will use in our Gradle script to also communicate with our server via SSH and SCP SCP is pretty much SSH but just that we are communicating with files so we can send file to our server and download files and with SSH it's more about sending commands but the tool we will use for that in Gradle actually needs this line because it uses some kind of key exchange algorithms which aren't specified by default and to do that to also support these we need to add this one so we want to say Nano slash Etc SSH slash sshd underscore config and here in this file I want to scroll completely to the bottom and simply add this line here so we copy this paste it here and then with Ctrl s we can save this file and Ctrl X we can get out of this file then we need to write system CTL restart sshd so we just restart that service which is responsible for loading that config and then we need to create our own service so with this systemctl command we can basically create a service that runs a specific file in our case that will be the jar file forward server and it will make sure that this really always runs because our server should of course always be reachable also if our VPS server will be rebooted then with the systemctl service we make sure that also our K2 server will automatically be rebooted or just yeah put into the auto start kind of so we want to say Nano slash Etc system d slash system and in this folder we want to create our own service and for that we need a config file this config font will in this case be called Tic Tac cho.service and this is of course now empty in this file we want to paste this config and I will explain the most important points of that I already explained all that in the previous video where I um talked about where we built an authentication system with JWT but let's go through this again so here we paste this config we can rename this auth service to let's say tick tack to service after Network Target means this will only run after our server is actually connected to the network here we specify some kind of um like time constants I'm actually not sure what this does it's a simple service we always want to restart this in the user's root that's important this environment file is not needed this is only needed if we would have environment variables in our server in our Kato environment that we would need to access we don't have that here and the most important line is this access start this will be the command that will be run together with our service so here we really want to launch our jar file our K2 servers Java file which we of course now need to specify here so we refer to the Java command here we say we want to launch a jar file and the jar files location will be at slash root code slash not JWT auth in this case we will say tic tac toe and the name of the job file will also be called Tic Tac Toe like that so if we now click Ctrl s then we save this and Ctrl X to get out of this we can now try to run this service by saying system CTL start and the name of our service is tic tac toe and if we don't get any errors that looks good of course nothing will happen right now because we haven't pushed our jar file to this VPS instance yet but before we do this a few more things that we need to configure here we want to say systemctl enable Tic Tac Toe this will effectively set a Sim link so that will make sure that our server will also launch together with our with our VPS instance so if we relaunch our VPS server then we've also directly launched our tic-tac-toe server together with that and now the last few things we need to do as you can see here make sure your ports are open so we need to kind of open our ports that means that we make them accessible for the public at least the port where our application is running on and we need to do we need to do this with this iptables command so we can copy this paste it here which will automatically execute that and then we want to also execute these two lines where we will basically say hey um Port 80 should be open and port 8080 should be open which is the port we will then run or server on so let's do this and by the way this first line um set up a pre-routing which means that we forward the traffic from Port 80 to 8080 so we don't always need to type the port 8080 when we access our server because that's the default HTTP port so we can now paste the second line press enter and we can copy and paste the third line and press enter and then since these IP tables commands are just temporary so as soon as we relaunch our VPS server this will actually be reset we want to save these and for that we need to install IP tables persistent so apt get install IP tables persistent which kind of yeah just saves these and reloads these when we reboot our server so we press yes then we will be getting a little window here we also want to press yes that we want to save our ipv4 rules we want to save our IPv6 rules and then we are good and yeah we just made sure that we always have these rules that we just set up and the last Point here is not relevant for us since we don't need to set any secrets cool so if we now clear this and actually go to our root directory this direct this is the directory where we actually want to put our jar file later on into the Tic Tac Toe folder right now if we type LS to list all files uh there are no files so what we're going to do is want to create a directory with mkdir and that will be called Tic Tac Toe so we just want to make sure there is this directory so we can then easily push stuff into this from our Gradle script so the last part will really only be in IntelliJ here we will be setting up our Gradle to be able to just run a single command in our command line to deploy our backend so it will be packaged into a jar file the jar file will be pushed to our um to our VPS server to our Linux machine and then we will actually relaunch our systemctl service and that we just set up which will then yeah just make sure that our that our up to date or updated server instance will be used so how will we start and for this part I will also copy and paste a bit of code since for most of that there's no real reason to to write all this off again you will find this down in this description you will find the latest version of this source code we want to go to our build and Gradle kts file and here the first thing I want to do is first of all fix this formatting which is broken for whatever reason we want to add a Gradle plugin which is called shallow and this Shadow plugin which you can find like this will take all of our cater server source code and package it into a single executable Java so that that's the whole purpose of this Gradle task with this jar file we then have something that is executable which we want to push to our remote machine however to be able to push something to our remote machine directly from within Gradle we need a so-called um SSH and task that is just something we can access and we can use in Gradle to access commands like SSH and SCP as I said to send command store server and to send file stores over and we need to create this um definition for this test like this and then we can add the dependency for this which we can paste down here which looks like this so this just adds the the functionality in Gradle to be able to use SSH and sap in our Gradle tests and then the code we will paste down below will be this so here we use this with groovy Builder since we are here in Gradle kotlin DSL and this this and task is only really defined in a The Groovy version of Gradle so we need to use this groovy Builder to kind of convert this and that's why we also have all these quotation marks but this really just defines these two tests so we have SCP to copy a file to our server and SSH to send commands to the server and the last thing that's now missing here is the actual task that will take our jar file or first of all generate the jar file and then push it to our server let's go through this step by step so here we have our task which is called deploy that's how we how we can call this task later on and it depends on clean and Shadow jar clean is basically responsible for just um cleaning up our build directory so it will just do a whole new rebuild after that and Shadow jar well we'll just package our servers code into a jar file and since this deploy task depends on these two tasks it means these two tabs actually need to run before our deploy tasks after that's done we say again with groovy Builder and here we specify a bunch of variables that are relevant for our server so first of all a non-host file which just contains some information about hosts that our machine already knows we just need to pass this here don't worry about that we specify the user which is Root in this case we need root access to our server we need to specify the host which is the IP address which is not the correct one here this is my IP address from the server I prepared this for so we just want to go back to Chrome and actually not this one and copied this IP address from our hostinger web panel again into IntelliJ so we replace this then we specify our key which is our SSH key which we generated this is the private key which now Gradle of course needs to know to be able to successfully connect to our server without needing to enter any kind of credentials like a password and we want you or we need to copy this into a folder of our project just to make sure that we have all the necessary privileges so we create a keys folder and put in the key which is in my case called D tic-tac-toe since I misspelled that um just replace this with whatever name you chose so in our root directory I first of all want to go to git ignore since this key should of course not be contained in Version Control since it's a secret and here we can just add Keys No like this so we just exclude the whole Keys directory which we'll now create in our root package um actually new directory called keys and then I want to open finder where I will navigate to my users directory going to dot SSH um you don't see this here because I don't need to show you that but just go to your users directory and then wherever you put that SSH key take this key and simply copy it in here yes that's a text file and you will now see only a little part of that we can close this and now we have our private key in here in our keys directory which we can then access from our Gradle script cool so the next thing will be our jar file name this will be the file name that is generated from the shadow jar plugin and I assume we just want tic-tac-toe Dash all dot jar that's just the name that will be chosen so it will be generated here in our build directory um yeah we need to run this Shadow jar task to be able to see this but I assume this will be the name um then we say SCP so we run the secure copy I think that what that's what it stands for now we want the copy command to copy a file from our local machine to our remote machine the file we want to copy is located here at build Dash ellipse Dash um our file name the directory you want to push this to is whatever user we chose so root add host so our AP address the same we specified for connecting to this with SSH and here we specify a specific path so slash root slash techno that's the directory we created in our terminal we specify the key file which we set here once we trust this host and we set the known house file next up at this point we know that our Java is on our server so we now execute the SSH command because we want to in this case just rename our Java on the server so we say um okay host is host username is user key file is key file we trust this known host and here this is the important part the command that should be executed is MV which stands for move that's the way we rename a file on Linux so we just take the file we want to move and we move it to the exact same kind of location with the new name so that way we just make sure that all Java files are called the same um since that's also the name we specified in our systemctl service we configured on Linux so we make sure that it will always run the correct instance and then here in the in the remaining two commands we just send the command to stop the current instance of our tic-tac-toe service and then start it again so we just start the new Java and that's really the whole Magic that happens here um so if we now click here to synchronize Gradle and actually go to application config since I want to change this port to a080 that is what we specified in our iptables rules um then we should be able to be uh to deploy this um I wouldn't be surprised if something goes wrong here but we can just try this in our terminal open the tunnel here and say dot slash Gradle W that is our Gradle wrapper which is used to execute Gradle tests and we want to run our deploy task we just created in our Gradle file press enter and it says permission denied for whatever reason I honestly have no idea why that happens because it's all inside of our directory enable Gradle w dot um that bats will work no okay I probably need to relaunch IntelliJ here with admin privileges so I'll see you back after that okay it seems like I just needed to run this line to give privileges to migrated W command it seems like that's necessary because um this Gradle W wrapper file wasn't created on this machine instead it was created by the K2 project generator and yeah then it seems like it doesn't have these privileges by default but now that works so in case that also didn't work for you just run this line we can then clear this and we can now try to deploy our backend with our deploy task so let's hit enter and hope for the best as as long as things are green it's it's a good thing so now things are red let's see what went wrong that is not a surprise no such file exception um Tic Tac Toe Dash all jar does not exist looks like the file was renamed or was named a little bit differently so in build Libs we should be able to see okay it actually prepends our package name so let's also adjust this here in our jar file name and say com PL coding dot Tic Tac Toe Dash all dot jar let's rerun our Gradle task and hope for the best now and now you can see the build was successful that means also server side everything went well otherwise the server would have responded with an error message if we now go back into our Channel um where we are actually on our server so I'm still logged into SSH and here in our root directory we have our tic-tac-toe directory where we can CD into if we now type LS here is our jar file that was actually pushed to the server and this should actually already be running which we can actually check with systemctl status I think and then tic tac toe and yes it seems like this service is active and running so how can we now test it or how can we now connect our app to this specific server here all we really need for that is our IP address let's copy this go to Android studio and we want to replace this part in our Cato real-time messaging client with our actual IP address and we don't need to specify the port here since we use these iptable rules which map the default HTTP port to um to the port our server is running on and if we now relaunch this on both my devices and I need to reconnect so here it already says waiting for player o which is good because if it couldn't connect then it would be saying connection error let's also launch this on my pixel and hope that we can now play yes we actually get the message axis next we can start to play here and that looks quite good um yeah so we can now play our game which looks really good play X1 then after five seconds we should be able to play another round and another round in another round it's a lot of fun let's try what happens if it's a draw right here right there right there here okay now I accidentally won but you can play around with this um I think you believe me that this is working we also already tested this locally um yeah I'm just using this remotely won't change anything in terms of functionality for the game of course so again take it as a homework to extend this to um and just make sure that players can create rooms where yeah everyone can play with a friend and as many players as you want can actually use your app and not only two players that will be quite a challenge but also a lot of fun I think and it will definitely help you to learn these things and internalize these much better than yeah than just watching a course can do and again if you're looking to create your own website I can really recommend hosting we will find all the links down below discount code philip7 I already showed you that quite often in this video but I I really like hosting as Services I'm also using these myself so definitely do check this out thanks for hosting her for sponsoring this video again I will see you back in the next video enjoy your week bye bye
Info
Channel: Philipp Lackner
Views: 23,511
Rating: undefined out of 5
Keywords: philip, philipp, lackener, lackner, game, jetpack compose, canvas, ktor, backend, deploy, free
Id: sKCCwl5lNBk
Channel Id: undefined
Length: 121min 19sec (7279 seconds)
Published: Wed Nov 23 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.