Deep Forest

Show off your custom modules and scenarios!


Forum rules

Unmarked story spoilers are allowed in this forum, because modding the game probably means you've seen what you wanted to see. Please take care to avoid spoilers if you don't wish to see them.

Post Reply
User avatar
SyntacticKitsune
Posts: 22
Joined: 05 Jan 2024, 02:44
Contact:

Deep Forest

Post by SyntacticKitsune »

Here's my contribution to Finmer's (as of yet) rather limited collection of modules. It's called "Deep Forest", and it replaces the "deep forest" location with a 10x10 randomly-generated maze! This maze is generated the first time you enter, and then saved; every time you re-enter it will be the same maze. The module generates perfect mazes, which means that every "cell" has exactly one path to it.

With this change comes a minor reorganization of basically everything happening inside the forest, and this also means that the module is almost guaranteed to be incompatible with any other modules that touch the forest. The standard encounter area (i.e. Chip) can now be found in the bottom-right corner of the maze. There were also changes to the ring part of the "Finding Rux" quest, which I think work really well with the maze itself. (I haven't tested it, but there's a pretty good chance that Arlo -- from the Commission02 module -- can be found in the same place as Chip, assuming it uses the same "encounter" API.)

Deep Forest also comes with an API -- see "DeepForest_MazeAPI.lua" in the module. It can be used to add custom "exits," increase the size of the maze, and one or two other things. Most of it has documentation. Deep Forest's source can be found here, along with information on the exact libraries in use (since plagiarising random Lua libraries seems like a Bad Idea -- I'm pretty sure the MIT License requires attribution anyway, so behold: attribution). Unfortunately, furballs don't have metadata fields for module credits or the version, but that doesn't mean I can't put that information in there anyway.

If anyone wants to examine the contents of the module, I would recommend downloading a copy of the repository, since it has comments and stuff on the json files -- things that were erased when the module underwent the lossy transformation into furball form.

Lastly, I have tested everything in the module at least once, but there could still be bugs. And while the module should be compatible with existing saves, I would still create a new one first. (Besides, then you can experience the ring shenanigans.)


That's about all the important stuff about the module, so now I'm going to talk a little about why I made this module, and then some challenges I faced making it, since I think at the very least the latter would be interesting to read about.

To begin with, I've been wanting to test the limits of Finmer's engine, especially when it comes to precedurally-generated stuff, because I think it'd be fun -- both for me and hopefully for anyone who plays through the modules that come out of it -- and because no one else has done it yet (I'm not sure anyone else will). It's also a great way to find out what parts of the engine will immediately cause problems. I originally had a different module I was working on, but I got stuck on a combinatorial explosion of descriptions and decided to do something a little simpler first, and that's where this module came in.

Now, the first challenge I faced was actually saving the maze. If you've perused Finmer's API documentation (or if perhaps, you've written it) you may know that there's only three kinds of things you can shove in the save data: booleans, numbers, and strings. Obviously, an entire maze is not really any of those; the maze is a table (of tables). You can't store a table in the save data, or can you? I proceeded to bundle an entire JSON parser in the module, all so I could turn the maze into a string and save that. It was pretty effective actually.

Another challenge was getting any of the scene patches to work, and honestly, Finmer's scene patching capabilities are pretty underwhelming. There ended up being a lot of duplicating sometimes-large portions of scene code because I couldn't say, replace a node, or a node's script(s), or a compass's destination, etc. It also wasn't immediately obvious to me that you couldn't have scene-level scripts in a patch. I probably struggled the most with the scenes actually -- half the initial problems I faced getting the module to load were the scenes not being up to Finmer's standards. Here's a funny example: if you have a choice node in the root node, you'll get the following load error: "Node 'Root' contains a link to '' but the target node is not a state. (Choices can only contain States or links to States)". That one confused me until I realized I needed to wrap the choice in a state. (Finmer's engine treats root nodes like choices internally, which is why this happens. It's just an edge-case I'm guessing no one else has encountered yet.)

