Understanding and working with MILLA #25867
FunnyMan3595
started this conversation in
Guides
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
MILLA is Paradise's atmospheric system, the successor to LINDA (which, as of writing, still has a few references in the code).
The biggest advantage of MILLA is that it runs as native code outside of the main BYOND thread, which means it can do more work with less lag. However, this also means that it's written in a different language, and that BYOND code may be running at the same time as MILLA is computing an atmos tick.
MILLA for DM authors
If you don't care about the guts of MILLA and just want to know how to interact with turf air from DM code without breaking anything, this section is for you.
There are three safe ways to interact with a turf's air.
.get_readonly_air()
. This will give you a/datum/gas_mixture
that's read-only; attempting to call any modification proc will cause a runtime. This is the best choice if you just want to know what the air is, without needing to adjust it..blind_release_air(datum/gas_mixture/air)
or.blind_set_air(datum/gas_mixture/air)
. The first allows you to push air to the tile, e.g. for a canister breaking. The second allows you to completely replace the air in the tile, e.g. for build mode or turf initialization. If you don't need to care what was in the tile before, this is a good option./datum/milla_safe
. Let's take a look at breathing as an example.I find it's usually best to apply the new type first, and then define it once you know how it's used. To apply it, you just need to create an instance of your new subtype (convention is to name it
milla
), and then call.invoke_async
on it with any arguments you want your code to take, usually starting withsrc
:After that, you just implement
on_run
on your new subtype, taking the arguments you passed into.invoke_async
:The key thing to notice here is
get_turf_air(T)
. This is a proc that/datum/milla_safe
provides, and returns a read-write version of the turf's air. As long as you don't try to store it for later use, you're free to pass it around, like passing it tocarbon.breathe
in the example.Caveats
Although
/datum/milla_safe
tries to make this as transparent as possible, there are a few limitations involved here:/datum/gas_mixture
returned byget_turf_air
is only valid until the end ofon_run
. This means you cannot store it anywhere, or use it in an asynchronous call, lest you cause weird bugs.get_turf_air
and anything it calls cannot sleep. This isn't enforced by DM itself, but one of the checks on your PR will usually fail and alert you to the issue. It's not foolproof, but it's pretty good.invoke_async
is often asynchronous. It will run synchronously if possible, but don't depend on that. If you need to do something after it runs, you'll need to either include that inon_run
, or sleep until you can confirm thaton_run
has finished.Why is this so complicated?
As mentioned, MILLA runs outside of BYOND's main thread. This is great for performance, but it means that your code might run while MILLA is calculating a new atmos tick. In that case, it's safe to read the data from last tick, but any changes you want to make have to wait until MILLA's tick finishes, or they would have unpredictable effects: it might be fine, or it might break things, or it might just not have any effect. To avoid this, MILLA refuses to let you write during a tick (it will generate a runtime), and all three of the safe methods listed above will never try to.
MILLA's code
MILLA is written in Rust. I won't attempt to explain Rust itself here, as there are many guides on it available, notably the ones from the language developers.
MILLA's is located in the top-level
milla/
directory, with the code itself inmilla/src
. The main entry point ismilla/src/api.rs
, which contains the bindings that BYOND uses to invoke it. On the BYOND side, these bindings are located incode/__DEFINES/milla.dm
, and they're primarily called fromcode/controllers/subsystem/SSair.dm
.If you edit MILLA's code, I strongly suggest running
cargo fmt
frommilla/
afterwards, to ensure that our formatting stays consistent.Building MILLA
Building MILLA is very similar to Rust-G and the same setup steps apply. Once that's done, from the
milla/
directory, run either:cargo build --release --target=i686-pc-windows-msvc
(Windows)or:
cargo build --release --target=i686-unknown-linux-gnu
(Linux)The newly-built version of MILLA will automatically be picked up by DM when you run it.
If you need a debug build, omit the --release tag. Debug builds will not be automatically used by DM. To use one, you will need to copy the .dll or .so file from
milla/target/[your platform]/debug
to the base directory, then ensure that you do not have a .dll or .so inmilla/target/[your platform]/release
.When creating a PR, please do not include your .dll or .so files. Instead, create the PR with code changes only, then add a comment to it saying
!build_milla
. A bot will pick that up and securely build binaries from your source code. As general procedure, it's best to re-run that command before any TM update or merge, even if the intervening changes didn't modify the Rust code. It makes it easier to verify that the binaries are current and securely built.Beta Was this translation helpful? Give feedback.
All reactions