Programming Project Marworyn

A population simulator inspired by Dwarf Fortress.

Programming Project Marworyn

How an Enterprise Dev Learned MonoGame

I've been in the heavily compliant enterprise software world for 8 years now. I've ingrained habits and customs that are an affordance or an asset of that type of development.

I've never built a video game before.

Not really knowing where to start on shifting my Console app into a Monogame app. I dragged in a scaffolded stub then started building it out...

Initialise()

base.Initialise() is called last, that seems correct. Monogame uses Initalise() to set up the GraphicsDevice. GraphicsDevice is a dependency of SpriteBatch.

In my typical enterprise fashion I naturally started with creating a DI container...

var serviceCollection = new ServiceCollection();
serviceCollection.AddCoreServices();
serviceCollection.AddSingleton(SpriteBatch)
var serviceProvider = serviceCollection.BuildServiceProvider();
var simulationManager = serviceProvider.GetService<SimulationManager>();
simulationManager.Start();
base.Initialize();

Oh dear.

See, Monogame builds up the dependencies for SpriteBatch - That GraphicsDevice on initialise and here I am trying to register the service BEFORE it has it's dependencies.

SpriteBatch is registered into DI before GraphicsDevice exists. SpriteBatch requires a GraphicsDevice in its constructor. So, when something resolves it from the container, GraphicsDevice needs to be available.

But, when I started out, it didn't matter because I wasn't using SpriteBatch for anything. I was staring at the default Cornflower Blue Monogame screen of liminal space going "So... What now?"

SpriteBatch was registered in the DI container, but since simulationManager.Start() never resolved it, it's never constructed. It's never used. It's dead.

And no one likes dead code.

Ok so, LoadContent() cerates a SpriteBatch directly and assigned it to SpriteBatch but the actual field was never used.

So, I had a field and a DI registration for a dead piece of code.

Urgh, 8 years in enterprise dev. 0 in gamedev.

With that background, let's be honest here. I was missing a lot of info.

I actually spent a good bit of time with Whitaker's turorials: http://rbwhitaker.wikidot.com/monogame-tutorials I just needed to grasp a few basics.

Here's the issues I identified:
-Monogame is NOT request/response, it runs on a gameloop. Update(), Draw(). Over and over. 60 times per second. This loop is the heartbeat of the game. It's not an Azure Function, an AWS Lambda, a REST call... No, it's a loop.
-SpriteBatch is the 2D renderer - Very important. It's how you shove things onto the screen. Begin(), End(). End flushes the GPU. Agian, it happens each time inside Draw(). A loop.
-GraphicsDevice is the raw GPU handler. SpriteBatch is a wrapper for it.

SpriteBatch is the 2D king of Monogame. Yes. Maybe. Probably.

Ok, so given my DI enterprise background... SpriteBatch lives on top of the game, in the presentation layer. Somewhere high level. In enterprise terms, SpriteBatch is like a View layer (View being an MVC term.) Nothing that isn't rendering/presentation related should touch it. It should not propergate down into the game engine logic backend layer. Seperation of concerns.

All fo this to say, SpriteBatch MUST come AFTER the Initialise call. SpriteBatch has a dependency on GraphicsDevice, which only exists after Initialise(), so we treat SpriteBatch as not buildable until after Initialise.

That's where LoadContent() comes in. This is the thing that fires after Initialise completes.

_spriteBatch = new SpriteBatch(GraphicsDevice);

...should go inside LoadContent(), hazzah!

serviceCollection.AddSingleton<SpriteBatch>()

DELETE. DELETE. DELETE.

(Have you ever seen a word repeat itself so much that it looses all meaning? Anyway...)

The problem with the above is that SpriteBatch will lack it's dependencies and fail. The moment anything tries to resolve it, it'll just go BANG.

Ok... So how to resolve this issue? I think to answer that I need to think of the application lifetime of SpriteBatch. SpriteBatch is a singleton.. Presumably. So is GraphicsDevice... So they have a longlife in the application. So really they'd need to be created once... Ah, but no, because Load() is called many times per second... So it's functionally destroyed and recreated many times a second... While I'm treating my DI container as a sorta initialiser... That feels wrong for SpriteBatch.

Nope! Wrong! LoadContent() is actually only called ONCE! The loop is Draw() and Update(), NOT LoadContent(). Right, ok, so LoadContent() is the proper place to initiate long-lived items.