The last major challenge I encountered after I remembered the ring shenanigans exist and decided I wanted to reimplement it right, by using an entire A* path-finder to guide the player to Rux's place. I spent so long getting that stuff working, but I think the end result is worth the effort.

Finally, I think I'll end this post with the following cursed information: I didn't use the editor for any of this -- not even for packaging the module. If anything, it was an enlightening experience. I really wouldn't recommend forgoing the editor to anyone else, as doing away with it requires a special kind of problem-solving ability, Lua programming knowledge, and of course, a program to actually create the furball. (I should also mention that I'm avoiding the editor mostly because I can. And since ≥60% of this module is hand-written or otherwise copied Lua code, I may as well hand-write the jsons too. Plus, it lets me sprinkle comments into the json files!)

Attachments
DeepForest.furball
(48.01 KiB) Downloaded 3131 times
User avatar
Nuntis
Game Creator
Posts: 32
Joined: 11 Nov 2023, 13:27
Contact:

Re: Deep Forest

Post by Nuntis »

Okay, first off: Wow. Holy shit. This is... kind of blowing me away. I never expected someone to try and bend the engine to its limits, in the same vein of what the Red Maw scene in the main game does.

I did give this a try, and it's pretty cool to see in action. I'll say that this use of the ring is a lot more interesting than what the base game does, heheh. Maybe it would be kind of cool for it to be theoretically possible, albeit hard, for the player to stumble upon Rux's cabin before the story gets to that point. Although then Chip may try to remove them from the premises. Anyway, I digress.

Let me try and respond to a few of the things you mentioned.

Save data:

Well... Throwing a whole JSON parser at the save data problem is kind of a hilariously sledgehammer-sized solution, haha. I guess if it works, it works - no need to fix what isn't broken! I simply did not expect there to be a use case for saving complex, structured data - that is why it doesn't exist. Strictly speaking the engine is capable of saving structures; for instance shops and inventory items are technically nested data blocks (closest thing to Lua tables), but that isn't exposed to the public Lua interface.

I think if I were to implement the saving/loading of a random maze, I would try generating a string that represents the maze, where, say, one character in the string represents the contents of one cell, or similar. But JSON also works! :D

Patches:

Fair points about the patching system's limitations. I agree that it is cumbersome to achieve some effects if you are trying to, well, anything other than adding new nodes onto an existing scene. I've been thinking about how to improve this, and I think I would actually be really appreciative of your feedback on this. I would really like to add more tools here, but I'm unsure how to make them easy to use, both in terms of tech and in terms of editor UI.

One of the things I was thinking of, that would probably fix several of your headaches, is allowing the replacement of nodes, wholesale. I'm imagining this would take the shape of a new patch mode, where each top-level node will find, and completely erase and replace, another node with the same key. That way you could replace its Actions/Appears scripts, and change its children too (or make links to the old children if you want to keep them). Since there is the issue that you could replace either States or Choices, I'm trying to think of what would be useful UI here, for letting the user pick what kind of node they want to replace.

That also ties into mod cross-compatibility, which is an issue I've been kind of ignoring: some mods are going to be incompatible with each other. Maybe there could be tools for configuring load order between modules (or, well, scene patch order, since that is the only part of furball loading where order really matters). Or perhaps there could be tools that let you only load certain scripts or scenes if another furball is (not) present, to allow for 'compatibility patches' and such.

I have also thought of allowing patching the scene-wide scripts, but I am a little unsure how to do this nicely, since there are so many options and complications: do you want to add commands after the existing script? Before? Replace it entirely? Inject it in the middle? What if it's Lua code instead of visual script? Etc...

Any other ideas on any of this, I'd love to hear your thoughts.

Top-level Choice Nodes:

You mentioned this:

if you have a choice node in the root node, you'll get the following load error: "Node 'Root' contains a link to '' but the target node is not a state. (Choices can only contain States or links to States)".

A Scene is not supposed to contain Choice nodes top-level, because there is nothing they would be attached to - it makes no sense to present buttons to the player, before the scene has even had a chance to start up properly. The way the engine works is that the Scene loads, the first State is selected so that we're grounded in something, and then, from there, the first Choices are presented.

