A Hat in Time Guide

A Hat in Time Modding: Online Party Support for A Hat in Time

A Hat in Time Modding: Online Party Support

Overview

Teaches the fundamentals of Online Party Mods!

Introduction

So you’ve made a fully functioning mod, but you’re interested in taking things to the next level. How do you do that, you might ask? Online Party Support! Online Party is an awesome way to make a wide array of mods more engaging and unique, even if it has been shrouded by a bit of mystery in the modding community.

This guide will take you through the basic concepts of creating a mod that supports Online Party. Everything contained within the guide will take place within scripts, so be warned that at least a decent working knowledge of scripting will be required to apply these concepts; fundamentals of scripting unrelated to OP Support will not be covered.

HOLD UP: Is This the Online Support You Need?

Alright! You’re ready to get started! ..Or wait? Are you? It’s important to actually consider whether your mod actually requires Online Party Support in the first place, or if this is even the right kind of support for the mod you want to make. Here’s some examples:

Is your mod a…

Sticker or Emote: In general, no! The only exception is if you are using a custom animation, which must be synced!

Dye, Pattern Dye, or Material Dye: Nope! The game will handle these for you.

Costume, Flair, or Weapon: Yes! However, these require something different than what will be discussed in this guide!

Custom Hat: Yes! You’ll have to sync things that are unique to your mod’s hat.

Mod Level: Generally, it’s not necessary! However, some levels (like Temperature Clash) can do unique things with Online Party Support!

New Feature/Gamemode: Definitely! Some examples of this are a few of my own mods (Level Compilations).

Something Totally Different: If you’re making something else entirely, the answer is likely yes, as long as there is some possibility of interaction between players! Some mods, such as Online Party Expressions, are created specifically for adding support to something not covered by the base game.

Fundamentals of Online Support

