summary: refactored a deprecated minecraft plugin that converts real DMX512 packets sent via Art-Net into in-game events.
what is this?
I wanted to design lighting for a concert on my minecraft server in a high-level way without using redstone. after looking around to find some sort of tool to fulfill this purpose, I found an old out-of-date github repository that served this exact purpose. it did significantly more than I expected: it hooked into various professional-grade lighting controllers over a local network and let you control lights how you actually would in real life, using the DMX512 protocol.
what did you do?
I updated the original creator's legacy code using NMS (Net Minecraft Server) to a modern version that uses the Paper API, which is more stable and consistent. usage of NMS is discouraged as it works by accessing Mojang's obfuscated code which is purposefully changed between versions to make it harder to understand--as such, every time the game updates, you must go through and figure out how to fix your code. the advantage of NMS is that you can do significantly more with it if you are willing; however, for this plugin's purposes, usage of NMS is no longer necessary as the Paper API now includes many of the required functions.
to update the code I ultimately had to completely replace many of the functions and preexisting APIs, such as the ones controlling guardians and end crystals.
i also added additional functionality to make the plugin friendlier for users. originally, you had to create all of your fixtures in .json files that looked like this:
[
{
"DmxId": 1,
"DmxType": "Beacon",
"Location": {
"x": 0.5,
"y": 100.0,
"z": 0
},
"Yaw": 25,
"OffBlock": "RED_CONCRETE"
},
{
"DmxId": 11,
"DmxType": "ParticleFlare",
"Location": {
"x": 0.5,
"y": 100.0,
"z": 0
}
},
{
"DmxId": 19,
"DmxType": "ParticleFlare",
"Location": {
"x": 0,
"y": 100.5,
"z": 0
},
"Yaw": 25
},
{
"DmxId": 27,
"DmxType": "ParticleFlare",
"Location": {
"x": 0,
"y": 100.5,
"z": 0
},
"Yaw": -25
}
]
instead, you can now use signs in-game using this syntax, and it will automatically a fixture at the sign's location (or offset from the sign's location):
[Fixture]
Address
FixtureName
XOffset YOffset ZOffset
what is the DMX512 protocol?
the DMX512 protocol is not designed using typical programming principles. it uses 1-indexing; because it uses 1-indexing, its array size is actually 513; there is no automatic error checking and despite that it is used for pyrotechnics; the term 'address' does not have the standard definition that is used in programming and instead only refers to the starting index within an array.
DMX data is sent continuously as packets containing the complete set of data for the show in each packet. each packet contains 513 bytes of data, reserving the start byte (at array[0]) for a start code, which is 0x00.
the protocol was originally used to control dimmers, which are lights that basically only turn on and off, though if you adjust the amount of power being given to the bulb you can control its intensity/brightness. given this context, the protocol provided a useful way to control the intensity of up to 512 lights using a 0-255 spectrum.
for instance, if i had 3 lights and i wanted light #1 to be off, light #2 to be at half brightness, and light #3 to be at full brightness, my dmx output would send an array that looked something like [0, 128, 255, 0, 0, 0, 0, ...]
each light would be set to listen to a specific index in the array, and it would only listen to one address of the array throughout the show. light 1 would listen to array[1], light 2 array[2], light 3 array[3]. again, array[0] is not being used, it is only sending a start code value.
this is very convenient! however, lights became more complex and no longer wanted to only control their intensity. let's take my favorite light, the ETC ColorSource PAR: in its default mode, it needs to be able to access up to 5 values, as it a color-changing and strobing light. the manufacturers determined that the light would read five addresses from the array in this order: intensity, red, green, blue, strobe.
now, let's say we have just three ColorSource PARs in our show and we want to assign addresses to them. because our array is 1-indexed and the ColorSource PAR wants five values, we will have to assign the lights the following addresses: 1, 6, 11. turning all three lights red at full brightness will now look something like this in our array: [0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, ...]
however, ETC is not the only manufacturers creating lights! other manufacturers are creating them too, and because no standard has been setting up, they are assigning each value to be read from different channels! you might have one manufacturer assigning the channels in the order of red, green, blue, intensity, strobe instead!
this is solved by lighting controller software which allows you to 'patch' lights. many come pre-loaded with popular fixtures that have already handled these demanded values, which are referred to as 'channels.' all you need to do is input the starting address of your set of fixtures (for example, in our earlier example, our CS PARs start at address 1) and if you give it your number of fixtures it will spew out the numbers you need to address the rest of your lights at (6, 11).
(note: in lighting, 'address' typically only refers to the starting index that data for a light is contained at. for instance, a CS PAR is said to use 5 channels, not 5 addresses. instead, we say the lights' addresses are 1, 6, 11 -- NOT every value in the range [1, 16]. I find this is more intuitive if you do NOT have a programming background.)
but another problem pops up: more types of values are desired than the simple IRGBS settings--take the Chauvet Rogue R2 Wash, a popular moving light. this light requires up to 56 different values from the array! the light's pan and tilt settings are so sensitive that it can't work with the 8-bit values in the array, so it takes two adjacent addresses and makes the first the high bit and the second the low bit of a 16-bit value.
now that we are using 56 different addresses from the array, if we have 9 of these fixtures, it leaves us with 8 more addresses to spare for other lights. this is extremely limiting for large-scale shows--how do we solve this problem while maintaining compatibility with our old lights?
lighting consoles developed the concept of 'universes,' which is an abstraction that allows you to create lights with starting addresses greater than 512. it follows a formula that looks like (512 * (universe_number-1)) + address
. for instance, if you have maxed out your first/default universe, your console can translate address 513 to universe 2 / address 1. alternatively, many also simply allow you to type in 2/1 as the syntax for universe 2, address 1.
actual fixtures wouldn't know what to do if directly fed this information, so lighting consoles have different output ports for different universes. you would chain all of the lights in universe 1 together, then chain all of the lights in universe 2 together to a separate output--you would then address those lights using values between 1 and 512 as normal.
final notes
this plugin currently works for minecraft 1.21.6; please see the repository's wiki for my existing documentation. if you have any questions, feel free to contact me: my email is on this website's front page.