-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Basic Netcode
Looking to learn how to write and send your own ModPackets? Read our intermediate netcode guide.
Terraria is frequently played with many players on a server. Ensuring that your mods work well with friends is something that should not be an afterthought. Test early and often in multiplayer as you develop your mod. In this guide, we will go over how to make your mods multiplayer compatible, and hopefully show you it is not as difficult as it may seem.
The following are main concepts key to understanding how Terraria works in multiplayer.
- When you play Terraria, your application is considered and called a "Client"
- During multiplayer, your client and other players' clients connect to a "Server"
- While connected to this server, the connected clients need to communicate well together through the server, so that things happening in the game are synchronized between these clients.
In essence: network packets are sent across clients to make sure the game is in sync for all players. This happens all the time in multiplayer play, even in vanilla. Clients CANNOT send or receive things DIRECTLY from other clients - the server has to act as a middleman.
You can test multiplayer by hosting a server and having others join your server. The mod should automatically be synced from your computer to them even if you do not update version in build.txt. You do not need to publish to the workshop each time to test like this since the mod is coming directly from the server.
If you want to test alone, you can do this be launching 2 tModLoader clients on your computer. Usually Steam will only allow you to launch the game once, but nothing prevents manually launching the game a 2nd time. To launch an additional client, open up the tModLoader install directory and double click on the start-tModLoader.bat
file (.sh
for non-Windows users). This will launch the 2nd client. You'll want to be in windowed mode instead of fullscreen. From there, have the first client "host and play" and use "join via IP" on the 2nd client. Use localhost
as the IP address on the 2nd client.
If you need to debug code running on the server, you'll want to launch the debugger but select the "Terraria Server" launch target instead of "Terraria". If you do this, you'll need to manually launch 2 clients to connect to that server via "localhost".
Do note that with 2 clients, the game might not update as frequently when the program is not in focus, so you might need to ignore certain apparent issues.
Terraria handles many network syncing things for us, we just have to use them right.
With NPC, any non-deterministic decision must be synced to the clients. The Server is the owner of all NPC. As seen in ExampleCustomAISlimeNPC.cs, we use npc.netUpdate
to trigger the NPC syncing code. Terraria will sync position, life, and other data from the server to the clients whenever npc.netUpdate
has been set to true. We can use ModNPC.SendExtraAI and ModNPC.ReceiveExtraAI to sync extra data needed by all clients rather than ModPacket
. This extra data will always be synced whenever the npc itself is synced.
Projectiles operate the same way as NPC, with the exception that the owner of a projectile is not always the Server. Projectiles spawned by players are owned by that player/client, while all other projectiles such as NPC spawned or World spawned projectiles are owned by the server. In the case of client changes, the projectile.netUpdate
flag will sync the client's data to the server and the server will relay that to the other clients. An example is Magic Missile. The AI will check if (Main.myPlayer == projectile.owner)
and then set projectile.netUpdate = true;
if the position has changed due to the client players mouse position.
World data should only ever be changed on the server. Whenever something important happens in the world, such as the Noon command on the server console, a Boss being defeated, or a random invasion triggering, World data is synced from the server to the clients. Whenever world data is synced, each ModSystem
is also synced via NetSend and NetReceive. We can trigger a network sync of world data by calling NetMessage.SendData(MessageID.WorldData);
on the server, as seen in Abomination when we set ExampleWorld.downedAbomination = true;
. Try to only do this when necessary.
Items are also synced. Use NetSend
and NetRecieve
to sync data. Follow decompiled source code if you need to do anything unorthodox, most mods won't need to do anything such as triggering manual syncing. When Items are synced, only a very select subset of the fields are synced, this is why changing default values such as Item.damage
in something like UpdateInventory
is not recommended. Use ModifyWeaponDamage
for that.
Items sync automatically in most relevant situations, such as when the item is dropped or transferred, but one situation that may require special consideration is when an item changes while in a players inventory that will have an effect on how the player behaves or how the item behaves when used. Without the syncing, the item could behave differently when observed by other clients. The Item.NetStateChanged();
method can be called to trigger a resync of this inventory or equipment item. UseStyleShowcase.cs shows this in action. As the local code changes how the item behaves, the code ensures the changes are synced so that the behavior is consistent for all clients.
Owned by the server. Only sent to client when the client first visits the "chunk"/"section" of the world where it resides. Changes are applied on the server and synced from the server to client. See TEScoreBoard for an example.
Player data is very large, so special data syncing is used to minimize how often and which Player data must be synced. See SyncPlayer
, CopyClientState
, and SendClientChanges
in the ModPlayer Documentation and ExampleStatIncreasePlayer to learn their purpose and see them in use. Vanilla Player
data such as inventory slots, health, position, and selected item are all synced automatically, so changing those in code will automatically sync to the server and be relayed to the other clients. Biome flags will be synced automatically. Failure to use SyncPlayer and SendClientChanges will lead to desync and many other problems, they are very important to get right.
Many examples can be found in ExampleMod for packets. For example you can use the Notepad "Find in files" feature and look for GetPacket()
in the ExampleMod folder. Make sure 'Match case' and 'In all sub-folders' are checked. You'll find all locations in which a packet is created and likely sent.
If you feel you want to delve even deeper to learn how to send ModPackets and optimize your networking more, read our intermediate netcode guide.