Logic World Wednesdays: Websites and Worlds

by @MouseHatGamesDeveloper1 year ago (edited1 year ago)

Exciting Website Progress! - Felipe

This week I’ve been working on some much-needed features and improvements on logicworld.net.

User profile pictures have been missing for a long while, ever since the rework towards Kubernetes. This has been on my mind ever since but it wasn’t a simple task, since I first wanted to build a reliable uploads system in order to manage images and other data that users upload. For the past couple of weeks I’ve been researching and building this, and this week I finally finished a service that manages uploads and removals of user-generated content, so users have profile pictures again!

comments with profile pictures and developer flair.png

I imported profile picture info from an old database backup from before the Kubernetes rework, so if you had a profile picture on the old site, you’ll have that same picture on this one was well. If you don’t have a profile picture but you want one, you can set one in your account settings.

With image uploads working, I also took this opportunity to add image uploading to posts, removing the need to rely on third-party image hosting.

image uploads.png

On a more technical note, this service keeps track of all the uploaded assets (currently just images, but in the future also boards, mods, etc) and what objects (posts, comments, etc) are referencing them. When you create a post and upload an image to it, you upload the image before the post is actually created, meaning that the image is not actually being used anywhere yet and it would be cleaned up after a certain amount of time (currently 10 hours). When you then actually submit the post, the uploads service is informed that the new post is referencing the image(s) and the image will no longer be deleted. If you were to then delete the post, nothing would be referencing the image and it will again be cleaned up afer a while. This system seems a bit overcomplicated (and it probably is), however it is very robust and flexible, and it will allow me to add features in the future like using an image on multiple posts.

I’ve also added a sick developer flair on comments and posts from Jimmy and me! You can see it as that blue box in the first image above.

World model refactaroo - Jimmy

Felipe and I began writing the Logic World codebase in October 2018. In the months and years since those first lines of code, we’ve both grown tremendously as people, as programmers, and also as people. When work began on Logic World, we sort of, a little bit knew what we were doing. Today, we know exactly what the heck we’re doing.

But there’s a problem with being very excellent programmers now compared to when we started this project. When you start a project, you don’t start with the secondary, superfluous systems where low code quality is acceptable. You start with the essential, core systems on which the entire rest of the game is built. Unfortunately, this means that our core systems were written by absolute clowns. Clowns I tell you, foolish ones with big shoes and sad, poorly-applied clown makeup.

Many of our old clown systems have been upgraded, replaced and refactored out of their holes of bad practice and no foresight. We’ve talked a few times before on this blog about those refactors. But there’s been one giant glaring system, written by me in those very early days, which until this week has escaped our ruthless refactoring: the world model.

Both the client and the server keep a synchronized internal model of all the components and wires in the world. The most important and complex part of the world model is the components. Each component has a bunch of important data associated with it:

  • Component type (i.e. is this a circuit board, inverter, XOR gate etc)
  • Parent component
  • Child components
  • Local position and rotation (relative to parent)
  • Data about the component’s inputs and outputs (i.e. how many are there, which inputs are exclusive etc)
  • Component custom data, specific to the component type. (i.e. board color, label text etc)

But each component also requires a bunch of shared functionality for using and modifying that data. Components must have functions to:

  • Calculate and cache world position/rotation (relative to world origin) and trigger cascading world position/rotation updates in child components when the local position/rotation is modified
  • Modify information about inputs/outputs
  • Modify component custom data
  • Trigger events when changes happen to the component (i.e. Sockets need to recalculate which other sockets they’re connected to when the component world position changes)

In the original 2018 world model, which persisted all the way up until this week, the data and the functionality for components was all part of the same class. This was bad and very messy. There’s a lot of code in Logic World that only needs to access the data of components, like when the client renders the components in 3D or when the server serializes the components to a save file. There’s a lot of very different and separate code which needs to run the functions on components, like when the server is processing world modification requests from the client. Furthermore, a lot of the component functionality assumes that the component presently exists in the world with other components. This makes it INCREDIBLY awkward to have components that don’t actually exist in the world, such as with saved boards or with the subassemblies system I’ve written about in recent Wednesdays.

It’s a bad idea to have data and functionality tied together here. As a result of that early naive decision, tons and tons of game systems built on the original world model suffered in quality. Awkward code begets awkward code…

Well, this week I grit my teeth and set about fixing things. I implemented a solution that you may have seen coming: I split the united Component class into a data-only ComponentData class and a ComponentDataManager class, which contains a reference to a ComponentData and runs functions on it. The ComponentDataManager only exists for components actually placed in the world, and is handled by a part of the code segregated from the bits handling ComponentData, so systems that just need read-only access to the components’ data are never even exposed to functions that would modify those components.

This refactor has taken much more time than I anticipated to implement. There are a LOT of systems in Logic World which deal with components – in fact it’s pretty much the whole game lol – and each of them needs at least some small tweaking to work with this refactor. But that work is almost completed, and golly gee whiz does that feel good. The component model was a part of the codebase that I knew was bad, but I’d avoid looking at it or thinking about it because doing so made me upset. But now I am proud of how it works: the new world model is rock solid, and thus will beget more rock solid code built on top of it.

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!

More Logic World Wednesdays

@Ecconia1 year ago

Wow, more bugs and design flaws to discover on the website~ But nice finally pictures back.

Yeeeeet it - Dr. Refactor is on its way~~~

Btw: “To make sure you don’t miss them, you can sign up for our newsletter.”

@JimmyDeveloper1 year ago

Heh, sorry, I forgot to send the email this week. Will be more diligent about it going forward!

@Stenodyon1 year ago

Woohoo I was waiting to be able to change my profile picture, great work Felipe!!

I love core refactorings, it’s always so satisfying once it’s done and the compiler isn’t cursing you and your family for fifty generations anymore :D

@JimmyDeveloper1 year ago

Haha yup. Gradually seeing the error count go from thousands to hundreds to dozens to single digits to finally solving that last error… there’s nothing quite like it 🥰