One way to visualize Online Support is imagining sending a message to a group in an app like Discord: You have an account which sends your message, a “channel” to send it through (like #general), and then the accounts of other server members which can then see your message in the channel.

Online Party is exactly the same way. Our account in this case is the GameMod class, which provides functions for sending and receiving Online Party messages through specific channel names.

Here’s the functions we’re interested in, located in GameMod

native final function SendOnlinePartyCommand(string InCommand, optional Name CommandChannel, optional Pawn SendingPlayer, optional Hat_GhostPartyPlayerStateBase Reciever); event OnOnlinePartyCommand(string Command, Name CommandChannel, Hat_GhostPartyPlayerStateBase Sender) {}

SendOnlinePartyCommand sends messages to other players in the same lobby as you, as long as they have your mod installed (doesn’t have to be the same map).

OnOnlinePartyCommand receives messages from players who use SendOnlinePartyCommand, these messages can then be interpreted and used to sync clients.

We’ll go into detail on these functions and their inner workings in the following sections.

Online Support Functions

One of the first things you need to consider for your Online Support is, “when do you need to send messages to other players?” All that means is you need to know when the mod should actually update something for other players to sync.

We do this through SendOnlinePartyCommand which has a few parameters you need to know first.

native final function SendOnlinePartyCommand(string InCommand, optional Name CommandChannel, optional Pawn SendingPlayer, optional Hat_GhostPartyPlayerStateBase Reciever);

InCommand – This is the key component of our text message analogy from earlier, it’s the data we send to other players in the form of a string. This can be as simple as “0” or “1” or even “hello” but can also get into much more complicated things such as “1|1.5+3|EZ+FP” using what we call delimiters. We’ll go into more detail on this later.

CommandChannel – This is the equivalent of the Hat discord’s #modding-general, it’s a name to distinguish what “channel” a sync message should be read in. Similar to strings, these can be just about anything, such as ‘UpdatePlayer’, but usually they will be jumbled text like a password. We’ll use a special file for this to hold our channels, keeping them secure is important if you don’t want people abusing your mod’s client syncing.

SendingPlayer – This is to distinguish who this message is being sent from, it’s relevant if your mod needs to support local coop players.

Receiver – This is to target a specific player to send your message to, if it’s left empty the message will be sent to the everyone in the lobby.

——————————————————————————–

The next thing you’ll have to consider is “what do I need to do when I receive a message?” This is the interpretation portion, where we transform sent data into actual syncs in the lobby.

We use OnOnlinePartyCommand for this.

event OnOnlinePartyCommand(string Command, Name CommandChannel, Hat_GhostPartyPlayerStateBase Sender) {}

Command – This is the string that another player sent to you as InCommand that will have to be interpreted.

CommandChannel – This is the channel the sync should be used under, it’s important that the channels are the same when sending and receiving a message for a particular sync.

Sender – This is the player state who sent the message to you, from this we can get the player’s actor, called a Hat_GhostPartyPlayer.

Command Strings and Delimiters

Now that we know the functions, we need to take a closer look at our Command String, which is integral to making the entire thing work.

There are 2 steps to this, you need to be able to encode your information into a string, and then be able to decode your string back into information.

In some cases, this is extremely simple. An example of that is sending a particular config value to other players, which would just involve casting that value into a string. Here’s the process for that.

Sending the Config Value

function SendConfigValue() { local String s; s $= ConfigA; SendOnlinePartyCommand(s, ‘CommandChannelA’); }

This could be reduced to just the SendOnlinePartyCommand line, but we’ll be expanding this concept in the next portion.

Receiving the Config Value

event OnOnlinePartyCommand(string Command, Name CommandChannel, Hat_GhostPartyPlayerStateBase Sender) { local Hat_GhostPartyPlayer GhostPlayer; GhostPlayer = Hat_GhostPartyPlayer(Sender.GhostActor); if (GhostPlayer == None) return; //The GhostPlayer will be None if the player is in a different map; you can omit this check if you want to send syncs to the entire lobby. if(CommandChannel == ‘CommandChannelA’) { DoStuffWithConfigValue(int(Command), GhostPlayer); } }

Our Command String will contain exactly what we sent in it: the value of ConfigA! This can then be casted back into an int and we can use it from there!

This works for really simple things, but what about having more information at once?

Delimiters

Delimiters are nothing more than symbols that we dedicate to separating independent information. They’re a very useful organizational tool that allows us to send and receive a wide variety of different things.

Examples of Delimiters include + | ? / = but you can technically use any character or string of characters as long as whatever you use is NOT part of the information itself.

With this in mind, here’s how you would approach sending multiple config values at the same time.

Sending the Config Values with Delimiters

function SendConfigValue() { local String s; s $= ConfigA; s $= “+” $ ConfigB; s $= “+” $ ConfigC; SendOnlinePartyCommand(s, ‘CommandChannelA’); }

Our Delimiter in this case is +. The string we send out ends up looking something like “1+0+2”.

Receiving the Config Values with Delimiters

event OnOnlinePartyCommand(string Command, Name CommandChannel, Hat_GhostPartyPlayerStateBase Sender) { local Hat_GhostPartyPlayer GhostPlayer; local array<string> arr; //This will be used to split our information up GhostPlayer = Hat_GhostPartyPlayer(Sender.GhostActor); if (GhostPlayer == None) return; if(CommandChannel == ‘CommandChannelA’) { arr = SplitString(Command, “+”); //Separate by the Delimiter //This array contains each of our bits of information in a separate element which //we can then convert to utilize in our code! if(arr.length == 3) { //It’s good practice to confirm the array length DoStuffWithConfigValue(int(arr[0]), int(arr[1]), int(arr[2]), GhostPlayer); } } }

The function SplitString allows us to divide the string by our delimiter.

You can even use multiple delimiters at once! If you want an example, check out The Ukulele mod which sends and receives Songs through this process!

Using Command Channels

Command Channels allow you to better organize your Online Party syncs by giving them distinct names and purposes. You can use any number of them, but generally you want different types of syncing to use different command channels!

Sending to Different Command Channels

function SendToCommandChannelA() { local String s; //… SendOnlinePartyCommand(s, ‘CommandChannelA’); } function SendToCommandChannelB() { local String s; //… SendOnlinePartyCommand(s, ‘CommandChannelB’); }

It’s as simple as changing the name, not much to it!

Receiving Multiple Command Channels

event OnOnlinePartyCommand(string Command, Name CommandChannel, Hat_GhostPartyPlayerStateBase Sender) { //… if(CommandChannel == ‘CommandChannelA’) { //… } if(CommandChannel == ‘CommandChannelB’) { //… } }

You can treat each sync differently in this way, divided into groups. A good way to save space is having your SplitString function call before the Command Channel checks, especially if you plan on using the same delimiter!

Command Channel Files

If you peek into basically any Online Party mod’s script files, you’ll probably notice that their Command Channels are not like you see here. They all use a separate file to store their commands and if you would have actually had the chance to peek at them, you probably would just find a big mess of random characters.

This is because Online Party Command Channels are important to keep safe, it’s possible for people to abuse the syncs of your mod if they can find or figure out your command channels. It’s never possible to 100% hide your command channels, but this section is still very important to prevent easy access.

An Example Command Channel File

class YourCommandFileClass extends Object; //Using const variables const CommandChannelA = ‘Vsrd5jc2rxR3zvVAMSSZEmWae’; const CommandChannelB = ‘xK3U8jAFEwwGYcVUrU9tTZhBG’; //Using defaultproperties var const Name CommandChannelC; defaultproperties { CommandChannelC=”qFtSKba7N2hQEqA3ZFzKdZu4X” }

Here, we’re dedicating a separate script file to store the actual “names” of our Command Channels. You can choose between using const variables or variables defined in defaultproperties, I’ve heard defaultproperties can be more beneficial to hide them, but as stated earlier they are never 100% secure. The variable name of your Command Channel should describe what it’s used for.

Using the Command File in your Online Party Commands

function SendToCommandChannel() { local String s; //… //Using a const variable SendOnlinePartyCommand(s, class’YourCommandFileClass’.const.CommandChannelA); //Using defaultproperties SendOnlinePartyCommand(s, class’YourCommandFileClass’.default.CommandChannelC); }

The only difference is we now refer to the variable inside our script, rather than specifying a particular name ourselves! This same process works for receiving commands as well.

Things To Note

  • When compiling the mod, it’s necessary that your Command Channel File is present in the Classes folder. After compiling and cooking your mod, remember to REMOVE the Command Channel File before the mod is uploaded or updated on the workshop and to STORE IT so you don’t lose the file.
  • Command Channels are NEVER 100% secure but this process is still important to decrease their visibility.
  • You can create your crazy string of characters for Command Channels by searching online for a “random password generator” or a “random string generator.”

The Syncing Process

We have all the components now to make something happen, but what do you actually do with it? That’s not the easiest question to answer, because it entirely depends on what you want to sync. I can’t tell you how to sync absolutely everything, but I can point you towards a few files that may be of useful reference! Most things that can happen locally to a player can also happen to a player in Online Party, although there may be differences in how they are achieved in practice.

Relevant Online Party Classes

Head to …HatinTimeDevelopmentSrcHatinTimeGameContentClassesGhostParty and consider checking out these files for ideas!

  • Hat_GhostPartyPlayer
  • Hat_GhostPartyPlayerState
  • Hat_GhostPartySettingsActor
  • Hat_GhostPartyEmote

And you can also check …HatinTimeDevelopmentSrcHatinTimeGameClassesGhostParty to look at some of the base class files.

  • Hat_GhostPartyPlayerBase
  • Hat_GhostPartyPlayerStateBase
  • Hat_GhostPartyEmote_Base

Another place is HUD …HatinTimeDevelopmentSrcHatinTimeGameContentClassesHUD.

  • Hat_HUDElementPlayerList
  • Hat_HUDMenuGhostPartyScoreboard
  • Hat_HUDMenu_GhostPartyLobbies
  • Hat_HUDMenu_GhostPartyPing

Don’t be afraid to ask for help if you can’t figure out how to actually make your syncs work!

Playtesting and Debugging

It is absolutely vital to playtest an Online Party mod with other people. It’s just impossible to know how things work (or don’t work) otherwise! Try and find a few who can help you out, it’s all part of learning how to mod!

Debugging is especially important during this process too, you really want to double check what exactly your mod is sending and receiving, and when.

This is a good Print function to add to just about any mod you’ll ever make, Online Party or not!

static final function Print(const string msg) { local WorldInfo wi; wi = class’WorldInfo’.static.GetWorldInfo(); if (wi != None) { if (wi.GetALocalPlayerController() != None) wi.GetALocalPlayerController().TeamMessage(None, msg, ‘Event’, 6); else wi.Game.Broadcast(wi, msg); } }

Consider having debug messages when an Online Party Command is sent and received, including information about what type of channel it was for and the actual Command it sent across.

Putting It All Together

We’re finally at the point where our Online Support can be completed, so I’ll leave you with a super short example mod that includes some of the concepts we’ve discussed here!

GameMod

//Created by EpicYoshiMaster // // This Example mod plays a “FOOL” death sound effect in Online Party. class Yoshi_GameMod_OnlinePartyExample extends GameMod; var SoundCue DeathSound; //Debug Print function static final function Print(const string msg) { local WorldInfo wi; wi = class’WorldInfo’.static.GetWorldInfo(); if (wi != None) { if (wi.GetALocalPlayerController() != None) wi.GetALocalPlayerController().TeamMessage(None, msg, ‘Event’, 6); else wi.Game.Broadcast(wi, msg); } } //GEI Function function OnPlayerDeath(Pawn Player) { local string s; s = “NothingImportant”; //The Command String isn’t important for this sync SendOnlinePartyCommand(s, class’YoshiPrivate_OnlineParty_OPExampleFile’.default.PlayerDied, Player); Print(“[OPExample] Sending Player Death:” @ Player.Name); } event OnOnlinePartyCommand(string Command, Name CommandChannel, Hat_GhostPartyPlayerStateBase Sender) { local Hat_GhostPartyPlayer GhostPlayer; GhostPlayer = Hat_GhostPartyPlayer(Sender.GhostActor); if(GhostPlayer == None) return; if(CommandChannel == class’YoshiPrivate_OnlineParty_OPExampleFile’.default.PlayerDied) { Print(“[OPExample] Received Player Death:” @ GhostPlayer.Name); GhostPlayer.PlaySound(DeathSound); } } defaultproperties { DeathSound=SoundCue’HatinTime_Voice_HatKidApphia4.SoundCues.VA_Hatkid_OnlineEmotes_Fool’ }

Command Channel File

class YoshiPrivate_OnlineParty_OPExampleFile extends Object; var const Name PlayerDied; defaultproperties { PlayerDied=”8uPu6rCJYajEpyXy2m7rEKaYW” }

Conclusion

Thank you so much for checking out this guide, if you have any questions feel free to leave them in the comments or to ask in the Hat Discord!

I’d like to give a huge thanks to yet another Online Party modder, Don, for proofreading this guide, and to an amazing artist, https://twitter.com/FMOptimist, for making the icon for this guide!

Good luck making Online Party Mods!!!

SteamSolo.com