You're right that the Root node is technically implemented as a Choice, but that is (ideally) a detail of the scene compiler that most aren't supposed to notice. I agree that the error message is very confusing though; I'll see if it can be improved.

I suspect this is an issue relating to changing the patch mode. But if this happened while using the editor, this is likely a bug - only in the very specific case where you have a patch scene marked as injecting Choices into a State, should there be top-level Choices. In all other cases, the Scene file is broken. If you can reliably reproduce this, I would like to learn more about how. Or was this related to manual file edits?

Hand-editing:

I see you also mentioned you bypassed the editor entirely and just hand-edited all the files. I'm going to guess that you wrote a small C# program that imports the Editor and Core assemblies, and just invokes FurballFileDeviceText/Binary? I'm curious, did you go for this approach really just because you wanted to? Cause if so, that's valid, but if the editor is so broken that hand-editing files is better, then I need to step up my game haha.

You'll probably be pleased to learn that I was considering, some time back, to add CLI-mode support to the Editor, actually. So you could just issue a quick command line to the Editor that, say, packages or unpackages a module, allowing integration into shell scripts / build systems / what have you. I haven't implemented this yet, but this is making me think there would be interest for such features.

Closing thoughts: Wow. Thanks for making this, and thanks for sharing.

User avatar
SyntacticKitsune
Posts: 22
Joined: 05 Jan 2024, 02:44
Contact:

Re: Deep Forest

Post by SyntacticKitsune »

On the save data:

It is pretty overkill, yeah. I originally came up with the JSON solution for that earlier module (which has significantly more complicated data), but reused it for this one. I can see how I might go about a custom string format, but it'd be a bit involved since each available exit has to be saved. I decided to use JSON mostly because I hadn't thought of any alternatives, and because it's a good test for any further-structured modules.

If it would be at all possible, I'd really like to have the builtin Lua updated to 5.4 so that I can have access to the bitwise operators. That'd let me encode every cell as a single number (by packing all the booleans into an int), which would make a custom save format more feasible, or at least make the JSON more compact.

On scene patches:

I did think about this a bit, and I'd like to suggest making scene patches their own asset type. I have two scene patch files in Deep Forest that target the same scene (that is, Scene_Adept) and it'd be nice to have those in the same file. (Also, it makes it significantly more obvious what fields aren't allowed since they won't actually exist.) Here's a mockup of the kind of JSON structure I'm thinking of. Let me know if there's any details you want expanded on, but I think that should be decent. (There are comments in it detailing some of the non-pictured parts.) It also means future expansion to scene patches won't cause more random fields to show up in every other scene.

For modifying Lua code, I already added some suggestions for that in the mockup, but I'll reiterate them here. Inserting before/after should be perfectly fine. I don't see many uses for inserting before, unless you're doing something really strange like wrapping everything in an if-statement. Inserting after allows you to mess with declared functions or run your own code, which should be good enough for the more complicated cases.

I think inserting Lua code "in-between" other Lua code is likely almost impossible unless both are visual scripts. Since otherwise you'll end up in Mixin territory, which is not trivial to address. There could however be a special injection mode that wraps everything in a function and gives you a callback for it, which would allow slightly more fine-grained ordering -- you could run your own code, and then run theirs, or run theirs after an if-check, etc. I'm not sure how that would play with other modules doing the same thing, but it'd be better than what we have now at the very least.

On mod compatibility:

I can think of roughly two things that would aid mod compatibility. There's probably more I could think of in the future, but these two should be a pretty good improvement over what we currently have.

The first is that you could introduce a more nuanced way of defining dependencies that includes version numbers. This could also involve introducing "discouraged" or "unsupported" dependency types, for modules explicitly not compatible with each other. People could create "compatibility" furballs that bridge support between two modules, requiring specific version numbers in case either module changes in an incompatible way.

