Adding Multiplayer to our Drawing App with WebSockets and NextJS 13!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey do you remember the drawing app that we built literally like two days ago well today shout out to this dude that's exactly what we're gonna do we're gonna create a websockets integration so we can interact with the same canvas on multiple devices and even if you're a beginner don't worry I'm gonna walk you through every steps of through every steps through every step of implementing web sockets this is totally beginner friendly let me show you exactly what we're gonna build and Dive Right In okay so here we are in the previous um thing we built the drawing app that worked just fine in the browser we've built that in the last video let me quickly show you what that looked like so let's start up the server and then go to localhost 3000 and this was the drawing app we've built in the last video so you can draw in any color you can clear the canvas pretty cool functionality and in this video we're gonna make a web sockets integration so right now as you can see when I load up the localhost um again nothing happens because this canvas state is not being propagated to this instance right here but what we're gonna do and I've got the working version over here so let me quickly pause this one and open the um one I've already coded out and so the it's going to be a bit different we're gonna have a client and the server in this one and let's start up the client and then also start up the server so yarn server and as you can see now we have the client and the server running and when we go to localhost 3000 and that might take a second we can draw something like a smiley and then when we go to localhost 3000 in another tab we can see what we have previously drawn and if I open them as side by side you can see whatever we write or Draw on one of them we can also see on the other one and we can clear the canvas that will work for both as well and how cool is that and that's what we're going to work on in this video and if you've never worked with websockets don't worry the integration is not that hard um so for now let's terminate this batch job and also pause the client and looks like the server is terminated as well it has terminated itself and okay I've got that project open here on my on my left and we're gonna work on integrating this in this current project that we've built out in the last video and I think the first thing we're going to start with is installing the packages we need so let's add the socket socket or actually no let's not do that I think the first thing we're gonna do I've got my OBS open here um is wrap this in another folder so let's call it canvas tutorial and put the canvas drawing app in there and then let's name rename this one to client because we're going to have a client and the server and let's also create a folder call it server now let's open that in vs code I think that makes more sense because now we can install packages that are specific to the client and that are specific to the server so let's go into the server directory for a second and let's type npm init Dash y so the dash Y is just going to create the package.json for us without us having to you know verify each step of it so that's way easier and okay now we have the package.json let's install the npm I socket Dot IO and let me also see if we need anything else because okay yes we do so we also want the um Express then we need node nodemon to keep the server running and also t as a note because we're going to be working in typescript on the server which is not going to be a huge deal so even if you've never worked with typescript before do not worry it's gonna be really easy okay and now inside of the server let's create a file call it index.ts you can call it whatever you want you could call it like server.ts I think it's very common and the first things we're going to do is import a bunch of stuff I'm just going to copy this over now um the GitHub repo is going to be in the description so don't worry you don't have to type this out yourself oh and we also want the npm I add types slash node so we get rid of these arrows right here and that should get rid of them let me restart typescript server and I think no they are still there oh I didn't install them as Dev dependencies okay hold up let's um install them as Def dependencies and that should fix the error okay and no wait that still didn't fix the error cannot find name okay you know what let's just declare VAR require any whatever I don't know what is error occurred let's just um get rid of it like that I know that's pretty dirty but whatever um and no let's import the server from oops from soccer.io and I'm gonna turn GitHub GitHub copilot off for a second here you can see that's a pretty heavy dependency but because we're on the server we are not really going to worry about that and then we're gonna create a new server instance now that's going to take the server we created up here the HTTP server and as a second argument we're going to pass an object that has cores which is an object and the origin it's just gonna be a star so you wouldn't really want the star for actual production level projects but for our local one that's totally fine it just means we're allowing anyone access to our server essentially okay and now that we got the server we can say I O DOT on connection so that means when oops connection so that means whenever a websocket or a client that we're gonna Define in a second connects to our websocket server what should happen well the first thing we're going to receive is the socket and let's declare this as a function not as a seven and as you can see the socket type is already here so we don't need to worry about that and now we can do a bunch of stuff so we can say for example um socket dot on and we can say on draw dash line because that's the event we're going to fire from the client in a second we're going to receive some stuff that's gonna be a previous point and I'm going to get to why we are receiving those in a second the current point and the color now how do I know we are receiving these properties well we can go to the um to the page and we can see in here that the jawline takes a previous point a current point a context which we're not going to pass to the server and then we're also going to pass a color in a second so we can actually propagate the color that each client uses as well now we still need the type for this and we're gonna call it draw a line and this draw line type we're going to Define up here which is going to be in you know typescript type the previous point is going to be of type points now that point also doesn't exist so we're going to say type point is equal to an object that has X as a number and Y as a number as well so really simple in the previous Point could also be no so if we didn't paint anything yet then the previous point is null and the current point it's gonna be wherever we start painting so it will always be um you know defined or not null and then the color which we're going to Define in a second is gonna be a string so that's just gonna be a regular hex string now this doesn't work yet the type draw line and that is because we also need to Define this as a function that will um infer the types correctly and now in here we're going to say socket dot broadcast dot emits and we're emitting the um draw dash line event and we are passing some properties as the second parameter which are going to be essentially everything we received up here so the current point the previous point and the color now what exactly is happening here okay look when we are on the client we're gonna fire an event that is called Draw line and whenever we fire this event after establishing a websockets connection to This Server right here so this is the server file and whenever we fire the event from the front end the web sockets are going to pass this event to the server and we are listening to this specific event so if we fire it from the client we're gonna have to say draw a line exactly like here and then the server is going to listen to that event we're gonna pass the data from the client to the server and then whenever the server listens to that event it emits to all other instances that are listening to the websockets connection um these exact data points but not to the one that sent it so imagine we have um you know let me let me draw that out for you give me a second so imagine this we have a let's make a new layer we have a bunch of clients right here so we have a client another client and then another client then we have the server so the client in this instance would be this page right here then the server would be this index file that we're also going to host and whenever one client draws a line that change will get sent to the server the server is then going to receive it right here at the jawline event and it's going to get the data that we pass from the client to the server and then the soccer.broadcast.emit means we are emitting that event to everybody that is not descender so we're sending the draw line to this guy and this guy but we're not sending it back to this guy because this client already knows where the line has been drawn because it was the one that originally drew the line and yeah that's pretty much what we're just doing here so nothing like no rocket science and there are a couple of things on the client that we still need um to do for this to work and I think the first thing that makes sense is to abstract this logic into a helper function so let's cut it out of there let's have a new folder call it utils because I think yeah we don't have one yet let's call this function drawline.ts export that constant and oh wait that is not what we wanted to do let me check what I did in the real one okay we are gonna remove this again export constant draw line which is going to be a function and then in here we're going to Define all that stuff and this function is going to receive a previous point a current point the context of the canvas that we are drawing on and then the color and that is going to be of type jawline crops now if you're working long in typescript nice if you're not working along typescript so in JavaScript you don't need to worry about the types at all I'm just doing this because I enjoy using typescript so this type we're going to type out as something that has the draw inside and if you don't know the draw type is something we defined in the last video we created a file called typings.d.ts and then here we typed out the types we need for that and to merge types and typescript we can say draw and and then we can add the one property we want in here because as you can see right now the color wouldn't be defined we Define these three properties in the type right here and now we are essentially saying and also add the color which is going to be a string and that is essentially all we're doing in the draw line function now the reason we are doing this is so at the client side we can have two different types of draw line the first one is going to be a create line and the second one is going to be a replica headline so that means when we are drawing we are calling the function called create line and you're going to see in a second why we're making a difference out of those two um we need the previous point the current point in the context which are going to be of type draw the one we initialized here and then this function is going to do two things very simple first off um it's going to emit to the server that's the most important thing and to do that let's check our package.json if we already okay we don't have it installed yet so let's go to CD dot dot see the client inside of the client let's install socket dot IO Dash client and that's the packet we're going to use to communicate with our websockets server so nothing special there we are gonna wait oh and I did use yarn for this project now I used npm that's probably not the best practice but whatever um and then we're gonna import something from sock dot IO slash client and what are we going to import that is gonna be the i o okay now that we've got the i o on the front and we can say because socket is going to be equal to IO and then the address we're going to be connecting to so where are um websocket server is going to be hosted which is going to be right here and right now it wouldn't be hosted there so that's why we still need to say um server Dot listen and for some reason oh yeah we declared it as any that's why I don't get oh look now it works okay whatever dude listen wait it's my vs code just really laggy well I'm not getting any code completion right now I'm not too sure why but you probably will when you're coding along and let's just say console.log server listening on Port 3000 and one and that's pretty much all we need to do so we're starting up the server the websocket server and now we should be able to connect from the client side that's um let's just start up the server for a second um let's have one client we can call it um client that's what I like to do when I work with a client and a server we name this to server and then the server that's going to the server and say oh we can't start the server yet because we have to update the package.json so let's say server and in here let's say nodemon index.ts so that means whenever we are executing the script npm run server this Command right here will run and because we have installed TS node it should know what to do and it does in the service listening great okay we can close the server we don't need it right now and now that we have gotten the server running let us work on the client side for one more second so inside the create line we can say actually now let me show you that we can connect to the server so yarn server and then on the client ER oops oops well why is this not working yarn Dev and that's star the server up localhost go to the server and we should see that actually no we're probably not going to see anything because we need to log out connection and now that we did that server is going to restart and we can see connection okay great that is exactly what we want we've connected to the client add to the server from the client I just wanted to show you that it already works and just so you get a better understanding of what we are currently doing okay so that means the server connection is successful now what do we do and I say we are going to work on the create line so whenever we draw a line from the client we're going to meet a websockets event and that event is going to be called Draw line now why is it called Draw line because we are listening to this exact event on the server so this string right here needs to match this string right here that we're emitting from the client and then as the second parameter or argument that we're going to pass whatever we want to receive on the server which is going to be these three properties right here the previous point the current point and the color so previous point current point and then the color that we want to pass and that is all we need to do right here and then we're going to call the draw line we've initialized as or not initialized we've put as the utils we've abstracted into the utils because we're going to call it from two different points and by abstracting it we are not gonna you know just copy paste code because that's never the way to go okay because we've already defined the function draw line here um this doesn't really know what to do so we need to import it from our utils and now this is throwing an error let's see okay and this is not what we want either way so we're gonna create a line so whenever we draw on the canvas this create line function is going to get called and um because we're passing it to the use draw hook the use draw Hook is something we've defined in the last video that essentially just handles the drawing process okay let's save that and then let's see at how it's working so let's start the client start the server and now that we've got those things done we can connect to the server let's see if we get a connect message right here because we should because we are connecting from the client grid we do get the connection messages and now let's see what happens so does the web sockets work no they don't but if we go to inspect and then we can go actually in this window we can go to um Network WS so web sockets and honestly it's kind of a pain working like that in um Firefox so let me just open this example in Chrome really quick um because I think the network tab is just way more enjoyable in Chrome let's go to the network tab right here and then also right here take a look at what happens so open the network tab go to web sockets and reconnect so we get the web sockets and this is the connection we are making to the server and now whenever I draw right here we can see this client is receiving a lot of information and this information is exactly what we drew right here but if we reconnect on this client and let's take a look at what this client gets this client is uploading a bunch of data so we are sending the data from the client so take a look at this we are sending the data from the client by emitting so that's what um this green stuff is right here we are emitting and we are receiving that data on the server sending it back to all websocket instances that are not the sender so everything except this guy so if I had five tabs open then the right four would all get the websockets data but this one wouldn't because it's the sender and then we are receiving that data on the client but we're not doing anything with that data yet because on the client we are not listening to the event and to listen to the event just like we do on the server on the client and we're going to do that inside of a use effect we don't want any unwanted side effects um have that with an empty dependency array and inside of that use effect now we can actually listen to um what happens when we draw a line so websockets dot on draw dashed line so whenever this event gets received from the client and we are going to receive a bunch of properties because remember we are just forwarding them from the server so and these are the the properties we are going to receive on the client and those are going to be of type draw line props and that is going to be a function so let's initialize initializes it as that and then the jawline props is going to be let's define it up here it's going to be this type right here so we have a previous point a current point and a color okay and to do anything inside of there we need the context of the canvas so we're going to say canvasref.current.com dot get context so this is going to be the ref to this canvas right here and after getting that ref we can actually do something with it but first we want a yard Clause so if the context doesn't exist we're going to return so nothing will happen but if it does we're gonna call the util helper we have initialized and then draw a line from the previous point to the current point on the context so the canvas in a color which is going to be color and that is exactly why we've abstracted the draw a line into a separate file because we don't want to copy paste code two times we can just call the same function twice from here and now let's take a look at what happens so actually that's we can just continue in Chrome for now we still have the server up and running and the client up and running let's restart these two clients and let's take a look at what happens and as you can see whenever we draw a line the changes get propagated to the other client so I could open them side by side right here close this down and as you can see it works and it also works with the color now the clear canvas doesn't work yet because we haven't done a websockets integration for that that one is going to be really simple but for now actually yeah let's let's work on the um on the clear for a second but remember before we do that and because we are using something externally in the use effect we also want to include that in our dependency array and now okay the clear is going to be really easy um so we're gonna listen to the clear event socket on clear and whenever the clear happens we're just gonna call the Clear function that we're getting from the use draw hook um that we're exporting from there and now when do we want to clear this well we can just say in here um socket dot emit clear that's all we need to do then on the server we need to listen to that event so we can say socket dot on and the string needs to match so it's going to be the clear and then we're going to say I O DOT emit clear and the difference between socket.broadcast.emit and I ordered emit is that while we are returning this one to every everyone bought the sender the i o dot emit is literally gonna emit the event to every connected instance so every canvas is going to be cleared we can save that take a look at what happens um so we're gonna draw something we see the drawings here if we clear the canvas the canvas is cleared for both great now the only problem that I have with this integration is that whenever we are not connected and there are something and then connect as you can see the drawing does not show up for the second client that connected which is something you definitely want in a you know something like a game and a solution I've come up with to figure that out is not keeping the canvas State service side because um I think that would be very intensive regarding the calculations on actually let me make the code a bit larger for you and because we we would have to pass a base64 every time we drew and from the client to the server and I don't think that's the way to go so what I've come up with is whenever we um connect in the from the client in the user fact we're emitting an event called client ready now we're going to listen to that event on the server uh so right here we can say let's say socket dot on on client they're ready because remember the strings need to match and whenever the client is ready we're just gonna um socket dot broadcast dot emit get canvas state so what are we doing essentially when a new client connects that has no idea what the current state of the canvas is we're gonna sending we're going to be sending that to the server and the server is sending to all other client instances that are previously connected that know what the current canvas state is that a new user has connected and it wants to get the canvas data from them um and null let me move that to the left and now we can work with that so we are getting a request for every other client to send back the canvas State data now to make use of that we are going to say on the client get canvas state so we're going to listen to that event We're not gonna get anything from the server because we didn't pass anything right here and then right here we're going to say if the canvasrev dot current dot to data URL does not exist then we are returning so essentially what this is going to do it's going to turn the canvas drawing that we have so this one right here whatever is on it is going to turn this into a long long long string and if you want we can log out the canvasref.current dot to data URL and see what happens so reload the page and go to the console and it doesn't show up right now because we would need to connect with another client and as you can see now um this client is supposed to send this to the server and as you can see this is a really long string that essentially captures whatever is in this image as a string so we can send that to the server and then back to the client that just connected now to do that we are going to say socket dot emit we're going to call this canvas Dash State and we're gonna pass the canvasrev dot current dot to data URL as the second parameter we can remove the optional chaining because we know this already exists and we're sending that to the server now to work with that on the server you know the drill by now we're going to say socket dot on we're going to listen to that event and whenever we receive the canvas dot appdf State we are receiving the state as the first parameter and so what so whatever we pass right here we are receiving that right here in the state and then let's say socket.broadcast dot emit um and let's call it canvas state from server and essentially what we're doing now is passing this state back to the client that just connected but to no one else um and we're going to pass the state just forward the state that we got on the server to the client that just connected and that is that is the work on the server done that's all we need to do and then on the client that just connected the it's going to be the only one that receives the image and we're going to say Can socket dot on and then canvas state from server let's just copy it from here because that's what we call the event on the server we're going to receive the state which is as I've shown you a really long string and whenever we receive that string let's console.log um I received the state to let the to let us know that we received the state on the client and then let's say Collins image is equal to a new image and the image.source is equal to the state so the really long image we passed to the server will be received right here we are initializing a new image and setting that state as the image source and whenever the image loads so image dot on load um a function is going to get called and that is going to be context if that exists dot draw image and that way we can you know draw the string to the actual canvas at zero zero so the canvas is just going to be the image is just going to be painted on the canvas can save all of this and now really important in the return statement of the use effect we need to clean up whatever we did with the socket dot on because these are similar to an event listener right and if we initialize event listeners in use effect we also need to clean them up so we need a bunch of sockets.off which is going to be the everything we have an on basically whenever we have a socket dot on we also need a socket.off so we have the get canvas State we have the canvas state from server and then we have the draw line and we also have the um clear weights didn't we have the clear oh we had it down here okay never mind let's move that up and now that we've cleaned up after itself let's take a look at what happens in the browser okay so I've connected with two clients and uh wait that that doesn't really make sense so let's have a smiley right here and then connect with a second instance and as we can see the changes do get propagated to the client so what exactly is happening we can take a look at the console um let's see what's happened I received the state and that is the console log we have defined right here so okay let me let me demonstrate you what happens we have this instance which is already connected and then the new instance this one comes along and um let's take a look at the code so whenever this new instance the second tab right here comes along it says I am ready the client is ready then the server takes notice of that and sends to everybody else so this tab right here the message okay somebody else connected get me the current canvas state so we're essentially turning this guy into our zombie that is just sending the data to the server because that's what that's what we're forcing it to do then the the client is listening and when it gets you know when when the server wants the state it's being listened to here and the client sends back the state with the current um canvas that we turned into a long as string the server is receiving that string and then emitting to everybody else so whatever whoever just connected the long s string that we are receiving right here and then we're setting that long s string as the um current canvas so we are sending it to the server receiving it on whoever just connected and that way we can get the current data and also all the benefits of you know just um drawing in colors and it will be monitored on all instances that are connected to the websockets server great okay I'm really happy with that websockets integration I think this is um a great practice if you've never worked with web sockets there are a bunch of ways you can expand on this project as well you can work on the CSS turn this into a beautiful page with soccer.io you could have separate rooms so not whoever connected to this server at all but you can have separate drawing rooms that could be something you want to work on and yeah just have fun with the project experiment a bit and that is pretty much it I really hope you found this video helpful um and fun to watch I'm gonna see you in the next one and until then have a good one bye bye
Info
Channel: Josh tried coding
Views: 7,119
Rating: undefined out of 5
Keywords: nextjs 13, nextjs, 13, react, next 13, websockets, typescript, nextjs websockets, nextjs 13 websockets, typescript websockets, nextjs 13 typescript, josh tried coding, joshtriedcoding
Id: Dib5TYHHfgA
Channel Id: undefined
Length: 32min 21sec (1941 seconds)
Published: Mon Nov 14 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.