I think my DI container would need to be on LoadContent() instead of initialise? Hmmm...

But, also. SimulationManager.Start() the more I stare at it, the more it stops making sense. I believe having the old SimulationManager (which is essentially a per-tick loop for the console app simulation). Is the wrong approach for Monogame. Some of the responsibilities of SimulationManager should move to Simulation.cs... The DeathEngine death check that currently happens per tick... But then each tick currently is one game day. Coupling ticks and frames feels wrong.

My simulation tick (one day) and a frame cannot be conflated. What I need is a Monogame accumulator. To track elasped time on Update(). I only advance the simulation when enough time has passed.

So, what does LoadContent need to do, RIGHT NOW, though? While the Cornflower Blue screen stares back at me almost accusingly.

That's a broader design question. Enterprise dev hat says to start with the MVP. Output text onto a screen. Draw() - Is the output, I can see that. LoadContent(). I have a font I plan to use. I should pull that in, I can do that without further knowledge. (For this project I'm using Pixeloid, see: https://ggbot.itch.io/pixeloid-font)

Ok, so Monogame pulls in assets from the Content pipeline. This uses the MonoGame Content Builder. I can't just shove stuff in a folder.

Ok, so I dragged in the font using the GUI, a fancy Monogame window. I created a spriteFont file. I added the path as the FontName.

Build succeeded in the Monogame window.

Content.Load<SpriteFont>("Content/SpriteFont.SpriteFont"); I figure it would need the path to the sprintfont file...

Wrong!

Turns out Monogame Content object already knows its root is the Content folder - neat!

Content.Load<SpriteFont>("SpriteFont");

But, to use it I must set it as a field. There is a thing I like to do when faced with an unknown object:

var x = Content.Load<SpriteFont>("SpriteFont");

My IDE then tells me this is a SpriteFont object on hover, ok that's easy. I just create a private field with _sprintFont of type SprintFont.

private readonly SpriteFont _spriteFont;
_spriteFont = Content.Load<SpriteFont>("SpriteFont");

Ok, so now I need DrawString method on Draw()... But from what I have learned about gameloop I need, surely, Draw() and Update()?

Nope, those are the MonoGame game loop methods, not SpriteBatch methods.

_spriteBatch.Begin();
_spriteBatch.End();

So now, all my drawing code sits between the two like a delicious sandwhich.

Ok, interesting thing. I vaugely know about positioning. It's xy 0 is the top left of the window. The catch is different devices use different sizes... Vector2 looks suspiciously like a positioning variable:

_spriteBatch.DrawString(_spriteFont, "Hello world", Vector2.UnitX, Color.White);

So, surely it must resolve to 0, 0, yes?

Wrong! Actually it's (1, 0). Vector2.Zero resolves to 0,0.

On different device sizes - It's important but right now it's a bigger topic. Much like css media queries I imagine that well runs deep, and right now I don't know what I don't know. Put a pin in it for later.

So, let's run it...

Oh no! Contains characters not resolvable by the font error

That's the character set issue. The .spritefont has a <CharacterRegions> section that defines which Unicode characters get compiled into the font.

<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#32;</End>
</CharacterRegion>
</CharacterRegions>

There's your problem. It's only compiling a single character (Unicode 32 to 32).

<End>&#126;</End>

Yes, that works. I can now see white text on a cornflower blue background. Quietly judging me.

What is Project Marworyn?

Marworyn is Welsh for ember. I picked it because I've always enjoyed a particular kind of video game. A lonely game where the world is harsh and difficult. You can overcome the challenge or loose to the environment. The actual risk of failure is what makes it so rewarding or so bittersweet.

Probably the biggest inspiration theme-wise was Banished. Banished is a game where your village is exiled and you must build a new village in the wilderness to survive. Get it right and you can build a thriving community, get it wrong and your people could starve or freeze in the uncaring winter.

I named this project "ember" because that's where the player starts. They start by clutching the faint ember of humanity in their hands. It can flare back into life, or it can grow cold and die.

Now, let's talk technical origins. This project started out as a name generator which you can find here: https://github.com/HazelLessiter/UniqueNameGenerator (warning, bad code ahead. I wrote this 8 years ago). Here's what's there and what's wrong with it:

It's an old ASP.NET MVC 5 web app that generates 22 random names by pulling random prefixes and suffixes from a SQL Server Express local database and concatenating them.