Another thing, which is likely more useful, is the ability for Lua code to query whether an asset exists. Something like "Asset.Exists('DeepForest_MazeAPI')". Ideally there'd be ways to query via either a name or a UUID, or maybe enforce having both. (It's a lot easier to tell at a glance what asset the code is checking based on the name, since I for one don't go memorizing UUIDs.) This would make it possible for modules to "detect" each other by the presence of their assets. There could also be an API for querying module presence and metadata. Either of these would make it possible for conditional scene branching based on whether certain modules are loaded.

This isn't really related to mod compatibility, but if we get an asset querying API, I'd also like to propose an extension to it with a brand new asset type (it's the last time I'll suggest a new asset type I swear -- otherwise I'm going to end up being known as "The New Asset Person"). Namely, I think it'd be neat if we could have some kind of "text" asset type. Something that the game largely ignores but can be queried by scripts to get the contents. I could see it being used to e.g. make a more advanced NPC generator where all the names/species/etc are in their own assets (allowing one to have however many combinations without cluttering the script file). It could also be used to store notes or like, the module's license. There could be a flag in the asset metadata that makes it accessible to scripts so that Finmer can just discard any inaccessible ones from RAM if size is a concern (since you don't need to keep the notes sticking around). Theoretically these could be accessed using "Asset.GetContents('FirstNameList')" (and/or via UUID).

For load order, there is (I believe) one other place load order matters: scripts. They already have a kind of load order configuration but that doesn't really address two modules with scripts loading after the same script. Consider say, two modules adding an exit to Deep Forest at the same location (I already have safeguards against this but pretend they don't exist here). Depending on load order, one of these modules would "win" and become the exit for that location.

To address load order shenanigans it'd probably be worth sorting the modules based on dependencies -- that is, dependencies of a module will load before the module does. This is the same kind of thing a lot of mod loaders (and games with mod support) do, since most games/loaders support replacing assets wholesale.

Speaking of replacing assets wholesale, it might be worth supporting that, at least for non-scene assets. Also, (and this isn't related to replacing assets, but is an extension on patching) it might be useful if string tables could be "patched" in an additive way, so that someone could add more strings to an existing key.

One last thing in the mod compatibility area, you're probably going to need to add a way to view the loaded modules (if such a thing doesn't already exist), and if that happens I think it'd be worth adding some more metadata fields to modules as some extra identification. I think at the very least a description and a version would be good to have. I'd also like a credits field (so I can shove my library attribution in there), but that's not strictly necessary. It could also be worth having some kind of homepage URL field, so it's easier to know where a module came from (for say, reporting a bug).

On the hand-editing:

