MapSub works by, in effect, converting a Dream Maker DMP file into the same format as a compiled map, then inserting that data into the original SS13 DMB file. It's a fairly complex task, involving rewriting a large portion of the DMB's data structures, but it seems to work OK, and now anyone, with a bit of effort, can create or modify a map for SS13. I hope to see some creative layouts soon.
One particular difficulty is instances; that's what you get when you add an atom to the map, and then change its variables. For example, SS13 has a security camera object,
/obj/machinery/camera
, by which players can view remote parts of the station. Adding an atom to the map is pretty easy, you just need to know its index number (atom number 32, in this case), but complications arise because each camera needs two variables set: its direction, dir
, and a unique name tag for the camera, called c_tag
.If you open up a DMP file in a text editor, you'll see the values of the instance variables after each atom name in braces, for example:
/obj/machinery/camera{dir = 4; c_tag = "Northern Airlock" }You'll see this format for any atom in the map which doesn't use the default variable values.
The difficulty arises because each of these instance initializations is turned, by the DM compiler, into a tiny, unnamed procedure in the final DMB file. The procedures are executed as the map loads, and set the atoms variables to their desired values. What this means is, if I want to be able to add new instances to a map (and MapSub would be much less useful if it couldn't), then the program must create an initialization procedure for each new instance.
In effect, that means compiling and inserting new DM bytecode into the DMB file; a fairly scary idea, particularly if you're working only with information you've reverse-engineered from the format. Fortunately, setting a variable is one of the simplest procedures: a push of the value (integer, float, or string index) on to the stack, then a pop of that value into the variable. (I'll have a lot more to say about how I think the DM bytecode works in a future post.)
So, MapSub must interpret the DMP format, convert the data into the same format used internally in the DMB file, identify each new instance and build the bytecode to initialize it, then insert that code into the DMB and make sure its referenced correctly. Not to mention decode the string table, append any new strings used, and then re-encode it. Frankly, I'm half-amazed that it works at all, but so far there seems to be no major bugs.
MapSub is written in Java, which has good and bad points. One constant problem is its lack of unsigned data types, a terrible pain when working with file formats (such as DMB) that use unsigned values. There are workarounds, but it's easy to miss a case, and I suspect MapSub will start throwing up bugs if anyone ever starts using, say, more that 32767 different objects. The other disadvantage is the need to download the Java Runtime Environment to run the program. A pity, but on the other hand, half the Windows programs nowadays seem to need some variant of the .NET framework, and it's a chore you only need to do once.
One big benefit of Java is its excellent Collections framework; things like extensible lists, associative maps, and so on. MapSub makes extensive use of these for all the data re-jigging it needs to do. C++ has the STL, which can do all the same stuff, but I find Java's versions rather easier to use.
Very impressive work. I'm looking forward to seeing this evolve.