The concatonation is super basic and boring. Yes, boring! It is just bare string concatenation with no logic at all:
names.Add(prefix + suffix);

No separator, no capitalisation rules, no length checks, no transformation, no nothing! The prefixes and suffixes in the database were then joined directly. The code had no awareness of HOW the names were joined it just stuck two strings together. That's it — it was essentially a data-driven name vocabulary builder.

So, why did I build it?

Ah, now then! This is where it gets interesting!

It's actually because of Dwarf Fortress. I was playing the game and I wanted a tool to help me nickname my dwarves.

Initially, as many players do, I did this manually using DFHack. I had this idea that I could track families in my long-running fortress by giving them nicknames:
-For a female dwarf: MotherName FatherName
-For a male dwarf: FatherName MotherName

The founders all had just one name like "Serena" and all the children had two names. By the third generation I was going:

-MotherFirstName FatherLastName
-FatherFirstName MotherLastName
Look familiar?

It's almost, but not quite my current naming logic for Project Marworyn. The name generation there is smarter. The "Person" object carries explicit Prefix and Suffix fields, and children's names are blended from both parents' parts rather than randomly pulled from a flat vocabulary. The linguistic "pattern" is an actual rule in the code. One that formed the very foundation of what this project is.

Go back in my github history... Project Marworyn started with just a "Name" object.

It was a NameGenerator.

But, a more interesting one.

My old UniqueNameGenerator Dwarf Fortress tool was clearly displaying that thread of thinking 8 years ago.

But, not quite.

Here's everything wrong with UniqueNameGenerator:
-new Random() inside the generation loop - under concurrent load it gets the same seed and produces identical names across requests
-rnd.Next(maxPrefixID) can return 0, and if IDs are 1-based, .First() throws InvalidOperationException and crashes the request silently
-88 database queries per page load — it recalculates Max(PrefixID) and fetches each name individually inside the loop rather than loading the data once. (This is how you make your DBA cry.)
-No authentication anywhere — the entire CRUD interface is publicly accessible... But this is kinda moot for what SHOULD have been a silly little console app. Why did I build it in MVC? IDK.
-debug="true" hardcoded in Web.config (for what it matters)
-Local .mdf database file sat in App_Data, committed to source control - Don't do this in production code, children. Not a good idea.
-.NET Framework 4.6.1, ASP.NET MVC 5, Entity Framework 6 with the old EDMX designer format (Database-First, generated T4 templates). All 2013 - 2016 vintage wine
-jQuery 1.10.2 and Bootstrap 3.0.0 — Both over a decade old and carrying known CVEs
-No async/await anywhere, everything blocks the thread pool
-No service or repository layer — controllers hit DBEntities directly, so it's completely untestable
-No error handling or logging beyond the default MVC error page

People who'd be horribly offended by this code:
-DBAs
-InfoSec
-Martin Fowler himself

But... At the end of the day it was a learning project from 2018 that works in the happy path.

Practically speaking it's not worth patching — it would be a full rewrite to bring it to modern standards. But, I don't care. I'm keeping it in a nice public archieve so the whole world can point and laugh at it. And, hopefully we can all look at Project Marworyn to see how far I've come in those 8 years.

That's cool and all... But, I still havn't explained where the prefix/suffix logic came from. Why, I had that idea when I was nicknaming dwarves in an ASCII game.

It actually came from a book.

I read the Anne McCaffrey Dragonriders of Pern series (yes, all of them, there are a lot). When I was younger I always thought how cool it was that this WORLD developed its own culture and its own way of life, different from anything that felt familiar. But logical in its origins.

