Welcome back to another Logic World Wednesday! It’s been a busy week for Mouse Hat Games and we’ve got a lot to talk about.
But first! This week the Youtube algorithm blessed us with an auto-generated channel for Logic World. Check it out here, and be sure to subscribe. All future LW videos will be found there – videos from us, from beta testers, and from anyone after May 1st who makes a LW video.
We met up this weekend to hash out some details about the soundtrack, and we made a vlog!
Happy Wednesday everyone! This week I’m happy to share the full version of my piece heard in the vlog above. I hope you enjoy.
I was sick of making responsible use of my time, so this week I made it so you can see your reflection in other players when they have SHINY BOBBY enabled.
Now that we were rendering your own player model, I was also able to let you see your own shadow.
But honestly this feels pretty weird. The shadow is a solid and unmoving circle, which we humans are very not used to seeing as our shadow. So I made it an optional setting, which is turned off by default.
Some beta testers were reporting some ridiculously long loading times when connecting to a server. We presumed this was a SECCS performance issue. SECCS is my open source library for converting arbitrary C# data into binary and back. We use it for serializing and deserializing all the network packets, so when it was taking a long time to send a packet SECCS was the obvious culprit.
So I set on to investigate. Using a profiling tool for .NET I was able to find out the culprit for this, which to my surprise was the
System.Array.Resize method. It turns out that Lidgren – our networking library – was allocating a buffer smaller than some big packets’ size, so every time some data was written to it it would have to expand, calling the method I mentioned. This lead to a lot of memory and CPU usage, as the runtime had to create a new array and then copy the contents over from the old one every time it resized.
I solved this by introducing a mechanism for approximately calculating an object’s size and creating a buffer big enough to fit it, reducing these resizings to 0. As a result, load times for saves have been enormously improved. In some cases this is as high as a factor of 10.
While investigating the performance issue above I realized that the SECCS codebase has become a nightmare of
Expressions and unreadable code, so I set out to completely rewrite it, this time with a clear goal of what the library should do and what it should look like. Plus, this time I’m focusing on writing tests for everything, so changes shouldn’t break anything.
This is pretty satisfying to see:
Before this week, the lighting of the scene had a BIG effect on the color of objects. You would choose a color, and the object would be a completely different color from the one you chose.
This has been bothering me, and this week I set about fixing it. I did some fancy graphics things and now colors are much more accurate.
This week I’ve also been working on abstracting the network system we use for communicating between the client and the server. This means that the game won’t tightly depend on Lidgren for networking, so mods will be able to plug in their own systems to either expand or completely replace the original system. For example, a mod could create a system on top of the existing one that encrypts the packets, or another system that uses a method for transporting packets which offers less latency at the expense of dropping packets. You could even use carrier pigeons if you wanted to!
We will use this abstraction in the integrated server, where we will be communicating between the client and the server using named pipes instead of UDP Lidgren connections. Named pipes are streams of data that two or more processes can attach to, sending and receiving data to and from other processes. This method has less overhead than a full UDP “connection”, which will allow the game to use less resources.
In recent weeks we’ve been getting a LOT of visits to logicworld.net. You can see this for yourself at https://status.logicworld.net/. In order to better understand why this is happening and how we can encourage it, this week I’ve been working on some internal analytics tools.
I added a service that reads Traefik (our edge proxy)’s logs and registers every request to a MongoDB database. MongoDB is a very flexible NoSQL DBMS oriented towards big data that allows us to execute some powerful queries. For example, we can take out the number of requests per referrer domain:
This flexibility will allow us to investigate traffic spikes and correlate them with other events.
For transparency, we are now collecting the following data about your visits to logicworld.net:
This data is purely for our own use in helping us grow Logic World. We will never give or sell your data to a third party.
This week we had to make some changes to the file format Logic World uses for saved worlds and boards. Our testers were very understanding towards the prospect of their saves breaking, but I wanted to avoid that, and I knew we were going to have more changes to the format in the future. So I took the time to add support for converters from one save format to another.
The update with the new save format shipped with a converter from the old format, and no saves were broken. In the future, it will be extremely easy for us to add a converter for new save format versions.
This section is long and technical, so if that’s not your jam feel free to skip it. TLDR: I made the game faster.
Still reading? Okay, buckle up. Many component types in Logic World have custom data associated with them. Displays store their colors, Mounts store their height, Keys store what key they’re bound to, et cetera. For managing this custom data, we have a clever system where we supply a list of custom data types and the game automatically handles all the annoying bits where that data is turned into binary and synchronized between the server and all connected clients.
This system is easy to use, but unfortunately it is not terribly speedy. We were running into problems using it with Delayers. Most components only change their custom data rarely, when a player opens the menu to edit them. But Delayers’ custom data changes every simulation tick, because they are constantly counting how long they have been powered or unpowered for. Because of this, not only were Delayers far more CPU-heavy than necessary, but they were also flooding connected clients with data updates. Clients don’t need to know how long a delayer has been powered for; that data is only necessary for computing the state of the simulation, which is all done server-side.
So this week I set about making a technical upgrade to our custom data system. Now, server-side logic components can mark some custom data as being used purely for the simulation. For these variables, the component uses a standard C# field to store the data. Then, when the game is saved, the component serializes that field as binary. At no point is the value ever sent to the client.
Thanks to these changes, Delayers are more than 10x faster and the game uses massively less bandwidth when delayers are running. This was a lot of work but I am very satisfied with the results.
We’ll keep releasing these weekly updates right up until the game comes out. To make sure you don’t miss them, you can sign up for our newsletter. Be sure also to wishlist Logic World on Steam and join the official Discord.
See you next Wednesday!