Friday Facts #379 - Abstract rewiring

Posted by boskid, StrangePan, Klonan on 2023-10-06

Let me show you around. That's our lab table and this is our work-stool. And over there is our interplanetary space-platform! And here's where we keep assorted lengths of wire.

Whoa! A real live space-platform!

We designed it ourselves. Let us show you some of the different lengths of wire we used.

Electric pole improvementsboskid

This will be more of a technical journey, a peek behind the scenes of what we have to consider when adding features, and it will be wired to one of the features I had to refactor in order to allow more features: manual ghost wires.

The technical starting point

Traces of the first implementation of copper wires can be seen in Factorio versions as old as 0.2.10 from 2013 and they actually survived up to the 1.0.0 from 2020.
Copper connections between electric poles were just a targeter pointing at another electric pole saying "I am connected to this electric pole".

The simple case - poles pointing to its neighbours.

To be more precise, each electric pole had exactly 5 of those targeters, as a static vector is more optimal than a dynamic one.

Electric poles in Factorio 1.0.0. Copper wires between electric poles were not possible between electric pole ghosts.

When an electric pole would be destroyed, all copper wires to other electric poles had to be disconnected because we can't pretend that the entity "hidden" in the entity ghost 'shell' is still connected to the network.

Real pole can't have a direct "normal" connection to the one inside a ghost, as the logic related to connection would get confused.

But to be able to have wire connections in blueprints (both normal and circuit), we had to add a special one-sided connection from ghost to real entity.

Special data structure for ghosts to keep track of entities its connected to. Featured in FFF-362 and released in 1.1.

If both were ghosts, they used this special connection bi-directionally.

This looked like a nice solution in the beginning, because we "glued" a new possibility to the ghost without disturbing the basic electric pole functionality. In other words electric pole logic didn't have to know anything about ghosts and its related logic.

But in reality, this solution already started to smell, as the special cases were starting to pile up.

  • When an electric pole is destroyed, you need to check if ghost is not pointing to it, and update its connection to the containing new ghost instead.
  • When an electric pole is revived, you also need to update the corresponding neighbouring ghosts.
  • When blueprint is being made, the wires for the blueprint need to be collected from entities and ghosts.
  • In any logic which needs to check if two things are connected regardless of ghost or real connection, we need to check all the possible ways the connection could exist. This applies to wire dragging, automatic connections, pole removal re-connections and such.
  • And this is just the start, because we are just considering electric poles.

We also have power switches. The switch+pole connection was also done in a custom way which didn't have the 5 connection limit.

Abusing power switch to have 6 electric pole connections.

So the amount of places where the connections are stored and the combination in which they can interact was just a programming nightmare, and the main reason why there were always some special corner cases where wires worked differently.

Similar limitations also applied to circuit wires. Circuit connectors were only able to store connections to other real circuit connectors, so when a ghost was created it would be a single direction connection only.
This means that the special logic related to circuit wires and its handling was also bloated by all of this.

The breaking point

At this point I was asked by kovarex to make it possible to add wires to ghost poles manually. After a day of not being able to reliably tell which functions to call to just add a ghost wire, I decided the amount of technical debt was just too much.
A refactoring was imminent!

I just had to throw everything out the window and propose a completely new design that would solve all the issues reliably in one go.

The realization

It started to be obvious that the original rule, to never point into "dead" entities inside ghosts by anything was not worth the complications.
Once we started to discover the possible simplifications we could do by breaking this old rule, it was more and more obvious, that this is the way to go.
It turned out, that the thing we were afraid all along, to alter the electric network pole connection logic to not get confused when ghosts included in the normal connection system, was basically just a few ifs.
In other words, we built a whole infrastructure to shield this logic from change, while the infrastructure was so overwhelmingly more complicated than just changing the original logic.

We thought it was a good programming design, but it led us down the wrong path.

The unification

And since everything could connect the same way, real, in ghost, power switch, circuit wires etc. We could encapsulate all the related connection logic into a single WireConnector class and make everything stupidly simple compared to how it was.