In those books, the Dragonriders had honorary names that were contracted forms of their real names. The idea was that when flying around quickly attacking Thread (if you don't know what I mean, just read the books), you needed a way to QUICKLY refer to someone's names without worrying about honorifics or titles. Just "OI YOU!" So your pal didn't get stung by the nasty Thread. The way the characters solved that problem, as a community, was they contracted their names to one or two syllables. You got names like F'lar and F'lessan.

However, what's also interesting is that the names these Dragonriders often gave to their children? Derived from their parents' names. F'Lessan was son of F'lar and Lessa. His name was a mutation of his parents' names. A portmanteau of his parent's names inline with the tradition and culture of his people.

I just thought that was perhaps the most interesting way to make a world feel alive. To have a history, a culture, and a way of carrying family names forward that feels different from our own.

I was thinking about it a lot 8 years ago, and it never really left my mind. So, here is my little version. Project Marworyn — a NameGenerator that kept expanding until it was something else entirely, creating a world of its own.

User Choice

Ok, so I got a little distracted and instead of building my population simulator I built a whole website. Only html and css, but still. That's two hours of my life. I really should get some coding done.

Right, next port of call. Bugs! I promise you, Project Marworyn has many! And-
Oh. Oh no.

Ok this is going to sound ludicrously petty, but excessive whitespace on a tiny laptop screen on a developer tool where I really want density of information is very annoying. The main problem I have here is that the solution explorer which used to look like this:
As you can see, much less information can now fit onto the screen at any one time. If that's your preference, that's fine, but for my personal developer experience, it's an irritation.

It is often said in UI design that "scrolling is cheap", but I personally disagree. Maybe scrolling is cheap on social media where there is infinate scroll and the user is searching for content, ok (though that has it's own problems, as we've seen in recent legislation). But, for a dev tool? A dev tool should allow a developer to complete their tasks quickly and comfortably.

There are some developers who prefer a minimalist experience. I've really nothing to say there, except that I'm not one of them. My objective is to fix the bugs in my project, focusing on the code, with less distractions from the UI.

Well that does sound like minimalism, now, doesn't it?

Ah, but well, maybe I'm not such an anti minimalist, though maybe I am simply a pro-speed engineer. When a tool I use daily, both personally and professionally suddenly changes it's UI. I'm going to have opinions about it. And, honestly, if I could hit my entire project onto the screen for Project Marworyn before, I would very much like to be able to continue to do so.

Ok, so maybe the solution is configurability!

Yes! Because then the minimalists who love their whitespace can have their giant whitespace (which, let's be honest, is likely beneficial to many due to real accessibility reasons. let's not dismiss that). While, people who prefer the opposite can simply change their settings! Yes, a highly configurable dev environment helps everyone!

Ok... So how do I adjust the spacing in solution explorer in Visual Studio 2026?

And this is where I could have written a long rant about the failure of configurability in modern software, how choice is stripped from the user in favour of some design ideal.
Instead, GLORY!

This was actually a very easy fix. I simply opened my tools > options and did a search for "solution explorer". It was almost the first option that came up.

The lesson here? Configurability is the gold standard and if your settings menu has a search, even better. Well done, Microsoft!

(Now, if you could just let me have a universal disable smooth scrolling toggle in Windows then I'd be very happy.)

You Should Write a Blog

It was a bright sunny day and I decided to do some sewing. But, no matter what I did project after project was a total disaster.

Firstly, I sewed a blue top too small, I drafted it for a version of me from two or three years ago. I've changed shape since then (fine and normal) and it no longer fits. Rather then adding panels or whatever, it would make far more sense to redraft a new pattern and remake it to actually fit me. I'd feel much better about the end result.

Ok, breathe.

Next, I tried an "easy project" I had scrap fabric and some rubber elastic. Ok, cool. I didn't give enough ease in my fabric so the scrunchie couldn't stretch and thus wouldn't ever work.

That's disaster number two.

You're here for the programming not for the sewing, but stick with me here, this has a point.

I decided to look online for anyone with any advice on sewing sustainably and where to start. Well, what I found was:
- Videos about "I sewed my own wardrobe in a day", not exactly the slow-fashion approach I aim for.
- AI generated images of strange denim teddy bears.
- Articles on "here's ten tips!" which don't really answer the question and kinda state the obvious. Riddled with adverts for some "revolutionary diet product", picked just for me based on my "demographic".
I'm not sure, if I'm making my point clear, here.

I want to connect with real creators, so we can share our knowledge and growth. I want to be a part of a community where we all build each other up. I want to see words written by real people for real people.

It's not really about sewing, it's about how in our algorithm fueled internet somewhere along the way the static built up so much that we can no longer hear each other over the noise. Like a badly tuned radio you might just be able to hear someone once in a blue moon before the static closes in again. It's honestly kinda sad.

Something is lost when we prioritise the mechanism of content generation and algorithmic consumption over the human voice. The loss of expression, of creativity and of the sense of community.

This is why I created this bog. I think there is more room on the internet for old-school indie web blogs. This one just happens to be about programming and sustainable crafting. By all means visit the Tavern to see links to various other interesting websites.

If you're a Software Engineer, like I am, I emplore you to do the same. The internet needs more human voices, so maybe we can hear each other over the noise.