UPDATE 6/15/2021: This method no longer works because Valheim updated the game code. New method is here: Revisiting Fixing Valheim Lag – Modifying Send Receive Limits
Valheim is a really fun game that recently entered early access where you are basically a Viking in purgatory trying to get to Valhalla. It honestly works pretty well (I would say especially compared to recently ‘AAA’ titles like Cyberpunk) for an early access title. Unsurprisingly though there are definitely still some issues and a very common one is lag on a dedicated server.
This guide will show some methods you can use to help reduce lag on your Valheim dedicated server!
If you are looking for how to back up your local game client’s world or characters, check out my guide here!
Symptoms of lag on Valheim dedicated servers
The symptoms you are experiencing on the server can help narrow down what type of lag you are experiencing and what may be causing it. Lag caused by your CPU being too overloaded for example will have different symptoms than lag that is caused by your server hitting the maximum send/receive limit as an example.
By far the most common type of dedicated server lag seen in the early access Valheim release so far has been mainly due to the low send/receive limit if 64KB/s that is hard coded into the server. This means that when you do something like take your character a heavily populated area on the server it has to send all that information about all the buildings/players through an artificially limited very narrow 64KB/s pipe.
Symptoms of the send/receive limit type of lag include things like chests taking a very long time to open, sometimes 10-30 seconds or never opening while at the same time being able to see players walking around normally. This is because the other people’s X / Y / Z positions is a very small amount of data compared to all the items/names/attributes that are stored in the chest. The other players moving easily fits through the narrow pipe while the chest full of loot’s data chokes since it is much larger.
I’ll specifically lay out how to modify your server and increase this limit in the “Modifying your server’s send/receive limit” section below. Before that though let’s cover a more specific type of lag to watch out for (mostly by being avoided) related to terrain manipulation.
Avoid extensive terrain manipulation (for now)
Another commonly observed source of lag has to do with terrain manipulation. Some players have found that if they take a big chunk out of the ground in Valheim and make extensive terrain modifications that the area gets filled with separate “instances” of terrain for each modification people make. Think of the original ground as one big piece in the game files. Valheim has the very cool feature of letting you modify terrain by raising it/lower it/level it/etc. Each time you do this though the game has to store what you did as a new “instance” or a modification.
The way Valheim seems to work now is it renders that original piece and then the modifications afterward. Early reports suggest having a whole bunch of them in one spot seems to sometimes cause everyone’s in-game FPS on the server to drop as low as 40 when the clients normally are much much higher than that.
The best way to avoid this type of lag at the moment is to limit how much terrain modification you are doing in areas near your base. It is likely this will be addressed/eased in future patches. There should be some things the developer team can do to clean up/merge these instances or at least reduce their effect on the rendering process so it doesn’t drop the in-game FPS so dramatically.
Modifying your server’s send/receive limit
At this time there is no options file for Valheim unfortunately. The only way to change it is to modify the server’s code and recompile the module that controls this.
Fortunately this is a lot easier than it sounds because the module we need to change is in .NET instead of something like C++ which would be much more difficult to do this with. I did not come up with this method, a very clever user on reddit (maximgame) shared it here and I want to give credit where credit is due!
Despite it not being too difficult these modifications are at your own risk. I haven’t experienced any problems so far but technically we are modifying the server’s code with the following procedure so understand that this isn’t an officially supported method and could have side effects or if you make a mistake could cause serious problems. Make sure you have backed up everything (your game world files especially) before you try anything!
Another important note is that it’s very likely you will have to make the changes again each update since the file we are going to modify is going to get replaced by updates. Until they add either a launch option or options file to officially change/control this property that is unfortunately unavoidable.
With all that being said if you understand the above and still feel comfortable let’s proceed!
Get dnSpy utility
We are going to use the utility dnSpy to modify the server code and recompile the module. The utility is free and is available here (download the Win64 build or dnSpy-net-win64.zip): GitHub Official dnSpy Page
Extract the archive and run the program dnSpy.exe from inside the folder you extracted. The dnSpy application will open.
Modifying server with dnSpy
We are going to go to the “File” menu in the top left and choose “Open” like this:
Now you need to navigate to the Valheim dedicated server folder which is located in your “steamapps” folder. If you left all the options default when you installed it’s typically “C:\Program Files (x86)\Steam\steamapps\common\Valheim dedicated server\valheim_server_Data\Managed”.
We are looking for the file “assembly_valheim.dll” in that folder:
Select the file “assembly_valheim.dll” and click “Open” and the server’s assembly will load into dnSpy.
Once it has finished loading we are going to go to the “Edit” menu and click “Search Assemblies” like this:
For the search criteria enter “ZDOMan” like this:
After the search completes you should see a list of results like the one above. Double click on one of these entries on the list and it should place you in the ZDOMan section of the assembly (seen on the left hand side of the screen):
Perfect. We are now in the right place to make the changes. We just need to locate the line that has the send/receive limitation. To find that press Ctrl+F to bring up the find box (or choose “Edit” -> Find from the menu) and search for “m_dataPerSec =” which should bring up the following line:
Now right click on the m_dataPerSec line and click “Edit Class (C#)…”. Another window will open that will let you make changes to the file. We are going to modify ‘61440’ to a variable of our choosing. Common choices are to double it or triple it or to add a 0 to the end. I added a ‘0’ to the end of the existing number for my server making it 614400.
Theoretically setting a limit too high could cause the server to swamp your internet connection if you have a large number of players on and there’s essentially no limit but there shouldn’t be any other effects of a very high number.
Once you’ve changed the number to the desired value we need to press the “Compile” button in the bottom right corner of this new window. This window will close and take you back to the previous one.
Saving the new assembly
It’s time to save our changes into a new .dll file. Choose “File” -> “Save Module…”:
Change the file name to assembly_valheim_modded.dll. Don’t overwrite the original file just yet as we will want to take a backup and make sure that the server is closed before we do that. Press “OK” to write the new assembly_valheim_modded.dll file.
Backing up original file and replacing with our new one
Now it’s time to back up the original file and replace it with our new modded file. First make sure your dedicated server is closed otherwise the dll file will be in use. It is also a good time to make a backup of your world file just in case (see my Valheim Dedicated Server Backup Location / Guide for instructions)
Navigate in a file explorer window to your Valheim server directory where we saved the new assembly file (usually C:\Program Files (x86)\Steam\steamapps\common\Valheim dedicated server\valheim_server_Data\Managed).
First make a copy of the original assembly_valheim.dll file. You can just select the file and “copy” then paste a copy of it in that same folder and Windows will make a “valheim_assembly Copy(1).dll” file for you or you can back it up to a separate folder. Now remove the original file and rename your “valheim_assembly_modded.dll” file to the original file name of “valheim_assembly.dll”.
That’s it, you’ve now replaced the old assembly with the new one! Go ahead and start your server normally and the changes will have taken effect.
(Optional) Modifying Clients (not recommended unless absolutely necessary)
Note that the game client also has rate limits so the server is only half the picture. It’s in the exact same file only instead of the dedicated server folder it is the client. The path on an install with default paths would be something like:
C:\Program Files (x86)\steamapps\common\Valheim\valheim_Data\Managed\assembly_valheim.dll
and each player would make the same modification as you did with the server to this file.
I did not do this on my server as I found the performance gained from lifting the limits on the server side to be enough of an improvement. It alleviated the congestion / eliminated the chest lag problem completely on my server. It would also be a royal pain to have every single player have to do this (and remember, again for every single update since those files will get replaced). However, if you know you are doing something “extreme” like having full server with 10 people building a massive fortress that takes up an entire valley / a crazy giant farm with lots of livestock / anything like that with a huge amount of entities it may actually be worth the effort to modify the clients in that case.
I haven’t heard of modifying game clients triggering VAC (Valve Anti-Cheat) on Valheim yet but remember you are in fact modding the game client when you run this procedure on the client side and this is often not allowed and actively enforced with frequent ban-waves on many games today such as competitive multiplayer titles. I really recommend against doing it because of this risk (if not now then possibly in the future) unless you’ve tried the server modifications and it isn’t enough or you’re doing something really extreme and know you need it and understand the risk!
Other Resources
It’s also important to back up your local game’s characters and local worlds if you are using them. I have written a guide for the local game side that is available here!
Other Valheim Lag Issues
If you know of other Valheim lag issues that I didn’t cover here or are still having problems let me know in the comments and I will update this guide with other tips/tricks and try to answer any questions. It’s still a very new game and there isn’t very much out there for it yet. Have fun and enjoy!
Hey James,
So I just found this fix for valheim and tried to do it for my server, I discovered that the integer no longer exists and I cannot find any information on how to otherwise change the speed of the server. I saw that someone had mentioned this on the reddit post but no one has replied to it though it has been upvoted a few times showing that others are experiencing the same problem. Did you have an alternate way to fix this? maybe another variable that has to be modified now instead? Hope you can help
Thanks
Hey TheTwiddler,
I think I figured out what they did. They seemingly hardcoded some of this stuff in here. I looked at the old code which was like this:
// Token: 0x06000867 RID: 2151 RVA: 0x0004506C File Offset: 0x0004326C
private int SendZDOs(ZDOMan.ZDOPeer peer, bool flush)
{
if (!flush && peer.m_peer.m_socket.IsSending())
{
return 0;
}
float time = Time.time;
this.m_tempToSync.Clear();
this.CreateSyncList(peer, this.m_tempToSync);
if (this.m_tempToSync.Count <= 0) { return 0; } int num = this.m_dataPerSec / 20;
ZPackage zpackage = new ZPackage();
ZPackage zpackage2 = new ZPackage();
int num2 = 0;
for (int i = 0; i < this.m_tempToSync.Count; i++)
Notice the m_dataPerSec / 20. Now here's the new code:
// Token: 0x060007D2 RID: 2002 RVA: 0x0003D744 File Offset: 0x0003B944
private bool SendZDOs(ZDOMan.ZDOPeer peer, bool flush)
{
int sendQueueSize = peer.m_peer.m_socket.GetSendQueueSize();
if (!flush && sendQueueSize > 10240)
{
return false;
}
int num = 10240 - sendQueueSize;
if (num < 2048) { return false; } this.m_tempToSync.Clear(); this.CreateSyncList(peer, this.m_tempToSync); if (this.m_tempToSync.Count == 0 && peer.m_invalidSector.Count == 0) { return false; } ZPackage zpackage = new ZPackage(); bool flag = false; if (peer.m_invalidSector.Count > 0)
{
flag = true;
zpackage.Write(peer.m_invalidSector.Count);
foreach (ZDOID id in peer.m_invalidSector)
{
zpackage.Write(id);
}
peer.m_invalidSector.Clear();
}
else
{
zpackage.Write(0);
}
float time = Time.time;
ZPackage zpackage2 = new ZPackage();
bool flag2 = false;
int num2 = 0;
while (num2 < this.m_tempToSync.Count && zpackage.Size() <= num)
Completely different looking. I took both code samples down to where they start the while() loop that enumerates through m_tempToSync.Count. It looks like they attempted to hard code their 10240 value in there as well as add some safety checks / limits to avoid people making it larger than that number they want or too small.
So according to this though this code will still limit it to 10240 no matter what your send queue size variable is set to. The old code divided m_dataPerSec / 20 for the calculation and this was removed entirely and replaced with this hard coded limit. I would say off the top of my head to adjust that hard coded 10240 value and raise that! You would do the exact same process of recompiling the dll and replacing it but instead of changing the m_dataPerSec variable you would change the 10240 variable.
I'll do some experimentation with this and update the guide if it's working but wanted to give you my initial thoughts!
I’m starting to dig into this to solve a lag issue (suspected) on my dedicated server. Did you end up finding a safe setting for the “magic numbers” in here?
From the article above I’d assume that 102400 (10x more) for the first magic number would follow. But should following 2048 be expanded to 20480 or left alone?
Thanks for your wizardry here 🙂
Hey Sam,
So I’ve been holding out for the Hearth and Home update and haven’t been actively on for a bit but that is a fantastic question. The old formula used to be m_dataPerSec / 20 so with the default numbers it was 61440 / 20 = 3072. This new formula doesn’t do any division this time so 3072 was the previous stock number and can be compared directly to sendQueueSize. With our old numbers increasing it by 10 would have been an equivalent of 614400 / 20 = 30,720.
These number changes mean that they effectively raised the send/receive limit from what it used to be at 3072 to 10240 (that’s the sendQueueSize which is basically the replacement for m_dataPerSec) or an increase of about three times the previous value. The previous value was incredibly low though (so low almost every server had the chest lag and other bandwidth restriction related issues) so even tripled it’s still not that high and you may run into send/receive limiting still with the stock number.
If you want (almost) exactly the same behavior as increasing it by a factor of 10 was before set it to 30,720. This is the apples to apples comparison number once we’ve converted everything in the formula for a 10x. There shouldn’t be too much risk of anything bad happening from setting it too high. Basically what this code is saying is “if the flush variable isn’t set to true and the sendQueueSize > 10240 don’t send these updates right now”. The flush variable would be set to true for critical updates they want sent immediately no matter what is in the send queue. Other things it depends what it is, some of them are probably stored in the send queue and others are probably dropped altogether that are frequently resent and they can be confident they’ll get another one soon.
By increasing the sendQueueSize you are short circuiting this completely to send the updates through even if the pipe has a lot of things waiting already. This is why things like opening chests become more responsive after this mod. Player updates seem to be “critical” and seem set to flush, so you’ll see people moving around but all of your actions will be stuck in a pending “queue” which is our send queue and literally what is happening.
Theoretically you wouldn’t want to fix this behavior normally since if pipe is too stuffed you would want to ignore noncritical updates but in practice the limits are so low that they actually cause the bandwidth issues they’re trying to prevent by leaving the pipe too “narrow” roughly speaking. Yes, you guys aren’t sending too much and overwhelming it, but now not enough can get through and that is backing up causing the same delays. To their credit though this is still an improvement over the old m_dataPerSec. That didn’t really have the queue capabilities this new system has that they can override with a “flush” and was basically a hard cap so this way makes more sense. They really should just make it a configuration variable though as different connections/situations will dictate what this should be and it’s not one size fits all which is why most other servers you can configure things like this (or do it at the OS level at least).
Hopefully that helps, let me know if you get it working as I’m planning on jumping back on soon!
Thank you! That is very thoughtful and detailed.
I’m reconfiguring my server and getting it stable again due to a range of mod updates. Once it’s stable, I’ll come back to trying out the tweaking of the send/receive limit.
I’ll report back here with what I find, probably in the next few days.
Cheers!
No problem at all, I’m very excited to jump back on there myself and probably won’t be able to wait if it’s still going to be a while before the new update. Have fun!
I applied the changes you recommended using dnSpy and recompiling. It worked very well.
The fix to the constant was simple and 30,720 was good. The game seems a little more fluid. Those of us on good connections don’t have any lag experience with 4 players and multiple mobs in complex areas. Our one player on a bad connection has occasional lag still, but that may also be a PC performance issue for him.
I don’t have more methodical or quantitative tests, but I’d say it worked!
What didn’t change: the textures on the server that Monsternomicon installs still lag out and cause errors with RRRCore, a common issue that players are seeing with dedicated servers. I don’t know how those will be fixed. I may experiment with a bigger constant, but I’m not sure that’s enough to solve the problem (or even the right understanding of the problem).
Hey Sam,
Thanks so much for the follow up! I completely agree with your conclusions and that sounds exactly like we would expect it to. There definitely are some other bottlenecks out there (the client used to be able to be modified the same way and I’m sure probably looks pretty similar to this) but the main one is client side performance like you are suspecting here. It could also be network related as well. One good test would be to have him open a command prompt and do a ping -t serveraddress. That will do a ping every one second and if he just lets it ping while he is playing if you see the ping jumping in the command window that is network lag/latency.
The RRRCore stuff I actually wasn’t familiar with but now understand it’s a framework for a set of mods for Valheim! These didn’t exist when I originally wrote this but that’s really cool. I imagine that is something that will need to be fixed by the mod creator but I’d have to learn more about it. Thanks for sharing that it’s even a thing though, I’m going to experiment with that the next time I jump on!
I’ve posted a new post with your information here. I went and made the modifications and updated the guide to help others. Thanks again for posting it!
You are too cool!
I am currently running 102400 as the “magic number” and it appears stable, but more multiplayer testing is needed.
A few other server admins are testing this out as well (you can find them over at Valheim Modding in the #user-assistance channel). We are all grateful for your help!
Hi!, yesterday’s update changed the fields in this tip about the synchronization. now the user with low connection drops off the server. rs =)
Is it possible to host valheim server on the raspberry pi 4? i have seen someone did it with WOR. Can it be done on the native rasbian distribution ?
Hey Ice,
That is a fantastic question, and Raspberry Pi stuff is probably 50% or more of my blog!
Steam is available on Linux and the dedicated server binary runs on Linux as well since Linux dominates the server world. I’m not sure if the binary would run on ARM though or not yet. The tricky part is the ARM part and I’d be pretty surprised if there is a native binary available for arm64 or armhf.
You would need to install Steam on your Pi for sure. If you can get Steam on there I would think the server would work (as long as it’s not too slow, it looks like some emulation is being used in this method since they are using box86)!
I gave it a quick try and I was able to get Steam installed but have not got the Valheim server to launch yet. I’ll have to explore this in a future blog post and see if I can get it running but if you wanted to try it on your own I would see if that Steam guide still works today in 2021 and if it will let you install it!