Difference between revisions of "User:Warriorstar/Guide to Mapmerge"
Warriorstar (talk | contribs) |
Warriorstar (talk | contribs) |
||
(5 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
{{Wip|assign=Warriorstar}} | |||
'''NOTE: This guide is not yet complete.''' | |||
Welcome to the guide to Mapmerge! This guide is divided up into two sections: | |||
the '''theory''' section, which explains how Mapmerge works, and how it's | |||
necessary, and the '''instructions''' section, which explains how to use | |||
Mapmerge and handle merge conflicts. You don't need to read the first part if | |||
you just want to learn how to use Mapmerge, but you might learn something | |||
interesting, and it's always helpful to know what's going on under the hood when | |||
troubleshooting problems. | |||
== Theory: Why do we need Mapmerge? == | == Theory: Why do we need Mapmerge? == | ||
Paradise's source code, including its maps, are stored in a git repository. Git | |||
is a fantastic tool for keeping track of changes to files over time, but it has | |||
no "knowledge" of the contents of the files. In addition, when multiple people | |||
make changes to the same file, it is possible for git to not know how to combine | |||
these changes in an acceptable way. | |||
=== Git and Text Files === | |||
==== Git and Text Files: Simple Merges ==== | |||
Let's start with a plain text example. Let's say we have a file that looks like | |||
this in a git repository. | |||
<pre> | |||
This is the start of the file! | |||
This is the middle of the file! | |||
This is the end of the file! | |||
</pre> | |||
Now let's say we have two programmers, Alice and Bob. Alice adds a sentence to | |||
the file, between the start and the middle: | |||
<pre> | |||
This is the start of the file! | |||
This is before the middle of the file! <--- Alice adds this line | |||
This is the middle of the file! | |||
This is the end of the file! | |||
</pre> | |||
Bob adds a sentence to the file, between the middle and the end: | |||
<pre> | |||
This is the start of the file! | |||
This is the middle of the file! | |||
This is before the end of the file! <--- Bob adds this line | |||
This is the end of the file! | |||
</pre> | |||
Now let's say that Bob merges in Alice's changes, via `git merge`. Now, on Bob's | |||
branch, the file contains both changes: | |||
<pre> | |||
This is the start of the file! | |||
This is before the middle of the file! | |||
This is the middle of the file! | |||
This is before the end of the file! | |||
This is the end of the file! | |||
</pre> | |||
This merged without issue because git has enough information about the context | |||
between the changes. It can "tell" that Alice's change was made above the middle | |||
line, and Bob's change was made below it, which is enough for git to pull both | |||
changes in without requiring any help from the user. | |||
==== Git and Text Files: Merge Conflicts ==== | |||
Now let's look at an example that causes a merge conflict. | |||
=== Git and Map Files === | === Git and Map Files === | ||
==== Git and Map Files: Silent | That is the basic concept of merging and merge conflicts. When git can tell that | ||
changes are distinct and in separate parts of the file, it can merge them | |||
largely without incident. However, if these changes touch the same part of the | |||
file, then merge conflicts occur, and git has no choice but to ask the user to | |||
resolve the conflict manually before committing the changes. | |||
Now we are going to see how this applies to map files. | |||
When git can tell that changes are distinct and in separate parts of the file, | |||
it can merge them largely without incident. However, if these changes touch the | |||
same part of the file, then merge conflicts occur, and git has no choice but to | |||
ask the user to resolve the conflict manually before committing the changes. | |||
=== Structure of Map Files === | |||
First, we are going to briefly go over the different parts of a map file. Map | |||
files end with the <code>.dmm</code> extension, and are plain text. They consist | |||
of three separate parts: | |||
#. The preamble, which we can ignore. | |||
#. The key definitions. | |||
#. The tile definitions. | |||
Key definitions look like this: | |||
<pre> | |||
"adw" = ( | |||
/obj/structure/cable{ | |||
d1 = 4; | |||
d2 = 8; | |||
icon_state = "4-8" | |||
}, | |||
/obj/machinery/hologram/holopad, | |||
/turf/simulated/floor/plasteel{ | |||
icon_state = "dark" | |||
}, | |||
/area/station/security/armory/secure) | |||
</pre> | |||
It starts with the ''key'', followed by a list of ''prefabs''. A prefab can be | |||
as simple as a type path, or it can include modifications to the type, known as | |||
''varedits'', which can change values of the object, such as their icon state. | |||
Depending on the size and design of the station map, there can be anywhere from | |||
100 to 10,000 unique keys. If every single tile on a 255x255x1 map is unique, | |||
this means there would be 62,025 keys in total. | |||
Keys are assigned at random by most tools in order to prevent collisions where | |||
two people add a unique key to a map at the same time. Keys are technically | |||
integers, but are represented as strings of letters in most cases. | |||
The tile definitions assign a key to every single coordinate on the map. The | |||
tile definitions look like this: | |||
<pre> | |||
(1,1,1) = {" | |||
aaa | |||
aaa | |||
aaa | |||
adw | |||
... // 255 keys per column 1 | |||
"} | |||
(2,1,1) = {" | |||
aaa | |||
aaa | |||
aaa | |||
dAc | |||
... // 255 keys per column 2 | |||
"} | |||
</pre> | |||
As you can see, we start with the first tile on the first z-index, (1, 1, 1), | |||
and then list all 255 keys that we want to assign to the tiles on that row. Then | |||
we start again with the second row, (2, 1, 1) and so on, until we get to (255, | |||
1, 1). | |||
When creating a new unique tile, nearly all mapping tools will choose a new key | |||
at random, in order to prevent collisions. If Alice and Bob are working on a | |||
map, and the last used key is "aaa", then having the next key for both of them | |||
be "aab" would immediately cause conflicts. So Alice's map editor may choose | |||
"ceD" and Bob's will choose "gJU". | |||
=== Git and Map Files: Silent Merge Errors === | |||
Now let's say Alice and Bob are working on a map. To keep things simple, we'll | |||
make it a small, 2x2 map, with only a handful of objects. This is the original | |||
map. | |||
{| class="wikitable" | |||
|+Silent Map Merge Failure | |||
|- | |||
|Original Version | |||
|[[File:[email protected]]] | |||
|- | |||
|Alice's Version | |||
|[[File:[email protected]]] | |||
|- | |||
|Bob's Version | |||
|[[File:[email protected]]] | |||
|- | |||
|Silent Merge Error | |||
|[[File:[email protected]]] | |||
|} | |||
=== Git and Map Files: Non-Conformant Maps === | |||
<pre> | |||
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE | |||
"a" = ( | |||
/obj/item/crowbar, | |||
/obj/item/wrench, | |||
/turf, | |||
/area/space) | |||
"g" = ( | |||
/obj/item/weldingtool, | |||
/turf, | |||
/area/space) | |||
"I" = ( | |||
/obj/item/weldingtool, | |||
/turf, | |||
/area/space) | |||
"S" = ( | |||
/obj/item/crowbar, | |||
/obj/item/wrench, | |||
/turf, | |||
/area/space) | |||
(1,1,1) = {" | |||
S | |||
a | |||
"} | |||
(2,1,1) = {" | |||
g | |||
I | |||
"} | |||
</pre> | |||
=== Git and Map Files: Merge Conflicts === | |||
<pre> | |||
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE | |||
"a" = ( | |||
/turf/simulated/floor/plasteel, | |||
/area/space) | |||
<<<<<<< HEAD | |||
"h" = ( | |||
/obj/item/weldingtool, | |||
/turf, | |||
/area/space) | |||
"X" = ( | |||
/obj/item/wrench, | |||
/turf, | |||
======= | |||
"v" = ( | |||
/obj/item/wrench, | |||
/turf/simulated/floor/plasteel, | |||
/area/space) | |||
"E" = ( | |||
/obj/item/weldingtool, | |||
/turf/simulated/floor/plasteel, | |||
>>>>>>> mapmerge_wiki_guide-2-alice | |||
/area/space) | |||
(1,1,1) = {" | |||
v | |||
E | |||
"} | |||
(2,1,1) = {" | |||
h | |||
X | |||
"} | |||
</pre> | |||
== Instructions: How do I use Mapmerge? == | == Instructions: How do I use Mapmerge? == |
Latest revision as of 20:32, 31 July 2024
Assigned to:Warriorstar
NOTE: This guide is not yet complete.
Welcome to the guide to Mapmerge! This guide is divided up into two sections: the theory section, which explains how Mapmerge works, and how it's necessary, and the instructions section, which explains how to use Mapmerge and handle merge conflicts. You don't need to read the first part if you just want to learn how to use Mapmerge, but you might learn something interesting, and it's always helpful to know what's going on under the hood when troubleshooting problems.
Theory: Why do we need Mapmerge?
Paradise's source code, including its maps, are stored in a git repository. Git is a fantastic tool for keeping track of changes to files over time, but it has no "knowledge" of the contents of the files. In addition, when multiple people make changes to the same file, it is possible for git to not know how to combine these changes in an acceptable way.
Git and Text Files
Git and Text Files: Simple Merges
Let's start with a plain text example. Let's say we have a file that looks like this in a git repository.
This is the start of the file! This is the middle of the file! This is the end of the file!
Now let's say we have two programmers, Alice and Bob. Alice adds a sentence to the file, between the start and the middle:
This is the start of the file! This is before the middle of the file! <--- Alice adds this line This is the middle of the file! This is the end of the file!
Bob adds a sentence to the file, between the middle and the end:
This is the start of the file! This is the middle of the file! This is before the end of the file! <--- Bob adds this line This is the end of the file!
Now let's say that Bob merges in Alice's changes, via `git merge`. Now, on Bob's branch, the file contains both changes:
This is the start of the file! This is before the middle of the file! This is the middle of the file! This is before the end of the file! This is the end of the file!
This merged without issue because git has enough information about the context between the changes. It can "tell" that Alice's change was made above the middle line, and Bob's change was made below it, which is enough for git to pull both changes in without requiring any help from the user.
Git and Text Files: Merge Conflicts
Now let's look at an example that causes a merge conflict.
Git and Map Files
That is the basic concept of merging and merge conflicts. When git can tell that changes are distinct and in separate parts of the file, it can merge them largely without incident. However, if these changes touch the same part of the file, then merge conflicts occur, and git has no choice but to ask the user to resolve the conflict manually before committing the changes.
Now we are going to see how this applies to map files. When git can tell that changes are distinct and in separate parts of the file, it can merge them largely without incident. However, if these changes touch the same part of the file, then merge conflicts occur, and git has no choice but to ask the user to resolve the conflict manually before committing the changes.
Structure of Map Files
First, we are going to briefly go over the different parts of a map file. Map
files end with the .dmm
extension, and are plain text. They consist
of three separate parts:
- . The preamble, which we can ignore.
- . The key definitions.
- . The tile definitions.
Key definitions look like this:
"adw" = ( /obj/structure/cable{ d1 = 4; d2 = 8; icon_state = "4-8" }, /obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel{ icon_state = "dark" }, /area/station/security/armory/secure)
It starts with the key, followed by a list of prefabs. A prefab can be as simple as a type path, or it can include modifications to the type, known as varedits, which can change values of the object, such as their icon state. Depending on the size and design of the station map, there can be anywhere from 100 to 10,000 unique keys. If every single tile on a 255x255x1 map is unique, this means there would be 62,025 keys in total.
Keys are assigned at random by most tools in order to prevent collisions where two people add a unique key to a map at the same time. Keys are technically integers, but are represented as strings of letters in most cases.
The tile definitions assign a key to every single coordinate on the map. The tile definitions look like this:
(1,1,1) = {" aaa aaa aaa adw ... // 255 keys per column 1 "} (2,1,1) = {" aaa aaa aaa dAc ... // 255 keys per column 2 "}
As you can see, we start with the first tile on the first z-index, (1, 1, 1), and then list all 255 keys that we want to assign to the tiles on that row. Then we start again with the second row, (2, 1, 1) and so on, until we get to (255, 1, 1).
When creating a new unique tile, nearly all mapping tools will choose a new key at random, in order to prevent collisions. If Alice and Bob are working on a map, and the last used key is "aaa", then having the next key for both of them be "aab" would immediately cause conflicts. So Alice's map editor may choose "ceD" and Bob's will choose "gJU".
Git and Map Files: Silent Merge Errors
Now let's say Alice and Bob are working on a map. To keep things simple, we'll make it a small, 2x2 map, with only a handful of objects. This is the original map.
Original Version | |
Alice's Version | |
Bob's Version | |
Silent Merge Error |
Git and Map Files: Non-Conformant Maps
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( /obj/item/crowbar, /obj/item/wrench, /turf, /area/space) "g" = ( /obj/item/weldingtool, /turf, /area/space) "I" = ( /obj/item/weldingtool, /turf, /area/space) "S" = ( /obj/item/crowbar, /obj/item/wrench, /turf, /area/space) (1,1,1) = {" S a "} (2,1,1) = {" g I "}
Git and Map Files: Merge Conflicts
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( /turf/simulated/floor/plasteel, /area/space) <<<<<<< HEAD "h" = ( /obj/item/weldingtool, /turf, /area/space) "X" = ( /obj/item/wrench, /turf, ======= "v" = ( /obj/item/wrench, /turf/simulated/floor/plasteel, /area/space) "E" = ( /obj/item/weldingtool, /turf/simulated/floor/plasteel, >>>>>>> mapmerge_wiki_guide-2-alice /area/space) (1,1,1) = {" v E "} (2,1,1) = {" h X "}