Making a Text Chat with Dedicated Servers using Unreal Engine 4
Overview
Today I'm going to be going over the process of making a text chat for multiplayer games using a dedicated server in Unreal Engine 4. This text chat should allow players that connect to a server to chat using a UI chat box, receiving and transmitting messages between each other. This project was made using Unreal Engine 4.18.2 with Blueprints (a C++ version may come later).
It's important to note that this will cover how to use a dedicated server and NOT a listen server. The process is slightly different and I'll explain exactly why in the next section.
Understanding the Unreal Engine 4 Multiplayer Framework
Before we get started, I think it's important to have a little background knowledge of Unreal Engine 4's multiplayer framework. The next few sections won't give you an in-depth understanding of the framework, but should allow you to at least understand what is happening and why things work the way they work in this tutorial.
Replication
Replication allows us to share variables over a network. When you replicate a variable it let's other players see changes that are made to that variable.
Dedicated Server vs Listen Server
Listen servers are when a player running the game is also the server that other players connect to. Dedicated servers are a standalone server which all players connect to. Dedicated servers DO NOT have a graphical representation of the game, so it is important not to write code like HUDs for servers. We can decide what code a server runs and what code a client runs by using the "Switch Has Authority" branching node, where any code run by the server connects to the Authority execution path and any code run by the client connects to the Remote execution path.
It's important for you understand the needs for your game and decide early which type of server you would like to use. The reason for this is because for dedicated servers you won't run certain code, but for clients you will. This will clash with a player who is the listen server since certain code won't run for them (i.e. user interface).
GameMode
The GameMode class allows us to define the "rules of the game". This could mean knowing what score a player needs to win or what characters and weapons can be used in the game. It is important to know that this class only exists on the server. That means objects that don't exist on the server don't have access to it. This also means clients that are connected to the server can't access it (directly).
GameState
The GameState class allows us to keep state (keep track) of the entire game. This class keeps a list of all connected players' PlayerState objects. The GameState class could keep track of all of the players' individual scores and then pass those scores to the GameMode. The GameState object exists on both the server and the clients, so it allows us to pass information between the two.
PlayerState
The PlayerState class allows us to keep state (keep track) of a specific player's information. This class exists on both the server and the client. It can be used to keep information such as a player's individual score, the player's user name, his clan ID, etc.
It's important to remember that the GameState class keeps an array of all of the connected players' PlayerState objects using the PlayerArray, which means we can pass information between the two easily and for other player s to interact with another's PlayerState object.
PlayerController
The PlayerController class allows us to handle input from the player. It is important to know that clients only know of their own PlayerController object and have no access to any other connected player's PlayerController object. In other words, there only exists one PlayerController object in any connected player's game and that is their own PlayerController object. The server, however, knows of everyone's PlayerController.
Alright. With that out of the way, let's get started.
Making the Text Chat
The following sections represent classes that I made in the Unreal Engine 4 editor. Each section will be headed with the file name I created for it followed by the type of object it is. For example if I created a widget blueprint class with the name MyWidget, the section would be titled MyWidget - Widget Blueprint. This corresponds to right clicking in the content browser and selecting User Interface > Widget Blueprint. If there is a colon, then that means the class inherits from that type, or is of the type given. There will be pictures of the blueprint graph, followed by a description of the object and what it is meant to accomplish.
SMessageData - Blueprint Structure
This structure allows us to neatly store a message and it's sender. There's not much to it.
Variables
Player Display Name : Text
The player's display name who sent the message.
Message Content : Text
The message's content.
As an omniscient programmer (through both trial and error and designing), I know that we will eventually hold an array of these structures on the server, where connected players will write messages to it and the server will send the messages to the connected players to display.
UI_MessageEntry : Widget Blueprint
UMG Elements
Message : Horizontal Box
Player Display Name (Is Variable) : Text
Seperator : Text
Message Content (Is Variable) : Text
Player Display Name will show our message sender's display name. Seperator contains just a colon to act as a seperator between the display name and message content. Message Content will show the message's actual message content.
Events
Set Message
Here we set the text elements to match the input message. The input for this event is (obviously) an SMessageData object. We again use Split Struct Pin to access the individual variables in the structure.
UI_ChatBox : Widget Blueprint
UMG Elements
Vertical Box : Vertical Box
Message Input (Is Variable) : Text Box
Message Feed (Is Variable) : Vertical Box
Message Input gives the user a place to type in text. Be sure to add the event On Text Committed by selecting this element, going to the Details panel, scrolling down to the Events section, and pressing the + button on the OnTextCommitted event. Message Feed will hold all of the messages that are created.
Events
On Text Committed (Message Input)
First we have to check if the type of commit was On Enter. This happens when the user presses Enter on the text box. We then get the owning player's BP_PlayerController and call the function Server Add Message, with the text from our event's input as input to this function. We then set the text of Message Input to empty, to avoid the user accidentally sending the same message multiple times.
Add Message Entry
This event has an SMessageData as input. We then create a UI_MessageEntry widget, set it's message to the input event's message using the widget's function Set Message, and the add the widgeet to the Message Feed as a child. Message Feed is a Vertical Box element, so any children of it will be stacked on top of each other nicely.
UI_HUD : Widget Blueprint
UMG Elements
UI_ChatBox
This is just a UI_ChatBox object that we place inside of the canvas. Place it wherever you'd like, making use of the anchor points and such.
BP_PlayerState - Blueprint Class : PlayerState
This object exists to set the player name that will be displayed in the text chat. It's non-essential for your design, since you probably want to either get the user name from Steam (or whatever platform you're aiming for) or allow the player to set it from the menu.
Variables
Player Display Name : Text
Holds the player's display name to be used in the text chat.
This variable is replicated (select the variable and in the Details panel change the Replication field to Replicated instead of None). The savvy UE4 user will say, "Ramon! The PlayerState class comes with a built-in PlayerName variable!" You're right! It absolutely does! Unfortunately we can't change this variable in blueprints, so we need to create another variable for use in blueprints. If you were in C++ this wouldn't be a problem.
Events
Set Player Display Name
This is the function that sets the name. In my particular case I don't pass input to it. Instead I get the game state and append the amount of players in the game to the string "client" and set that as the Player Display Name. The idea behind this is that as players join they will get the display name client#.
It is important to note that this function is called in GameMode after a player joins the server. This is because PlayerState will be initialized on the server, and as such, if we used BeginPlay it would get all wonky trust me (or don't and try for yourself, I may end up being wrong).
BP_GameState - Blueprint Class : GameState
Variables
Chat Messages : Array of SMessageData
This contains all of the messages that have been sent from the clients.
For this variable we need to set the Replication to RepNotify. This gives us a function (shown below) that is called whenever the variable is changed.
Functions
OnRep_Chat Messages
This function is automatically created when we set the Chat Messages variable's Replication to RepNotify. In it we let the player controller know that a new message was added from somewhere and to display it in their HUD.
Events
Add Chat Message
This event takes a SMessageData object as input. We then tell the server to add the message to the Chat Messages array, which will in turn invoke the OnRep_Chat Message function.
BP_GameMode : Blueprint Class : GameModeBase
Events
OnPostLogin
This event is inherited from GameModeBase, so you should be able to override it in the Event Graph. All we do here is call the new player's BP_PlayerState function to set the display name.
BP_PlayerController : Blueprint Class : PlayerController
Variables
HUD : UI_HUD
This is just a container for our HUD we will display to the player.
Events
OnBeginPlay
Firstly, we make sure the code is run only on the remote client (since we're dealing with a dedicated server that doesn't have a graphical representation of the game). We set the cursor to be shown. Then we create a UI_HUD widget and set it to the HUD variable. We then display it on our player's viewport.
Server Add Message
First, set this event to replicate and to only execute on the server by click on the event node, going into the Details panel and set Replicates to Run on Server and be sure to check that the event is Reliable. The input of the event is a Text object that contains the message data. We then get the GameState and call the function Add Message from it. Since the input for Add Message is a SMessageData object, we need to use Split Struct Pin by right clicking the input and selecting the appropriate option. We pass the Player Display Name from our PlayerState and the input message into the Add Message function.
That's it!
That's it! I think I got everything and I hope I explained it all easily enough to follow. There are some limitations to this implementation of the Text Chat that I'll be going over in the next section, as well as some possibile improvements and speculations as to how they could be implemented. I hope you enjoyed reading this and learned something from my efforts. Multiplayer stuff is fascinating and also difficult, so pass on information as you come across it. Hopefully I can post some more articles in the near future!
Wrapping Up
Limitations & Improvements
This project works great! You can send messages amoungst any number of clients using a dedicated server! Exciting!
But what if there are too many messages? Here we run into our first problem. With the current UI_ChatBox, we just continually add UI_MessagEntry objects to a Vertical Box. This will just continue until the messages go off screen. A simple fix to this would be changing the Vertical Box to a Scroll Box.
Okay... But what about memory constraints? Adding messages will eventually make our Chat Messages array in GameState consume a very, very large amount of memory. There's a couple of ways we can tackle this problem. I'll provide two possible solutions, starting with the one I wouldn't use. The solution I wouldn't use is giving each message a lifetime. After their lifetime has expired they get deleted and removed from the Chat Messages array. The reason I wouldn't use it is because it seems complex to remove an element from an array after a certain amount of time. I mean, we could figure it out, but why would we? There are better solutions. The one I have implements in my test project before this was one based on a maximum number of elements allowed in the array. Basically we implement a circular array. When we add a message, we check if the if the maximum number of messages is already in the array. If there isn't then we simply add the message. If the max is already reach, we remove the element from the UI, remove the first element (oldest message) from the array, and then we add the new message to the array. This is very easily implemented with what we went over in this article, so I propose you try to implement it yourself! It'll be fun, I promise (at the very least it will build expand on your knowledge and be satisfying when it works).
Goodbye!
Thanks again for reading through this guide. I hope everything is documented correctly and you're able to get it working in no time! If you do have any problems or questions, or spot a mistake with any of the information in this article, please contact me. Good luck!












