SDG Blog

"Remember to put a witty tagline here!" – 2021 edition.

February 2021 Development Update

Late anecdotes from the past month of gamedev.

There are less days in February than I thought, so when it was suddenly March 1st there was no blog post! Then I prioritized other tasks rather than writing this. It will only list February stuff though.

We have somehow had an Unturned update every Friday of 2021! How long will this continue...?

Roadwork

A player named Zefirka emailed me last month highlighting that all of the older maps had wonky jagged roads, and kindly offered to help update some of them. I had to decline the offer because we do not have a contributor licensing agreement (yet), but it did remind me that the nicer spline roads were added all the way back in mid 2016, and at the time I had decided it would take too long to manually update the existing maps.

I think my perception of time has changed since then. My plans for updates were a week or two ahead, whereas now my changes are in preparation for updates even a year away, and of course Unturned II is an extremely long-term project in my mind. With this mindset it was a no-brainer to take a bit of time in each update of February to make the Yukon, PEI, Washington and Alpha Valley roads nicer, and I think it was worth it.

Embedded Item Store

As far as updates go this was probably only interesting to item creators, but on the design side it was the first sanely-built menu in the game using the new UI wrapper from 2020. Every Unturned menu up to this point is a horrible mess of global variables, e.g. the inventory is almost entirely static members/fields, so creating a new menu was a breath of fresh air for how much cleaner the UI code can be rewritten going forward.

The default Steam item store is fairly okay, but in my opinion the game-specific menu is now able to provide a better customer experience. Backed up by the game's sales getting a nice boost after the release.

Weather

Development on custom weather was originally started for an upcoming map. Converting the hardcoded rain and snow was daunting because of spaghetti and backwards compatibility, but dogfooding would make it more robust - ahem pay no attention to the fact I broke custom weather timestep two updates in a row beforehand, sorry Animatic...

Looking at the disastrous state of the "devkit" 2017 changes however is a lesson for me in the importance of following through on these rewrites, lest we create a bigger mess. In the end the weather features are now awesome, with the legacy weather converted, and plans for more vanilla weather events.

Network Progress

Following the data packing rewrite the next step was the "messaging" layer, e.g. initial connection, authentication, BattlEye data, etc. This was a horrible many-thousand line long switch statement before.

It read data in probably one of the worst ways possible: passing an array of types to a method which allocated an array of objects, then went through each type in a huge if/else chain to read the correct type, then boxed it into the array, and finally as a cherry on top the user had to cast the object at the correct index to the correct type. This was a nightmare to work with, for example "hmm is (ulong) objects[37] correct?" The worst offender was this one line:

object[] objects = SteamPacker.getObjects(CSteamID.Nil, offset, 0, packet, Types.BYTE_TYPE, Types.BYTE_TYPE, Types.STRING_TYPE, Types.STRING_TYPE, Types.BYTE_ARRAY_TYPE, Types.BYTE_ARRAY_TYPE, Types.BYTE_ARRAY_TYPE, Types.BYTE_TYPE, Types.UINT32_TYPE, Types.BOOLEAN_TYPE, Types.SINGLE_TYPE, Types.STRING_TYPE, Types.STEAM_ID_TYPE, Types.BYTE_TYPE, Types.BYTE_TYPE, Types.BYTE_TYPE, Types.COLOR_TYPE, Types.COLOR_TYPE, Types.COLOR_TYPE, Types.BOOLEAN_TYPE, Types.UINT64_TYPE, Types.UINT64_TYPE, Types.UINT64_TYPE, Types.UINT64_TYPE, Types.UINT64_TYPE, Types.UINT64_TYPE, Types.UINT64_TYPE, Types.UINT64_ARRAY_TYPE, Types.BYTE_TYPE, Types.STRING_TYPE, Types.STRING_TYPE, Types.STEAM_ID_TYPE, Types.UINT32_TYPE, Types.BYTE_ARRAY_TYPE, Types.BYTE_ARRAY_TYPE, Types.STEAM_ID_TYPE);

Writing was similarly terrible. Thankfully it has been mostly cleaned up now. One feature I am particularly happy about is automatically generating read/write implementations for each enum as an extension method with the per-enum needed number of bits. As an example:

public static bool ReadEnum(this NetPakReader reader, out EPlayerSkillset value)
{
	uint index;
	bool result = reader.ReadBits(4, out index);
	// Casting out of range index to enum would throw an exception.
	if (index <= 10)
	{
		value = (EPlayerSkillset) index;
		return result;
	}
	else
	{
#if WITH_NETPAK_EXCEPTIONS
		throw new System.IndexOutOfRangeException();
#else
		value = default;
		return false;
#endif // WITH_NETPAK_EXCEPTIONS
	}
}

Working on all these rewrites and cleanup has me feeling very excited and optimistic for the future! I am really looking forward to sharing more news. Thanks for reading!

Categories