So back in December, before I got the game running on Linux (and before I knew the Editor had the capability to open furballs), I made a program to unpack furballs. I had seen all these neat little furballs floating around the forum and decided I wanted to be able to look inside (because I didn't feel like breaking out my VM and playing with them), and so I wrote this program to do exactly that. Of course it needed to be able to spit out a project given a furball, so I figured going the reverse wouldn't be a big deal.

The program is written in Java (yes, Java -- not C#), so it can't use Finmer.Core (I'll admit, I didn't think about using Finmer.Core until after I had the program in a working state). It almost perfectly round-trips the Core module. (It's "almost" for probably not the reason you think. I'll elaborate on that more whenever I get around to making a post for this program.)

Anyway, so half of why I wanted to not use the Editor was because of some sunk-cost fallacy shenanigans arising from the fact I had this whole program right here. I also thought that the custom furball assembler would be the cherry on top of this mountain of Finmer engine (ab)use, because why not add another ridiculous detail to the pile?

Editor CLI support:

Some CLI support for the Editor would be great, actually. I probably wouldn't use it (since I have my own questionable program), but I can easily see it being useful for other people, or for say, packaging the Core furball in CI (automated beta builds, anyone?). It could also open the door to automated testing, or at least verifying that your furballs aren't too cursed.

To end my large wall of text, I do have a question. Should I be spoilering my mentions of plot things in future module-related posts? I was operating under the assumption that people would only look for modules after playing the game, but since you spoilered stuff in your reply, I'm wondering if I should be too.

User avatar
Nuntis
Game Creator
Posts: 32
Joined: 11 Nov 2023, 13:27
Contact:

Re: Deep Forest

Post by Nuntis »

I'd really like to have the builtin Lua updated to 5.4 so that I can have access to the bitwise operators.

Unfortunately, upgrading the Lua runtime is not an option at this time. Finmer makes use of the 'function environment' feature (lua_setfenv), which was removed in Lua 5.2 in favor of the _ENV global variable, which is not a direct replacement. Lua 5.2 also makes major changes to how calling conventions work, which would require me to carefully rewrite several portions of the core scene playback system. In short, a lot of work for no gain.

However, I don't think this is actually needed. If bitwise operators are what you need, I can also just implement those myself in C# and export them to Lua directly. I'll admit I've needed them too at some point - the undocumented Flags property of a Creature is a bitmask - which I've worked around with awkward addition/subtraction, which is not quite correct as I'm sure you know haha.

I will put this on my backlog.

I did think about this a bit, and I'd like to suggest making scene patches their own asset type.

Hm. An interesting idea. I'll need to think about this.

The example format you posted seems reasonable; I'm thinking about what this would look like in terms of editor UI. For practical purposes it would probably make sense for it to be a Scene asset still, since large chunks of the scene editor would be reused anyway. But I do agree that allowing multiple patches in one file is convenient and does make a lot of sense.

I think inserting Lua code "in-between" other Lua code is likely almost impossible unless both are visual scripts. Since otherwise you'll end up in Mixin territory, which is not trivial to address.

Yes, I also think this is almost impossible to solve in a way that is both easy to use, and flexible enough to address all use cases. Mixin is really cool tech - it's similar to Harmony for C#, I'm sure something similar can be done for Lua with metatables or indirection trickery - but I kind of feel like it's overkill.

Adding code in front or at the end is likely enough to solve many use cases; 'before' could be used to conditionally execute the original script (e.g. you can toss a return in your patch to stop it).

The first is that you could introduce a more nuanced way of defining dependencies that includes version numbers.

Effectively, just more tools for allowing the game itself to act as a proper mod loader, then. I wonder if it would be really necessary to have a 'mod menu' in-game.

Regarding some of the other things you mentioned: functions for querying the presence of assets and modules seem reasonable, I can see that being useful. I'll think about adding that. Making sure the scene patches resolve in the order of the dependency tree makes sense as a starting point, however then there could still be conflicts.

I wonder if that means that a patch would need an optional 'condition' that could prevent the patch from being applied - e.g. checking for the presence/absence of other modules. Perhaps ideally you could also have one mod suppress a patch from another mod. That would help reduce the need for mod authors to create loose 'compatibility patches'.

Or perhaps this is way too complicated for a silly vore game, heh. I'm not quite sure where the balance lies. I'd love for the tools to be flexible, but then again I'm a little unsure how much use they're actually going to get.

Replacing assets wholesale shouldn't be too much work to implement, I think. The UI would be a bit tricky; there would need to be a method to duplicate the UUID of an asset from another mod. More to think about here.

I think it'd be neat if we could have some kind of "text" asset type

I think, for most purposes, a script asset would cover most of these use cases. You can use them to define global variables like tables or configurations, then query those from your scenes. So you could have a script with a list of first names, another list with last names, then select from both to generate a random name, that sort of thing.

The Core module even contains such a 'text' script - it's called the scratchpad, which is a giant comment block with a bunch of my notes in it. I removed a lot of the contents before shipping, but it is effectively a freeform notepad. You could use these to embed your license attributions, if needed.

it might be useful if string tables could be "patched" in an additive way, so that someone could add more strings to an existing key.

This is already possible. If two string tables define the same string set, then the contents are merged. It is not currently possible to remove/replace string sets from other modules, however.

The program is written in Java (yes, Java -- not C#), so it can't use Finmer.Core

Huh, interesting. So... did you use Finmer's source code to reimplement the furball loader in Java, or did you actually reverse-engineer the file format by hand?

And yes, packing the Core mod in GitHub CI was one of the use cases I had in mind - it'd help test a lot of code, and validate that the project files are at least somewhat sane.

Should I be spoilering my mentions of plot things in future module-related posts?

Hm, after having thought on this for a bit, gonna say no. It's a fair point that modding the game probably means you've at least seen as much of the game as you want to. I'll add a little note to the subforum as a warning.

Hope I didn't miss anything.

User avatar
SyntacticKitsune
Posts: 22
Joined: 05 Jan 2024, 02:44
Contact:

Re: Deep Forest

Post by SyntacticKitsune »

On upgraded Lua and bitwise stuff:

I figured there was probably a reason that Lua 5.1 is in use here, but I wasn't sure what it was, and I figured I'd ask anyway. Earlier (as in, between now and my last reply) I was looking through some of the Lua-related game code and found the Scene sandboxing stuff. I'm guessing that's probably related to the dependency on 5.1. (I was looking for a way to do a general "GetScene()" to grab the scene's asset name, but later realized that wouldn't even help since I'd still have to patch a bunch of scenes manually -- and then I'd have the asset name right there anyway.)

I suppose for bitwise support you could "backport" the bit32 library from Lua 5.2 (or something reminiscent of it)? Or at least, the less complicated stuff anyway. I think the only things I need are bitwise OR (for combining the directions) and bitwise AND (for testing each direction). I could see left shifting being useful though, since I could describe each flag in terms of 1 shifted left a number of times.

I did notice the creature Flags bitmask. I don't think I ever added support for "decomposing" them to my furball manipulation program (mostly since I never needed to inspect them -- maybe if I ever write my own sad little editor...). I think the preysense stuff is also a bitmask, or at least the visual scripting piece is. So there are a few bitmasks floating around.

On text assets and mod menus:

Yeah, a Lua script would likely cover most use-cases there. I guess it just feels a bit weird to me to "hard-code" all the data in a script, but I suppose that does come with the benefits of free patching support -- if it's all in a table, other modules can add their own stuff to that table (or remove things). I suppose the storing-a-bunch-of-strings-picked-randomly-from use case would equally befit a string table too.

I've already added the licenses and such as comments in the corresponding library script files. My "credits" field suggestion was mostly for something a little more user-facing (since otherwise you have to either find the source of the furball or unpack it using the Editor.) The mod menu is probably not necessary; I know lots of games/mod loaders get along just fine without one, and I suppose people don't tend to look at them anyway, so it'd be wasted effort. I just thought it could be nice to have.

On string table patching:

I actually wasn't aware this was already possible. I might make use of that at some point. I see now that the documentation already spells it out too (and in bold), so I suppose I'm just blind.

I don't imagine people will be removing strings, but I suppose replacing them could be something someone would want to do. I don't believe it would come up terribly often though. I didn't initially suggest either of these because I wasn't sure of the uses nor the implementations. (It's really telling that the only use I can immediately think of for replacing strings is to fix typos, which is not a terribly compelling use case.)

On the furball manipulator:

I think I was originally going to try to reverse-engineer it, but mostly I just kind of read through the relevant source code. The encoding of asset IDs as the hash code of their class names is not something I would have been able to reverse-engineer, although I'm not much of a real reverse-engineer anyway. (My original attempt involved hard-coding every ID as I came across them -- a futile effort once I had reached the visual scripting classes).

The worst part of this whole thing though was easily C#'s GUIDs. Did you know that instead of being two longs, they're actually one int, two shorts, and eight bytes (all subject to endianness)?

Another fun part was when I naively implemented the variable enum-sizing, which broke on the few enums that aren't byte-sized. They turned out to all be various visual scripting ones.

And you're almost certainly already aware of this, but the nature of direct-to-binary file formats means that in order to decode them you need every object layout. The codebase I wrote effectively duplicates almost every object's field layout in Finmer.Core. (Shout out to the one time the field ordering broke inheritance.)

Last thing I'll mention here is that I did add custom MIME types for furballs and Finmer projects, so now my system can recognize them. (The "FURBALL" magic is nice here, even if it's easy to fake with a text file. Speaking of text files, one day I want to trick Finmer into trying to load one of these "furballs." I think I'd have to wait until format version 32 for that to actually work though, and I have a sneaking suspicion that's gonna be a while.)

Alright well I'm going to stop adding more text to this reply now, lest it become a wall of text so long it pierces through reality, revealing that the Shore was actually the Backrooms all along (I think Rux would be somewhat disappointed).

User avatar
Nuntis
Game Creator
Posts: 32
Joined: 11 Nov 2023, 13:27
Contact:

Re: Deep Forest

Post by Nuntis »

About Lua:

Yeah, the scene sandboxing stuff is one half of what's pinning me to version 5.1. So each Scene gets its own global namespace, where it can write whatever global variables it wants, without influencing the namespaces of other scenes. That seems like a very desirable property, not so much for security reasons as it is to reduce risks of scenes breaking things in other scenes.

About bitmasks:

I can absolutely backport most of that bit32 library, yes. I can either just write the functions in C# and expose them to Lua (it's not like they're particularly complex), or maybe I can even steal that part of the original Lua 5.2 C library and just inject them into Finmer's version. Either way, I don't expect it to be very complicated to add some bitwise operators.

You're right that the Preysense visual script action uses a bitmask, but that one isn't accessible to scripts, just an implementation detail of how the action saves its configuration. Should hopefully be fine. But if there are actual bitwise operators, then I can also formally document the Flags property; I didn't do that because there currently are no reliable tools for dealing with bitmasks.

About text assets:

Well, I would argue that even if the text is stored in a different type of asset, it's no less hard-coded than if it were stored in a Lua script, no?

Perhaps interestingly, the Core module already (ab)uses string tables for some randomization: there are various points where a random species is selected, which is done by having a string set with lots of species in it, and then using Text.GetString to grab a random one.

I'll think about adding descriptions and legal text fields. I suppose they only really make sense if there will indeed be a mod menu.

About your custom furball tool:

Damn, man. That is dedication haha. You're pretty much duplicating the functionality of the editor then. I can imagine that having to copy over all the data structures, and keeping them in sync with the mainline, is a huge pain.

I do know that GUIDs are a mess on the inside, yeah. Any time I have to deal with them I just view them as a 16-byte array, and I do not want to care about / interpret what's inside. I don't really know why Microsoft's 'registry format GUID' (which is what we're using for the readable-text format) is so complex.

I'm curious what you said earlier about the round trip not being perfect - is something being deserialized incorrectly then? Also, what do you mean with tricking Finmer into reading format version 32? I didn't quite catch that.

User avatar
SyntacticKitsune
Posts: 22
Joined: 05 Jan 2024, 02:44
Contact:

Re: Deep Forest

Post by SyntacticKitsune »

The thing about format version 32 was because in ASCII the first non control character is a space, which corresponds to a decimal value of 32. The following string is a "valid" (for some definition of valid) format 32 furball: "FURBALL ". One could alternatively use a line-ending, but those are below format 19, so Finmer will bail trying to load them. When I was writing the MIME type I just had the thought about "could I get Finmer to load a text file as a furball" since creating such a text file made my system think it was a furball.

Anyway, about the round tripping. I had planned to save this for whenever I got around to making a post for the tool, but I'll just describe it here. Basically, the furballs produced by the editor aren't completely deterministic (at least as far as I can tell) in regards to asset order. I'm pretty sure the editor adds assets in whatever order the filesystem returns them in, which (unless Windows is special in this regard) varies between computers (and certainly between filesystems). (I could be wrong here though; maybe I'm just not accounting for some kind of Windows peculiarity.)

My program perfectly round trips the contents of the furballs, but it cannot create an identical furball from the source project because the asset ordering varies. If I sort the assets first then they are all identical (even down to things like line endings or the fact that all the JSON files are UTF-8 BOM for some reason). I actually have some unit tests set up to guarantee this. (Well, I say some but the real count is 1450. Most of those are comparing individual files for equality though, since it's easier to debug a single file failing than "one of these 300 different assets failed, but who knows which one".)

Post Reply