Getting Started with Godot and Nakama for Online Mutiplayer Games

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
this is a complete free introduction to nakama with kudo nakama is a free and open source server technology that you can use to make multiplayer games for desktop mobile you name it it's developed by herrick labs who also sponsored this tutorial we have made a free and open source demo that you can find right now on github link in the description that has extensively commented code so you can learn from it and in this tutorial series we are going to look at the basics you're going to learn to write multiplayer server code and the foundations that you need to get started making a game with godot and nakama we are really focusing on the networking code as there is quite a bit of complexity and quite a bit to learn there and if you have any questions they are more than welcome you can leave them in the comments below or much better you can head to the nakama godot community and ask your questions there you can find the time codes to this tutorial in the video description below and all the links that you will need to follow it but with that we can get started installing nakama with docker and creating our first go to project with it this tutorial is going to show you how to install godot with nakama to get started using it as soon as possible we're not going to dive in the details of what is docker which one i'm going to use and things like these you have links in the description below to answer more questions we're going to need two things to install nakama and the nakamagoto client first is docker there's a link to this page in the description below if you are on windows or mac os you can download docker desktop and install it like any program it's going to install everything we need if you are on linux i'm going to show you on ubuntu how to install it i'm going to use the command line here and i'm going to say apt install and we want docker dot io followed by docker compose i have them installed here already so it's not going to install anything else but you will need these two components to run your nakama server once you have that installed the next part is the godot nakama client to use it we have to download this add-on and place it in our google project i'm going to create a new go to project so i'll open the game engine new project let's call it nakama test and i prepared a directory called nakama tutorial i'll select it create and edit the project this is just so i set up my directory with some files down there now if i go back to the nakamagodo repository you can go to the releases tab and download the nakama zip file now it's downloaded i'm going to click on the zip file to open it and i have my add-on directory that i want to copy into my nakama tutorial there so i'll drag and drop it and there you go you have the good old nakama client in your project it's almost ready to use we still need to add nakamada gd as an auto load in good old so heading back to godot i'm going to go to project project settings auto load and i want to go find the nakamado gt file so i'll dive down to add-ons com.hericlabs.nakama double click nakamada gd and add it alright so now in godot we've added an autoload to access the nakama api from our scripts that does not set up our server it's just the client side so now you can quit godot i'm gonna head back to my project's root directory and i'm going to fold all that into a subdirectory i'll call it my goto project and create a new one called nakama this one is going to hold the configuration for the server but also the code that you might want to run server side we want to let docker set up the nakama server for us download nakama and download the database program called cockroachdb to do so we're going to use docker compose it's going to compose several containers and download all the programs they depend on so in the documentation you have that section linked in the description below where you have a sample configuration file it's going to set up the database here and then it's going to set up nakama it describes how to download like the dependencies but also the commands to run to start the server so let's copy that and we're going to go back down to our project folder in the nakama sub directory i'll open it in my terminal here i'm first going to create an empty dockercompose.yaml file and then i'm going to open it in emacs like so in my text editor so i'll paste the content of the file and it's mostly good there's one thing that we want to do to set up our project is to set a server key a unique key when we run nakama so if you look at the text here you have a services category or set of settings first you have some settings for cockroachdb and then for nakama so it's under that nakama part that we run commands for nakama and you can see them in the entry point here these lines are going to run some commands anyway the one we're interested in is the one that says exec i'm going to zoom in a bit here exec nakama with a given name database address and we want to add another option to that comment that's going to set the server key so dash dash socket dot server underscore key it's very important that you rate it that way and then you can give your server a unique key that you will use from godot to connect to it so we chose nakamagoto demo for our demos here save that you can then close the window i'm going to close that as well now you need your terminal open in that nakama directory where you have the docker compose file and then you can say docker compose so this is the name of the docker compose program and you say up up is going to start the programs or the containers described in your docker compose configuration file but it's also going to download any program it lacks it's going to set everything up for you so i press enter and it's going to recreate a database it's going to download nakama if necessary for us here it's not necessary because we already installed it in another directory so i have cockroachdb and nakama somewhere and it started the server here that's what you can see in the command line at the bottom it's saying startup done so for you it might download files it might take a bit more time but at this point you're ready to get started coding with nakama and koto this guide was to get you started as fast as possible now if you're going to spend time working with nakama you might want to better understand docker you can find a link to the nakama documentation in the description below to learn more about it in the next part we are going to get started coding in godot and we're going to talk about how to authenticate users with the nakama server see you in the next part now we install nakama we are going to get started by authenticating with the server this is what you will learn in this video while this just looks like a print statement what happened here is we requested for the server to authenticate ourselves creating an account if necessary and we got the server's response which you can also see at the bottom in the output console but with that let's get started here in the left pane of my shell i am in my nakama directory the one that contains my dockercompose.yaml file so i'm going to type docker compose up to get the server started on the other side i'm in a tutorial directory where i have the minimal go to demo with no particular code except for some stuff to make it look pretty you can work with an empty guru project and use print statements again this one is just to make it look a bit nicer so i have a demo scene and it does nothing at the moment we are going to work with two separate gd script files to have the code that communicates with the server and let's say game code that uses it add two nodes i have one called demo and i'm going to add a node called server connection right below it then we want to add two scripts to these two nodes so for server connection one called serverconnection.gd i save it in a directory called source main and for demo same thing a script called demo.gt i'm going to remove the default comments in there so we can get started we're going to start with the authentication to authenticate to the server you will need a few bits of information so we're going to need the unique server key that we're going to store in a constant we're going to need to create a client and to use the nakama client api to authenticate with the server let's start with the key constant so that key is the unique key you set in your docker compose nakama call we've called it nakamagudo demo in our case then you're going to need a session object we're going to store it in a variable it's of type nakama session the session is going to store an authentication token that will allow the user to stay authenticated with the server it's going to be returned by us when we ask the client to authenticate the user the client is what we create next so let's create a pseudo private variable and we're going to ask nakama to create a client for us it's part of the nakama class you have a function called create client this function just takes your server key and it has some default parameters after that we're going to specify them explicitly so we need the ip address of the host in that case while working over the local host so 127.0.0.1 is going to be our ip address allowing us to access the admin backend by entering that ip address in our browser we're going to have the port over which we communicate with the server the default port is 7350 it's an integer in that case and finally you have the protocol that you use to communicate the default is http so you can remove these last three parameters and have client work exactly the same way any time if you want to read the source code for these functions you can control click over the create client function here and it will take you to nakama.gd in this case for the nakama class you don't have doc comments at this time but for other classes you will see that but for other classes you will find the code reference as comments inside the nakama source code so once we have that we are going to create our first function to authenticate the user we're going to call it authenticate async when we place the word async it's to signify that this function is not going to run and return instantly we have to send the server a request which can take some time and so for that we're going to use the yield keyword turning this function in a co routine this is very important because when you call these kinds of functions if you need them to return a value you must use the yield keyword to wait for the function to complete our authenticator sync will take two arguments you're going to have the email and the password of the user we can make it return some integer it's going to be the error code returned from the server so we're going to start with that so goto does not complain the result is going to be okay by default okay being a built-in constant in gd script in kuto and so we can say return the result if the request fails we will change that result variable but while setting it up first so the linter does not complain that we are not returning that value now what we're going to do is assign the result of an authentication call to our session when we call client.authenticate email async that is a method on the client api it will eventually resolve to a session object or it might be an exception in which case we have to change our result here okay so authenticate email a sync again you can control click to get to corresponding function in the script editor in godot and you will find all the parameters it mainly takes the email and the password of the user you can create an optional username for that user so you could use that email but you can let the player enter the username they would like for their account okay let's say it's the email or if you don't want any you can pass no and the next argument is whether or not you want to create an account you want to register an account for the user so if you're creating a login only you can set this to false and that way you will get an error if the user has not created an account first if you want to automatically register with that email and password you can set it to true it's default value now if we try to call that asynchronous function like that it's not going to work so we need to use the yield keyword to create a co routine and wait for this function to be completed in case you don't have too much experience with core routines in godot when you use the yield keyword calling a particular method it's going to instantly return a gdscript function state object which we can find in the dark so gscript function state here that object has a completed signal that's going to be emitted when the function finally returns so this is why we can yield on that function and wait for the completed signal to be emitted otherwise you can also yield on an object and wait for that object to return a given signal this is how you wait for an asynchronous function to return in godot then once it's completed you will get the return value of that function instead of that gdscript function state object if that authentication or server request worked it's going to create our new session it's going to return a nakama session object otherwise we can check if we got an exception an error here's how we're going to do it we're going to stall a new session in an object and we're going to say we want it to be a nakama session then we want to check if the new session this object rated by nakama is an exception so you have a method defined on the results returned by the nakama api called is exception you can use it to check if you are getting an error if the new session is not an exception we can assign it to our session variable we got a nakama session object otherwise though we want to return the correct error code so you we can say result is equal to new session dot get exception dot status code and this is going to give us an integer that corresponds to one of the error constants in good old if you want to find them you have to go to global scope i guess is it there yes if you search for error in the global scope reference you will find the constants that represent various types of errors and with that we can authenticate a user store an off token and keep them authenticated during the game session i'm going to rename my session variable here to underscore session to make it pseudo private to indicate to my teammates that i don't want them to modify that variable we don't want it to be accessible from outside that server connection script i'm also going to close the other tab nakama client here and then we can start using that authenticate async method so in our demo we're going to create an unready variable to get our server connection node here it's going to be equal to get node server connection then in the ready function so we want to authenticate a user we are going to create two variables email it's going to be a test at test.com and a password so my password i set it to password i think by default on nakama you have to have eight characters from the password that's the default configuration then we can do some debug print so that's how i recommend doing it if you started with an empty project in my case i have a debug panel here to put labels onto the screen so i'm going to create an other unready variable debug panel i'm going to get that debug panel node and my debug panel has a method called write message to write a message to the screen i'm going to call that write message we're going to start by saying well authenticating user and we're going to pass in the email here then we call our server connections authenticate async method so we are going to store the result from that function call and we have to use the yield keyword to wait for that call to resolve server connection dot authenticate async we need to pass in the email and the password here and wait for the function to emit the completed signal or the gdscript function state object to emit that signal right so if the result is ok then we can write to the debug panel or you can use print debug instead to write to the console i'm going to write authenticated the user successfully so i'm going to use a string template with modulo s and replace it with the email value otherwise we can duplicate that line and i'm going to say we could not authenticate the user here with that email we can now try the game ensure that the server is running of course otherwise you'll get an error and press f5 you'll see while authenticating the user we authenticated it successfully so there is a small delay when you communicate with the server even though it's local so you'll see that it takes a fraction of a second it's really fast to make these calls but they are still asynchronous even if they seem to happen instantly you can stop the server to press control c in your command to ask it to stop and if you press f5 then you will see we could not authenticate the user so in this demo we don't have more information but you can use then the error code to see why that happened this could be because the account was not created this can be because the server is offline so you could not establish a connection note that when there's an exception the nakama objects will also return an error message as a string we're going to move that to a function i'm going to create a function called request authentication i'm actually going to move the email and password in there as well whoops i need to move the two of you at the top of the function it's going to be a void function and then in ready we can call that function i'm doing that because we're going to have quite a few steps in this tutorial and the operations we do with the server so we are going to keep the ready function clean so we can sequence all these calls okay now we can move on to the complete game to see how we made the login form and register form work in this part we are going to see how we implemented the authentication in context in the demo that you can find on github you can find it in the google directory in the nakama goto demo repository linked in the video description let's just talk about how it works first the user enters their credentials you verify that the email address has the right form so you press enter or you log in the server isn't running so it's not going to work there but you send a request to the server to authenticate the user i won't talk about the ui too much the only notable thing is that the ui does not communicate with the server it only emits signal and another script in that case main menu is the one sending request to the server so when you press enter or click the login button the ui says okay there's a login requested or the login button was pressed and the user entered this email address and this password the main menu receives it in an on login and register login pressed function that you can see here on the screen this one is only going to disable the interface and call a function named authenticate user async it takes the email the password and also it tells if the user wants us to remember their email then authenticate user async is going to use a function similar to the one we coded in the basic tutorial here represented by server connection dot login async but it's going to do so in a loop the loop is going to have a counter of server request attempts it's an integer that we increment by one every time we make a new request up to a maximum number of attempts and every time we try to log in the user when the result of that call that server connection dot login async call is okay we get out of the loop and we can then say the email on the server and open the character menu there is a bit of extra code to write some error code optional error code on the status bar so this is what we can see here error code two the request failed it's a response from the nakama api and we can re-enable the login and register interface if we got an error and we couldn't authenticate the user now let's look at that login async function so i'm going to click control click on server connection and head down to login async that function doesn't do much it mostly delegates the call to the authenticator i'm going to open the authenticator let's see a control click works this is a delegate class that only handles the authentication logging in registering an account and also storing the user's authentication token because when you authenticate on a server you get a token some string that is valid for a certain amount of time so the next time the user logs in to the game we don't have to do a server request we can log them in instantly saving them a bit of time and the server a bit of work okay so the login async function is similar to what we coded in the simple example the main difference has to do with that token here we're going to see how this works in the next part if we have a token we are going to restore the client's session from the previous time the user was logged in we are going to authenticate the user using their email note that we did not supply a username here only the email and password and that we don't want to automatically register an account that's the fourth parameter then if the user could login could authenticate successfully we write the authentication token to a file now let us see how the authentication token works we created a helper class at the bottom of the authenticator script that's going to write and recover the users of token here's how it works so when we authenticate on the server the server answers with a session that has a token parameter we pass it to the session file workers write earth token method it's just going to write a file to the user directory and the file is named earth we use the file object to save the file and encrypt it using a password here passed to the function as well so this is all part of good o right we store the user's email and token on two separate lines and close the file and so then when we want to load the file back in we create a new file object open the encrypted file using again open encrypted with pass this is a method of the file class we pass in the file path we want to read the file so the file that read constant and the password to decrypt the file if the file was read successfully we can get the first line and store it as our of email and for the token we get the second line then close the file you must close the file manually in goto finally we ensure that the email we pass to the function the user's email matches the email that we had in the file and we can return the off token that way otherwise we'll return an empty string back to our login async function this is why we check if the token is not an empty string because the session file worker will always return a string it's just if we don't have a valid token it's going to return an empty string in which case we can skip ahead skip to the second part of the function and authenticate the user but if we have an earth token then we can restore the session using the nakama client api that has a wrestle session method and we have to ensure that the new session created or returned by that method is valid and it's not expired because every earth token has an expiration date the last thing i have to mention is that our login async is meant to be a co-routine and if we have a valid oauth token we'll return early from the function we must yield somewhere but the thing is if you have enough token already you're not going to communicate with the server you're going to restore the previous session object using the client api so because of that we have to add the extra yield here and we use engine.getmainloop and it has an idle frame signal that we can use to wait for the next frame that way we just wait for one frame and return from the function making the color when you try to yield on login async work as expected you also have a register async function that only registers the user and then we still have to log in so we separated the two operations so that when a user clicks on register they can register a new account and when they click on login it's not going to register a new account for them that's the main difference and that is how we authenticate the user in the final demo in this lesson we are going to connect to the server that is to say to establish a two-way communication channel between the client the player's machine and your web server this builds upon the authentication from the previous video let's get started back where we left off we're gonna head back to our server connection to write that server code and connecting to the server involves creating a socket a socket is a technical term for a pipe line that connects your client and the web server so let's create a new variable call it underscore socket and it's going to be of type nakama socket sockets allow you to send messages to the server but also to receive messages from the server now we're going to create a function called connect to server so function connect to server async as always it's going to be an asynchronous process we're going to return from the start error can't connect these error codes are all parts of godot they are built-in constants now the thing we're going to do here is ask the nakama api to create a socket first to do so we can use our client so the nakama class has a create socket from method to which you can pass your client and it will return a nakama socket object the nakama socket has an api that allows you to send messages or requests to the server like one to connect to connect to the server we're going to use the socket api so now we have our socket object this is not asynchronous by the way the object gets created instantly we can call methods on it connect async is one of them and it's the method that allows you to connect to the server this one takes our session object as an argument it's asynchronous so we have to yield over it i'm going to add some parentheses around that use the yield keyword and wait for the function call to be completed then we are going to store the result from that call again so we're going to create a result variable it's going to be of type nakama async result and it's going to store the resulting object there from that object we can check that our socket did connect to the server successfully so we can say if not result dot is exception like amazing result has this method here we can return okay we could connect to the server successfully now we can do a bit more than that from the socket and this is where it gets interesting so you will be able to use the socket api i'll click on connect async to jump to the source code where you have all the code documentation so you have lots of uh methods like add a matchmaker create a match etc to create a match between players well all sorts of things you want to do in your game but you also have lots of signals as you can see at the top of the nakama socket class you will get for example a signal telling you when a socket was closed imagine that it was closed by the server closing the connection then you can free your socket to ensure that you won't call methods on it you also have some messages you can receive from the server telling you that other players are connected like receive channel presence this is for the chat you also have received match presents telling you that another player joined or leaves a match a game you are part of anyway let's go back to server connection we're going to connect to one of these let's say socket.connect we're going to use the simplest one here closed so the connection was closed by the server we're going to connect to that node and create a new method for the callback called on nakama socket close this one's somewhat important if the connection was closed you don't want to use that socket object anymore because if you try to call something over it it's not going to work so you can create that callback function on the camera socket closed and you can set the socket variable to no doing so will free the nakama socket object and the reason is i'm going to go back to nakama socket and to the top of the file that the node extends reference it's implied whenever you create a gdscript file this is the base type that you necessarily extend when you have an empty gdscript class reference is a class that's reference counted meaning that when you don't have any references to an instance of the class the object in question will get freed from memory so setting our socket to no if nothing is referencing it is going to free our socket from memory now well done with the technical lingo we can go back to our demo code so through the scene i'm going to click on the script icon next to demo and we're going to call our connect to server async function i'm going to create a new function called connect to server i should call it async as well but for the sake of being coherent with the final demo code i'm going to stick to what we have on the repository so we're going to create a result variable from the start we're going to call serverconnection.connectserverasync that we just wrote and we know it returns an integer an error code and while going to yield over that call so server connection dot connect to server asynchronous we're going to wait for the process to complete and then you can use the print function and i'm going to use my debug panel that we use in this series so if the result is okay if we're going to write a message to debug debug panel we connect it to the server otherwise we can say if we get error can't connect because we know that's the error code that we are going to receive we're going to say could not connect with that in the ready function we can add a new line as always we're going to yield connect to server and we're going to wait for it to complete it's just as a base for the next step in the series for the next lesson but with that you can press f5 to see that we authenticate still as before and we have a message connected to the server with that we have the server connection established in the next segment i'm going to show you how we implemented similar code in the final goto nakama demo which is much more complex so i'm going to give you an overview of that code and in the next lesson we'll talk about joining the game world training a match in the final demo the server connection happens way after the login so you first log in and it happens right there when you confirm the character you want to use and you enter the game the server connection the creation of the socket to communicate with the server happens between these two parts let's see how exactly so i'm going to go to the main menu.gd script from the main menu scene and navigate down to join game world async this is where the server connection is happening let me expand the script editor so here you can see that we have a call to join world async this is something we'll talk about in the next lesson the connector server is very similar to what we did we call connect server async on server connection so this is about the same so far as what we did in our connect server async function from the demo i'm going to control click on this to jump to the server connection script and there you can see there's a little more code so same thing we create a socket from the client using the nakama api we then call socket dot connect async the session in the final demo is stored on a delegate class called authenticator so instead of being underscore session it's stored in a separate class that stores the session from the user and then instead of using result.isexception we have a little helper class that's going to pass the exception and return an integer for us so it converts the neck amazing result to an integer but it does a bit more than that anyway if our result is okay then we connect to the various signals that we want to connect to on the socket api connected closed received an error match present which we'll talk about a bit later and that is it it's very similar to what we did in the basic demo we just do a bit more we connect to a few more signals i'm going to jump to one of these callbacks or search for it it seems i can't jump to it like that so you can see that when the socket gets closed we set the socket to null just like in our basic demo then we have some more codes when we receive new match presences from the nakama server this is a server telling us that a user left or joined the world we are part of so if you're on a game level if you're in a first person shooter match if you're in a pvp battle in some mobile game this callback and the received match presence signal from the nakama socket api will allow you to spawn and remove players from an ongoing game for example they disconnect those kinds of things we'll talk about the rest of it later this big function here is the one that handles the changes in the game state when you are playing with other players in real time but that's about it it's all to say that you have main menu dot join game world async that has that connection to server and then in server connection you have the on nakama socket callbacks that listen to the server that get messages from the server and do things based on that but with that let's head to the next lesson to talk about that matchmaking see you there building upon the previous lesson we are going to see how to join a game world and have multiple players be able to join the same world this is going through the matchmaking system and what's called authoritative multiplayer part of nakama to do so we're going to write some server code using the lua programming language when many players are interacting in the same match or the same game you want the server or at least the host to double check everything that everyone is doing to prevent any cheating from happening so that's the role of that code here it's going to run a game loop on the server in our case it's going to run 10 times per second we'll see that in detail in a moment and that code is going to check and validate what every player is doing now we're not going to do too much we're just going to lay down the foundations then we will see in the godot nakama demo i will give you an overview of the code we use to make the characters interact with one another and you can already see that it involves quite a bit more code which is why we're not going to go through it step by step but i'm going to give you an overview of it instead but with that let's get started we're going to start by writing that module as our good site code is going to depend on it here i am in my google project i have one directory for godot and one called nakama with my docker compose file we are going to write some modules that will be run by the server to do so you want to add a modules directory i'm going to add that now so modules and enter it and place your lua or go files in there they're going to be run automatically by nakama when we start the server so i'm going to create a new file called world underscore control.lua and we can get started in there this one is going to define the base api for the authoritative multiplier that we are going to code you can find a complete documentation for that and code reference linked in the description below it's the authoritative multiplier page in the nakama documentation here i'm going to show you the minimal setup to get this working so we can have multiple players joining the same world and then we will see in the final demo how we coded all that to have a bit more complexity how we have the game loop essentially but here it's just the basics we're going to need to export a set of functions that nakama expects so we're going to create a local variable local table here it's called world control like the module note that a convention in lua is also to call it m like that for the the module that's going to hold some functions and we're going to return it at the end of the module at the end of the file we need to define five functions here the first one is going to be matching it so i'm going to insert a new function you have to call it every time world control dot match init so the function gets registered into our world control table and each of these functions is going to take a set of parameters and that is defined in the nakama documentation so if we go there to match init it takes a context object and parameters i'll let you read the reference on context subject to see what you can find in there but what's important is you also get the data that you must return from the function we need to return a state a tick rate and a label so we need the context and params keywords there arguments and we're going to define the three variables that we need to return first we're going to have the state the state is a table that you create however you'd like so we're going to have a key called presences in there and in lua you have to use the equal sign to assign values to a key similar to godot actually you can use the same syntax this state we are going to pass from function to function in the match loops and we are going to update ourselves however we want so we want the presences here to keep track of how many players and who is connected to the match then we want the tick rate this is the frame rate on your server the frame rate of that match how many times per second the nakama server is going to run a function that we'll define in a moment called match loop so in that case 10 times per second the higher the tick rate the more resource intensive the game becomes but also the more responsive it can be for the players depends on their connection their ping but this can help have more reactive physics etc and finally the label is the title of the match that the players could use for example if you had a server search interface or match search interface where players can look for a specific match with their friends or something like that you could list these labels in the client in godot anyway we're going to call it game world it will be the only world we're going to have here i'm not going to create multiple and so we're going to return our parameters so to return a list of parameters i guess it returns as a table as well seems everything is based around tables in lua we can just use commas like in python so we return the state the tick rate and the label all right that's our match in it then we have to add a few functions related to joining and leaving the match so we have three of them i'm going to create a function template and we're going to add weld control dot let's start with match join attempt so someone tries to join the match this one takes five parameters context dispatcher tick state no six sorry presence and metadata the context is the same context object as for matching it the dispatcher is an object that allows you to broadcast messages to other clients for example so it's an object that has some methods to spread data send messages things like these like for example you could say this player has joined the tick is an integer that represents the current frame of the match so if you want to send that to the player for example if you have some game world simulation in real time you can send the tick number so the game can simulate the state up until there and you don't have to send all the data through the server state is our state variable presence is going to be this player that's trying to join so a given client and well metadata can be any kind of metadata from this function we have to return the state object that we can update if we'd like to and we have to return true or false if we accept or reject the player's connection so our default return path will return the non-modified state and true the player could join and then we want to have a check to see if the player has already logged in and try to join the match we can say if state dot presents is presence dot user id so each presence that we get from nakama is going to have a few parameters and one of them is the unique id of the user so we can say if it's not equal to nil so if this key exists in our table note that the not in lua uses that tilde symbol we can say then we're going to return state false so we'll return the state object then we refuse the user's connection and we can pass an extra error message there so we can say the user is already logged in and we end the block with the end keyword all right i'm going to copy that function for the match join function so i'm duplicating the same function i'm just going to rename it to match join match join takes slightly different parameters so the first four are the same but you don't have the metadata and instead of one present you're going to have a list or a table of presences and we can remove the code that we have in there the match drain function tells you that multiple players joined the match and it might batch them for example if you quickly have many players joining it so you might have three four new players that join the world in which case we can use that information to update our state table so we're going to use the ipez function to get key item pairs from our presences here so we're just going to use the value but we don't need the key and we're going to take the presents in ipads presences this is a function that gives us item pairs we are going to do state dot presences we are going to add new keys using the presence object's user id or presence table should i say and we're going to assign the presence to it so we loop over these new presences the persons who joined the match that went through match train attempt and for each of them we're going to use the user id that's a unique idea as a key and keep track of the corresponding presence that way when people leave we can free that from the presences dictionary and in a complete game you can use that information to tell the client these players left you can use it to have a pop-up notification for example that's one player left like we have in the final demo with that you want to return just the state object updated so we've effectively modified it we have a very similar function that is match leave so we're going to copy matchjoin and i'm going to swap that substitute leave to the word join this one takes the same parameters the code is going to be almost the same we want to remove the players that left the game from the state table so we're going to replace presents here by nil then we have two more functions to to add i forgot one at the beginning uh we have six and not five so we're going to have well control dot match loop it's the main loop of the game match it's going to run tick rate times per second in our case 10. so it takes the context dispatcher it takes the tick state and a list of messages that could have been passed to the function in our case we're just going to return the state we're not going to have gameplay checks but just note that this is the kind of function where you would check for what the players did the kind of inputs they entered and validate their state you ensure that they are not moving to an invalid position etc and that depends entirely on the game finally we have the function match terminate this one is going to be run when the match was terminated by the server on the server side and it takes almost the same parameters as match loop only the last one is grace seconds it's a duration in seconds for all clients to do things such as leaving the match slowly playing a bit of an animation taking the players back to the menu cleaning up the state depends on the game but there again we are going to return an updated state object mainly and this is the base setup right this is the minimal setup that you need to get your authoritative multiplayer server started for more information i invite you to read the authoritative multiplayer docs page the link is in the description below it has lots of information you have the api but also the code reference there and all the objects that you can use and functions that you call to help you the code we wrote here allows us to define the matchmaking system in the match loop on the nakama server now we need a way for the player to join that match to do so we need to define an rpc a remote procedure call that's just a function that you can call on the server from the client so let's create a new file called world rpc not rpg rpc.lua and we're going to define that function in there i'm first going to require the nakama module because we're going to need it to register that remote procedure call so let's create a new variable nakama where we require the nakama module then we want to call nakama dot register rpc at the end of the file we're going to pass it the identifier of a function so we're going to create that function in a second but let's call it get world id and then we have to pass it a string a name to the function that we'll call from guto so get world id we're going to use the same okay so i'm going to create a local function for that let's see local function and let's call it get world id as it's an rpc it's going to get two parameters we're not going to use them but these ones should be context and payload so context being your context object as usual payload being any table of data could be some json data that you're getting from godot in that case i guess but we're not going to use the parameters so let's use the underscore sign instead what we're going to do here is either create a match if none exists or return the only match that we're going to ever create from this function we want to create one match for the purpose of this demo of course if you want complete matchmaking and all you will need a bit more code but let's first get the matches from nakama so the nakama module has a function called matchlist that's going to give you a table an array of matches then we can see if we have a current match so to do that we get the current match as the first index in the matches table it's a table it works like an array just note that in lua the first index in an array is 1 and not 0. now what we can do is if that index does not exist in the array the current match value is going to be equal to nil so we can say if current match is equal to nil then return nakama dot match create it's going to create a match and return its id so we're going to use the world control module that we just created and we're going to pass an empty table of data we don't need to pass in any information right there otherwise we are going to return the current match dot match id it's a field that every match has it has a unique identifier that we need to pass to godot for the player to be able to join that match and with that we have our server side code done to have our matchmaking system and to be able to join worlds so with that i'll head to my shell and i'm going to navigate to my nakama directory there when i have the doc compose.yaml file then i'm going to call docker compose up to start the server this is just to show you in the log messages you should find found runtime modules count two we have worldcontrol.lua and worldrpc.lua this confirms that they have been found and then you can see whenever you registered an rpc function the registered lua rpc function invocation we have get world id so we will be able to call it from our client be sure that you have that if you don't it's probably that you have an error in your code but if that's the case you should get some error message somewhere in there you want to read that log generally to debug your server startup and runtime but with that we're going to move on to the goto project to work on draining the world in this part we are going to add the ability for the player to join the world or the match so ensure that you have your server running and with that let's get started we are going to add a join world function to the demo and also to the server connection with which we are going to start at the bottom of the class under connect server async we're going to add a new function join world async so in this one we are going to return a dictionary we are going to return the other players that are connected to the world and we will get that from the matchmaking api so we want to join a given match in nakamas terms and to do that we need to know which match to join we're going to use the remote procedural call that we defined in the previous part so let's create a new variable we're going to call it world let's say and we're going to get an api our pc object so it's nakama api dot api rpc this is the written type of this function the rbc's calling functions on the nakama server is available through the client api the client being the variable we created a few parts before so we're going to call client.rpc async and you want to give it your session object as well as the name of the function that you want to call so it's going to be get world id the third argument after that is a payload it would be a json string that you might want to pass as a message or some data or metadata to the function and we're going to wait for that function to be completed we wrote that function and we know that it should return the world id as a string so we can do it like that we can check if it's not an exception like the result value that api rpc so if not is exception we're going to store our world id let's say we can create a new world id variable and we can say world id is equal to world dot payload again the payload being a string of text that is being sent back by the server and typically this would be jason but in the case of our world id it's just a string note that you can move that to the top of the script that world id you might want to store it for later going back down to join well they sync well let's get rid of that error right now we're going to return an empty dictionary from the function and now we have the id we want to join that match if we can so we're going to call socket dot join match async and we want to pass the world id to that because it's going to join that world specifically as with any asynchronous function we have to wait for it to complete and we're going to use the yield keyword to do so and of course we want to store the result value from that so it's going to be matchtrain results we can call it that way and it's going to be f type nakama dot rtapi.max rt api standing for real time api real time multiplayer now once again we can check for exceptions here so if matchstrain result that is exception we can do some exception handling we're going to get the exception this time and print an error if there's a problem training the match this can be useful because you might have an error on server but you want to see on the clients when you are developing the game if something failed if some call fails so you can debug it see in which line it happened in your code etc so that's what we're going to do here we're going to get a nakama exception from the matchstone result variable so matched on result dot get exception and we're going to print an error from that so we can say there was an error joining the match and we're going to get a status code from the exception so it's going to be an integer and a message so we're going to use string interpolation to have the two here the first one is going to be the exceptions status code and the second one is going to be the exception objects message and in those cases that's where you could also say i want to return an empty dictionary for example however if it worked we're going to get a list of presenters in matchstrain result in which case we can loop over them so for presence in matchdownresult.presences we're going to store them in a variable so we're going to build a dictionary from them one thing we can do is we can store it as a class variable so i'm going to go to the top of the script and put it under the world id there and it's going to be an empty dictionary by default but now down there in our loop we can say presences we're going to use the user id as the key like we did in lua so presence dot user id there is going to be the key is going to be equal to that presence object and that way we get a dictionary where the keys are the user ids over which we can loop and the values are the user data where we have a bit more information and finally we have to make sure that we return those presences now let's go back to the demo.gd file and we're going to add our new triangle function at the bottom so let's create a function called join world and we're going to install the presences get the presences from our newly created function server connection so let's create a presences variable it's going to be a dictionary and we're going to yield on server connection dot join world async we need to wait for the call to be completed and once we got that we're going to write in my case to my debug panel but you can use the print function as usual i'm going to say we join the world we're not doing exception handling here as you can see because from that join world async method on server connection will only return a dictionary while not returning exceptions or errors and i'm going to write another message here we're going to show if we have some all connected players so we can say we're going to use string interpolation and print presences dot size okay and from our ready function we can yield on train world and wait for it to be completed all right let's run the game and you're going to see zero of the connected players now i'm going to keep that game running here in that instance and i'm going to make a copy of the project here i'm going to open another instance i need to change the credentials down there otherwise we're going to get an error but if i do that and play the game you will see we have one other connected player and comes from the other instance of the game i was running so this is a quick way to check that you are joining the world and that you can have multiple players in the same world because this is counted from the presences that were returned by the server and that is how you make multiple players join the same game world in a nutshell we're only covering the basics here but hopefully that's enough to get you started and in the next segment we'll look at much more complex code to be honest but how we did it in the final goto nakama demo see you there in this part i'm going to run you through the code that we have in the final godo nakama demo i'm going to run you through the lua code that's a bit more complex than what we wrote and also talk a bit about how we communicate with the server and our authoritative match loop first let's talk about where that code comes into play so it's after the login and after selecting a character once you enter the game world you first spawn it's going to be broadcasted to all clients and you can then move around and these information gets sent to the server which updates your state and broadcasts it to other players 10 times per second it tends to accumulate messages that you send via the socket api so let's see first on the client side how you send messages and then we'll see how they are processed on the server so you join the game via the main menu we have a function called join game world async that calls drone world async on server connection remember this is the final good nakama demo that you can find on github and not the tutorial version so the code here to join the world is similar to what we had get a match id through our get world id i rpc we ensure that it's fine and we store the world id for the time that you are part of a certain level or match we then try to join the match ensure that everything is okay and if it is we store all the presences in an array until there it's what we've done in the demo then we join the chat which we will see in a future lesson what's interesting is that from there we use the socket api to send messages to the server once we've joined the match to update our position for example so let's move down a bit so we have functions called for example send position update we ensure that the socket object is there is valid so we have an active connection we create a dictionary that we convert to json right so we call that the payload it's the message that you send to the server and then on the socket api you have sent match state async this allows you to send to a given match that's why we have to store our world id then we send an operation code this is an integer that represents specific commands or bit of information you want to get on the server so we'll see that we have the same a num here on the client but also on the server in a second and then you pass your message and this is what you get on the server through the message let me see well control dot match loop the messages is a list of these messages that come in the form of a json dictionary with an operation code i ctrl click on up codes to see where they are defined now defined at the top of our script and so we have an enumeration called upcode and it's just a list of update position is one update input is two status three etc note that our enum starts on one that is because nakama has custom codes that are equal to zero or have negative values you can use all positive values above 1. and so now if i go back to my server code worldcontrol.lua at the top we have an array that defines the up codes as well so we say update position is 1 update input is 2 update status 3 etc now these up codes map to functions so we've decided to have a variable called commands and commands for each of these up codes is going to map to a function for example updating the position is going to look at the positions in our state again state being the matches state that you pass from function to function that we wrote the previous part and so we update it if it exists for a given user id in other words up codes map to functions or operations that you want to do on the server that entirely depend on your game which is why we haven't written specific ones they also add quite a bit of complexity to the teaching part now let's look at the specific of our world control authoritative matchmaking loop we have these opcodes that each of them is essentially going to update some data in our state field there we have the same functions like worldcontrol.matchinit worldcontrol.matchjointemp matchjoin etc that we wrote in the previous lesson we just have a bit more data so when we initialize the match we not only have the presences but we have a table of inputs of positions of jumps etc for each of them we're going to store a player's id and the corresponding inputs position jump state colors etc match drawing attempt is about the same what's important is match join i wanted to touch on that because when a player joins a match in the state dictionary in the state table we initialize some values the player's presence positions the inputs colors and names and we feed it with dummy values we just initialize some variables if you want except these are key value pairs in a table and we do that because when you join the match you haven't spawned in the game world and say depending on your team you might spawn in different places or you might have entered the map through a different entrance so at this point in match join it's like a constructor in a sense we initialize some data to ensure that all the keys we want to access later exist in match leave we then clean that data you can see that we set everything back to nil we remove the key value pairs and it's in match loop that we check for the up codes if you want the messages is going to be a table or an array of messages which we loop over and for each of them we look at the up code and we decode the json that came the payload that we sent for example in what i showed you i have to go back to send position update where we send the update position up code and the corresponding json payload that information the user id and the position so once we have that we ensure that we have an existing command that corresponds to that upcode and we check the up codes so we do a switch case statement if you want with ifs and elifs if the up code is spawn then we spawn the player so we set them spawnposition and we send the information through the dispatcher we broadcast a message this is sending that information to one or more clients and we say here where you spawned it's the server that says where you spawn spawn position here being a constant in our game it's not something dynamic it's at the top of the script go back to the top and you can see we have some hardcoded values uh the smart positions spawn height and the width of our level here because it's a demo because it's uh limited in complexity but uh there you go if the upcode is update color in the loop we send your new color to all the other connected players etc this is how it works in a nutshell and you can see the use of the dispatcher argument with its broadcast message function which allows you to forward a piece of information that one client sends so i've moved there i've changed my color and then the dispatcher sends it to the other players and you have an example of match terminate that might be interesting as well when we terminate the match we save the information about the players could be anything you could decide that you save analytics of the player how many persons they've killed if they won or lost their mmr if you want to have a competitive multiplayer game those sorts of things and you can write them to the storage which will be the topic of the next lesson let's talk about world rpc really briefly so let me go there you will see more functions that are related to adding and registering and removing character names from the server so this is stored this prevents players from picking a name for their character that is already taken by another but besides that get world id is the same kind of function we wrote in the minimal demo we just have a pseudo private function that kind of encapsulates the getting the first world because later you might want to allow players to choose the world but if it's their first match you might want to send them to the tutorial area or something like that so you can have a few functions for that and get first world it's just like we did it in the demo with that this is a quick overview of that part of the code i will leave some links below to the corresponding files that include code comments and if you have questions that are welcome please ask them in the comments below with that in the next part we will look at storage see you there when you are working on an online game you want to write data to the server the player has had that many victories their name is this or that and they use that class well you want to store data in this part we're going to see how to write and retrieve data from the server storage so let's head to our demos.gd script where we will first create some data that we want to store your data is going to be json collection so it's like a json object in which you can nest objects key and value pairs as deep as you want we're going to create a list of characters here an array and we're going to store each character as a dictionary for example let's say we want to store our character's name it's going to be jack and a character's color that's what we do in the complete godu nakama demo so we're going to create godot color object and the color class has a method called 2html that returns the color as a hexadecimal string and we're going to say we want it without alpha add a comma duplicate the line and we're going to store another character called lisa let's say and her color is going to be red with that we have some data that we want to store on the server so that's where we have to head to serverconnection.gd and around the bottom we are going to create a new method for that let's create a new function called write characters async this is just an example of writing and retrieving data the point is that you can really store anything you want any better dictionary or data that you can convert to json so we're going to say you have to pass a list of characters by default it's going to be empty so it would wipe the characters array and we have to call a function on the client api so let's go with nakama client our client here stored in the underscore client variable and we're going to call write storage objects async for this we need to pass two arguments first we need to pass the session object to authenticate the client that's calling that and then i'm going to wrap my lines here and there i'm going to write my nakama write storage object and we need to pass an array of nakama write storage objects so i'm going to create an array and let's create a new nakama write storage object dot new so create an instance of the class this one is going to take a few parameters you can control click on it to see the parameters to get there and you can see in the init function it needs the name of a collection it's a key to your table let's say on the server where the data is going to be stored so you have one collection one key you can think of it as nested dictionaries right we're going to create those two then the read permissions i'll expand the editor here write permissions the value that you want to store is going to be some data converted to a json string and the version is a specific value that would be provided by nakama it tells you if some data existed on the server before to do versioning but it's not something that you provide yourself like saying it's file version 0.1.0 so just be wary of that so back to the server connection script we first passed the collection so let's say this is data about the player let's say it's player data so we're going to store the two characters that the player made our example so we're going to set characters as the key and this collection and key will use it to retrieve the data from the server now we need to pass some read and write permissions so to do so we're going to head back to the top of the script and these are integer values 0 1 2 so we want to store them in nums around the top of the script so we're going to create a first in them called read permissions and it's going to have don't read owner raid is the value 1 and public read so like you can prevent some clients from reading other clients data etc and we're going to have write permissions there the values are full zero it's you prevent writing and one means the owner of the data can write to the storage back to write characters async we are going to pass in some read positions so we're going to say the owner of the data can read it or write to it so write permissions dot owner right these values are just one and one but as you can see it's much nicer if we have an enum for that it's much more readable then we need to pass the data as a string so we're going to use the json class json.print is going to convert some godot data into a dictionary and we're going to create a dictionary like that we're going to say characters is equal to so the character's key here is going to be equal to characters here at the array so characters finally we still have to pass the last argument we're going to pass an empty string so we don't have any version that we pass to the object now with that we are going to wrap that call and turn it into a core routine so use the yield keyword at the start and at the bottom we are going to wait for the call to complete and that's all we need for this function now we're going to write a function to get that data back so let's create a new function i'll call it get characters async and this one is going to return an array of characters so our initial data type let's first create that arrays we're going to create an empty one to start with and return it from the function now to retrieve the objects or any data from the storage we have to use another function of the client api it's going to be read storage object async so this one we are going to pass our session and we need to pass an array of nakama storage object id like so so we're going to create a new instance of that object now for this one you need to pass the following pieces of information the collection the key that we use to store the data the id of the user who owns the data so in our case it's going to be the id of the current client and you can also pass a version hash but while not using that at the moment so the collection is player data the key characters so we're going to pass player data the second argument is going to be characters and the id of the user is held on the session object the nakama session so we're going to pass session.userid now as usual we're going to turn that into a co routine so we're going to wait for the function to complete i'll wrap it in parentheses and add the yield keyword in front of the call and we're going to store that in a variable so let's call that variable storage objects because it returned an object of the type nakama api dot api storage objects so a list of these objects if you call that function specifically restart objects async you can always control click on the function to jump to the function definition so you can see exactly what it returns at the moment some complex return types are not supported by godot version 3.2 so this is why you don't get autocompletion for them all right so we get these storage objects now we need to convert them to an array of characters so we can check that we have objects on our storage objects it has a key called objects and we're going to use the json class to decode the storage objects that's our json data json text so we're going to create a new variable let's call it decoded we expect an array an array of characters this is what we're supposed to store so we're going to call json.pass now we know that we only have one object we created our characters here we wrap them into one dictionary so we expect only one object we're going to pass storage objects dot objects zero and this is what we need the if statement because if we try to access something that doesn't exist it's not going to work dot value and then the api storage objects store a result key that contains the dictionary that we expect so result dot characters and just so you get it the storage object is going to look something like that so it's going to be a dictionary with a keycard result and that result is a dictionary that contains our characters i'm using a different syntax like the python like syntax and not lua this time but it's going to be characters and characters is going to be an array you get the point that's the kind of data you get back from the api storage object now this should give us our array of characters so then we can say characters equals decoded and note that depending on how you want to write the function you can return the first line if we get to the end of the function we return an empty array like so and if we have storage objects we can return decode it our variable and with that we can write data on the server and retrieve it so to do so we're going to save our code and head to the dmod.gd file here we need to call right characters async on our server connection node so let's write the yield keyword server connection dot write characters async we're going to write our arrays of characters wait for the function to complete and then we are going to retrieve the data from the server that's the whole point of the exercise and of the example we can say characters data is going to be equal to yield server connection dot get characters async and we wait for that to complete as well from which you can just you could say print the character data you would see that we retrieve it we are going to write it to the debug panel so what i'm going to do is create some text string to write one label to the debug panel let's call it string it's going to be an empty string and i'm going to loop over the characters so for character in character's data i'm going to add some text to my string variable we're going to say the name and the color of the character so we can use string in the interpolation for that with a backlash n at the end of the line to have line wraps and we can say we want to print the character's name and the character's color and we'll write that to our debug panel so debug panel.writemessage is going to be from server storage so this is the data we get back from the server right while not printing it directly and i can put a backlash in maybe here and plus string now it's time to test it my server is running and and press f5 we'll see from the server storage we get jack and the color lisa and the colors so once you have the data you can then load it back to create characters in the game which is what we do in the final demo but that's the gist of server storage it's really all you do the data you're going to store on the server is entirely up to you and depends on your game but that is at least the basics of it in this part we are going to see how to join a chat and make multiple users communicate together on the same chat channel so i'm going to send some message on one side and you will see that another user with different name will reply on the other side so we have two different instances of godot running the demo at the moment we're not going to see how to design that chat box here instead we're going to focus on the server side code once again this is following our series on getting started with good oh and nakama you'll find the link to the previous parts in the description below and with that let's get started in the previous part we worked on the server storage and so we were outputting information to the debug panel i'm now going to hide it because we are going to work with the chat box so in the file system tab head to the source ui directory and you'll find a chatbox.tscn file drag and drop it under the canvas layer you'll see it appear in the middle of the screen i'm not going to detail how it works here just know that it mostly has a function called add reply and this one is going to add one of these lines here with the player's name or the username and some placeholder text in that case now the chat box also has some signal if we go to the notes tab the text send signal is going to tell you when the player the user press the enter key while focusing on this bar there so if i type some text and press enter it disappears and it presses the send button it's going to emit that text send signal which we're going to use to send the text to the server wait for the server's response and when the server tells us it received the message we're going to add it to the chat box before we move on to the server code i want us to do one thing we're going to go to the demo and create a new unready variable let's call it chat box to store our chat box this is just to not forget later on then we can start working on the server side code so open server connection and we're going to need a few things we're going to first create a function to join the chat after join world async that we wrote in a previous part i'm going to expand the script editor and write join chat async this one is going to return an integer an error code and so we're going to use the socket api to join the chat so socket dot join chat async we have to pass a chat channel that we want to join you can name it however you want let's call this one world you can imagine that you could have one chat channel per match for example but you can also have multiple channels like in an mmorpg where you have the world the current region you are in the market channel those kinds of things that you can join or communicate through however you'd like then we have to pass a channel type so this is defined on the nakama socket object you have a channel type in num and we want to create a room note that you also have direct message or group if your player is part of a team or trying to message another player directly the last two arguments are persistence a boolean so whether you want to store the messages or not and the last argument is if you want the user to be hidden on the chat it's also boolean so we're going to have false for the two of them then as usual this is an asynchronous function so we have to wait for it to complete i'm going to wrap it into parentheses and add the yield keyword before don't forget to wait for it to be completed here and then we're going to store that in a variable the result from that called the return value let's call it chat join result the type that nakama returns is from nakama rt api real-time api it's a channel object and so we are going to yield on that value here i'm also going to wrap the line now we can check if that joint worked or not so if try join the result is not an exception we're going to store our channel id so for that we need a new variable at the top after presences or world id you are going to create a channel id it's going to be an empty string to start with and going back down to our chat result we're going to set the channel id to try drawing result dot id now we're going to return okay in that case and if the chat join is an exception if it didn't work return error we can say connection error for example and that's it that allows you to join the chat then we need a function to send a text message so let's write one we'll call it syntax async and we're going to just pass some text that we want to send to the chat we'll also return an error code if it didn't work so again we can do error and exception handling throughout our application the first thing is you need to use the socket api so you could say if you don't have a socket this is some safety statement at the start you're going to return error unavailable and we want to make sure that we also have a channel id so the the thing is you need to call joint chat async before you can send text to the chat right so we can say if the channel id is an empty string then we're going to print some error as well there's a function print error for that that's going to give you some debugging information along with the print statement and we can say can't send a text message to the chat channel id is an empty string is missing let's say and in that case we can return error invalid data for example then we can finally do the calls to actually write the chat message so i'm going to yield instantly and we're going to call on the socket api write chat message async to that function we have to pass our channel id the channel we write to then you have to pass a dictionary that's going to serve as the payload so the dictionary should have a message key and the message should contain the text and finally we want to wait for that call to complete now of course we're going to store the result from that request so the result is going to be of type nakama rt api so again it's on the real time api it's called the channel message ack ack here stands for acknowledgement it's the server telling you that it got the message correctly and then we can again do some conditional returns so we can use the ternary operator here we can say return the connection error if the result is an exception if result dot is exception otherwise we return okay okay so now we can connect to the chat and send a text message while missing one part it's the ability to receive messages from other players sent by the server now to do so we have to go back to the connect to server async function when we connected the server we connected to some signal on the socket object it turns out there is another signal we can use to receive messages so socket dot connect the signal is called receive channel message with under skulls and we are going to connect on this object and create a new function we'll call it on nakama socket followed by the name of the signal receive channel message we're going to create a new signal at the top of the script to emit some information when we received a chat message the signal chat message received and that will be the one that our demo.jscript will connect to we're going to pass the username followed by the text of the chat message and you could use that too basically when you receive a chat message you might have extra information that you want to filter and emit a neat signal that's just going to have the data you need to display on the screen for the player and so we can move to the bottom of the file after on nakama socket closed and we need to write our function so let's create a new function on nakamasa receive channel message you are going to get a message of type nakama api dot api channel message and it's not going to return anything because it's going to emit our signal we can get some code on the message so we're going to check for the kind of code we get and it should be zero in our case if it's not we're going to return from the function it's that while not getting a proper channel message in there the messages are similar to the ones you would see in the lua api on the server side code so you get some json data that we are going to unpack so we're going to create a new variable called content going to be a dictionary because we are going to pass some json from our message now we're going to get some json data from the server so we have to turn it into some g-script data some dictionary we're going to use json.pass from that and that data lies in the message.content property then that dictionary is going to have a result key it's a bit like what we got with the characters in the previous part so we have to get that result and from there we can emit our signal chat message received we're going to send the username so message.username and the content so it's in content dot message the text string the chat message and with that well done for the server connection part next we are going to move on to the demo.gd script to make some calls and to display the messages in the chat box the demo part is a bit easier than the server connection so here's what we're going to do we're going to join the chat then we're going to listen to the user pressing enter when using the chat box so the text send signal and we'll connect to the signal on server connection.gd allow me to go back to the top chat message received because we have to send the message to the server and get a response from the server that the message got stored and from there we will draw the text in the chat box there's a bit of looping here as you can see but that's how it works in an online game you need to have a lot of stuff go through the server anyway i'm going to create a new variable at the top just to store the user's color and by default i'm going to use a constant from the color class color.line now in the ready function of demo.gd we have to join the chat so we're going to yield on serverconnection.joinchat async wait for the call to complete and voila you've joined the chat there what we have to do next is do some signal connections so back to the scene we're going to click on chat box head to the note tab and double click on text sent note that for testing purposes you don't have to have that signal you can call connection dot send text async directly and bypass that part that's if you didn't use the demo anyway i'm going to double click on text sent and connect that to my demo.gd script at the bottom of the file here i'm going to yield and call server connection dot send text async and i'm going to send my text there and wait for this to complete now we need to connect the signal we created on server connection chat message received so click on server connection and in the note tab double click on chat message received to connect it to demo.jd and here we are going to add a reply to the chat box you could use a print statement instead i'm going to do chat box dot add reply and we're going to pass the text then we need to pass the user name and finally use a column the chat box add reply function if you control click on it you'll see it takes some chat text the sender's name and some color and with that i have the server running so i can say something like hi it will display my email address the one i used to connect here and the message this has gone through the server which you can see from the bottom of the screen sending a request etc with the message hi so you can see in the debug part at the bottom of the screen that this is working as expected we can also go to my console open another instance of godot in which i'm going to change my username so in request authentication i'm going to call myself test2 at test.com i'm placing the two windows next to one another and in one i'm going to say hello so you can see that these are two clients communicating with the server they have the same color but you can see the email addresses are different and in the second chat box i only get the new messages that come from other users of course you could get all the chat history by making the chat log persistent and sending it to every new user who joins the chat channel but that's it for the basics with that in the next video we're going to see how to round out our project by adding pop-up notification when a user joins the game world see you there in this last tutorial in our nakama getting started series we are going to see how to do notifications so look down there as i create a new client for the game gtquest test.com joined i'm going to place the window here if i log out i can remove that window and you can see that hello test.com left so we have two players and we can get notifications when a new player joins the game all leaves the game let's get started with that all right to get started we are going to add the notification list from the demo that you can find in the source ui components directory you can also use print statements as usual you do not need to use that demo scene but if you want to have the nice pop-up notifications you'll need that this little scene defines an ad notification method that you can call to add a pop-up notification with that i'm just going to add it as an unready variable in my scene which i like to do first a notification list and cache the node that way the path to the node and with that we can start working on serverconnection.gd going to close the other tabs so here we are going to define two signals just like chat message received that will emit when the user joins or leaves the match so signal user joined we're going to pass in the username and we're going to have a signal user left when the user has left the match to know which users joined or left the match we have to subscribe or connect to another signal on the nakama sockets class or object so go back to connect to server async and here after receive channel message we're going to duplicate that line and we're going to replace that but received match presence this is going to give us a list of the people who left and joined the match since the last server frame let's say okay so we're going to create a new callback method on the comma socket received match present as a callback for that and let's go to the bottom of the file to create that callback function this function is going to get presences from nakama a bit like the one we got in lua when we were defining the match interface the type of the new presences object is nakama rt api dot match presence event and this function is not going to return anything it's just going to emit signals now this new presences object has two properties leaves and joins and we can loop over these arrays of people who joined and left to emit the signal some user left some user joined for user in new presences dot leaves we are going to do two things we are going to emit our signal but first remember if i go back to the top of the script we had a dictionary of presences in our serverconnection.gdclass we want to update that whenever users join or leave so you can do it like so presences dot erase when someone leaves and you want to pass the key we're storing the user ids so we take user dot user every one of these presences has the user id the username and a bit more information you can print the array if you want to see that or use the debugger then we're going to emit the signal that a user left and we're going to send the user's username we have to do the same thing for users who join so for user in new presences dot joins we're going to add the corresponding key pair value to the presence's dictionary so user dot user id is equal to that user and we emit the signal user joined and we're going to pass the user's username and that is it for the server connection part now let's head back to the demo.gd script where we are going to do a few things so one thing we want to do is to go to the request authentication function where we stored the email and the password we didn't really store it we hardcoded it to be honest and we're going to move them up so just that we can check that we only show notifications when someone else joins or leaves the world and not when we or this client does so we're going to add the variables at the top of the file and note that you might well want to store that on server connection kind of encapsulate that for this demo that's how it is for now so we have these two properties at the top and then we need to connect to the two new signals of server connection click on server connection to select it head to the note tab and we're going to connect user joint to our demo node and go back to server connection connect user left to your demo node the functions should be added at the bottom of the file you could just print the username like that user joined and in our case we're going to use the notification list dot add notification and this one takes three parameters we have to pass the username we have to pass some color so we could say user color that we defined in the previous video and the third parameter is optional if you control click on add notification it is the user disconnected or did the user disconnect or leave so i'm not going to use it when the user joined but you can press ctrl d to duplicate that line and move it down to the userleft signal callback where we are going to add the third parameter and set it true we want to show that the user left the game we're going to check if the email is equal to the username that we get from the function then we will return from it and the username i should specify that this is a text string i realize that we haven't specified the types of the parameters in our callback functions normally you shouldn't see when you left the notification because in our case it's when we quit the game but in a complete game you might want to have the same kind of safety only emit the signal or display the notification if another user left the game or maybe you want to still emit the user left signal normally and let the game code handle it maybe you want to have some special ui or message when you the player the current client leaves a given game now to test that we have to open two different instances of the game with different credentials so i'm going to run the first instance here on the left and i have another instance of the editor of the project open i'm going to open demo.gd change the username or email to something else and run the game and move it down so now look at what happens when i quit the first window and i maximize the window we can see a pop-up that the other user left everything's working and that brings us to the end of this getting started with nakama series to go further i invite you to check out the nakamagodo demo repository that has lots of code comments and also a complete demo that's more complex than what we saw in the tutorial where you can just learn more and go further so in this one you will see some players you have the chat the ability to change colors create characters etc it's all free software and with that i want to thank you kindly for watching be creative have fun let's see one another in the next one bye
Info
Channel: GDQuest
Views: 29,638
Rating: undefined out of 5
Keywords: godot nakama, godot multiplayer, godot network, godot online, godot tutorial
Id: r3T_ED281vU
Channel Id: undefined
Length: 109min 45sec (6585 seconds)
Published: Mon Aug 10 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.