With all the changes, setup like this is no longer deemed impossible.

There were also some interactions that could be improved even a little. When dragging an electric pole with circuit wires, it will drag those as well.

Also, the circuit rewiring logic could simply be done the same way as normal wire rewiring.

The closure

And finally, after 2 weeks of refactoring. I sat down and finished the original task to allow player to manually connect electric poles even in the ghost form.
It took me just a few minutes.

Where are my wires?StrangePan

Speaking of wires, we noticed a few oddities with how the game was drawing them. Once I saw these problems, I couldn't unsee them.

  • Wires connecting two "short" entities over a long distance (such as inserters or chests) were sagging way lower than they realistically should.
  • And what's with those shadows? Why are they sagging south while all other shadows stretch towards the east? That's not how physics works!
Combined, these make wires feel out-of-place in the game world and appear pasted onto the screen. Let's fix that.

While the game is 2D, we do have some hint about the relative 'heights' of the wire connections, which is the relationship between the connection point and the shadow connection point. Using these two offsets from the Lua definition, we can estimate the maximum wire sag that should be present.

To better communicate a wire's position, circuit wires strung between two short entities now only sag until they would touch the ground.

Wire shadows now sag towards the sun. They feel more natural and less out-of-place when viewed alongside other shadows.
Together, these two changes give the wires a better, more consistent illusion of height and space within the game world.

Long-distance connections between combinators should be more readable at-a-glance.

Once you see the improvement, it's hard to go back without noticing the flaws.
Pop quiz: can you spot the shadow problem I want to fix next?

Tiny inconsistencies in a video game's graphics, UI design, controls, etc. can taint the experience, even if most players can't articulate exactly why. Most players will never notice the changes made to wires; yet all of them will see wires eventually and unconsciously enjoy the benefits. Taking a moment from time to time to improve even the smallest of details can go a long way towards making a good game feel great.

As a bonus, we can also close some of the historic bug reports on the topic.

Abstract itemsKlonan

Most items in Factorio are nice and normal, you use them as ingredients, you use them to build machines and walls, some of them you even eat to recover your health. Its simple, it makes sense.

But over time, some new features were introduced that had some unique and specific actions, and items were the only natural way for the player to discover and use them. Some examples:

  • Blueprints and deconstruction planners
  • Red and Green wires
  • Discharge defense remote
  • Artillery remote
  • Spidertron remote

These were real items that you needed to craft to use, they took up inventory space, you lost them when you died, etc. You might be a little surprised to learn that when blueprints were first added, you had to craft them with an advanced circuit, and to erase them cost an electric circuit.

Over time we came to decide that having everything as 'real items' was not always the best gameplay-wise, and with the introduction of the blueprint library, blueprints became the first 'abstract item'. This means that you can just spawn one in when you need it, and you can store them in the library so they don't take precious inventory space (but you can still put them on your quickbar to convenient access).

Weird tricks and remote interactions

Blueprints were the major one, but it left the rest of these types of items in a strange place. The strangest and most annoying was the red and green wires. It took good hard well-earned copper and iron to craft them, but if you unwire something you got nothing back. Additionally, if you used a blueprint, the red and green wires were included for free.

The aspect of 'remote interaction' also started to become strange. You can not red wire something from the map, but you could take a blueprint of it, wire it locally, then place the blueprint back over the top, and the wires would magically change. Thats just frustrating and annoying.

In the end we took the path that made a lot of sense, which is to turn these all into abstract items which you can spawn for free, and the abstract items work from any distance. This also applies to the copper cable, there is the 'real' copper cable used for recipes, and the 'abstract' one used to connect electric poles. The new abstract items naturally tie in to the shortcut bar system, which is being used to its full potential.

You might also notice that the shortcut bar can now be up to 12 slots wide!

These changes also improved the ability to do things remotely, and remote interactions will be a focus of the next FFF.

As always, connect some wires between your fingers and the table to let us know what you think.