Aurora Station code is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE-AGPL3.txt.
Commits with a git authorship date prior to 1420675200 +0000
(2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt.
All commits whose authorship dates are not prior to 1420675200 +0000
are assumed to be licensed under AGPL v3, if you wish to license under GPL v3 please make this clear in the commit message and any added files.
All assets including icons and sound are under a Creative Commons 3.0 BY-SA license unless otherwise indicated.
When does this section apply to me? When you are integrating content that is not licensed under AGPLv3 (code) or Creative Commons 3.0 BY-SA (icons and sounds). The most common application here is for icons and sounds gathered from an external source or repository.
As a contributor, you must do your utmost to pay respect to international copyright law. This means that, if copying code or content from an external source, you must be aware of what license it is published under. And you must ensure that the conditions of said license are followed when integrating the content into the codebase. (Sometimes this is not possible, and the content cannot be used.)
If you cannot locate or reasonably dicern authorship or the license associated with given content, we ask that you not submit it into a pull request.
As a courtesy, attribution of authorship is also encouraged, even if the license of the content does not require this outright. Generally this is done in the PR by declaring
the source of the material, the author(s) (if known), and adding similar attributions to the relevant changelog entry (if applicable). This may be both done in the author
field
or within the plain text describing the change itself.
When reading the sub-sections that follow, note that these will be generalizations, and the specifics will be dictated by the license itself.
Code from most other open source SS13 code bases is GPLv3 or AGPLv3. This means that it's free to be copied without any additional effort. Though any notes of authorship within the code files, if presents, must be kept intact. Be wary of Goon: their code is licensed under CC-BY-SA-NC and is not directly compatible with our codebase's license. Porting Goon code directly is highly discouraged as a result.
If the code is external, or licensed under something else (example being the TGS library), then ensure that the copyright notices within the file(s) (if present) are kept intact,
and that a separate license file (if present in/packaged with the original source) is added to the repository. This generally means that you have to put all of the code you are
porting in this manner into a modules/
sub folder, and stick the license file in there.
If the material is distributed under CC-BY-SA 3.0, then it can go straight into the relevant folders, as long as you attribute the author(s) in the PR and changelog if they can be identified.
In any other case, create a subfolder somewhere in the relevant structure, stick the items in there, and provide a copy of the license with the content. Also, ensure that the license permits the intended use of the content in the appropriate manner.
All pull requests are subject to peer review prior to being merged. After said reviews, they are given a final once-over by a maintainer and then merged if good.
A feature pull request will require two reviews, with one of them being a community developer's. The other review can come from a "trusted reviewer" or a developer. Contributors who have shown that they are capable of properly reviewing pull requests can request the "trusted reviewer" role. There is also a minimum time-out of three days before a feature pull request can be merged. This is to ensure that there is enough time to review and discuss new additionsfrom the game.
A bug fix pull request will require two reviews, if it is to be merged in the first 24 hours, or one following the first 24 hours.
Pull requests should do one thing.
This means that a series of small, dependant pull requests is preferred over one large, monolithic one. Developers and maintainers may request that you break up a pull request into multiple smaller pull requests to enforce this standard at their discretion.
Small pull requests allow for easier reverting, easier (and faster!) reviewing and deliberation, and enable developers in the future to more easily locate all changes relevant to a potential issue.
Changelogs are automatically parsed from within the html/changelogs
folder. A readme file exists there with specific information on how to
create and manage changelogs. All pull requests which contain player-visible changes are required to have a changelog. Any others, like pull
requests containing background system tweaks, minor optimizations, admin systems, etcetera, do not necessarily require a changelog.
Changelogs should be written in a concise and clear manner. There is no need for long winded explanations or too much detail (such as specific numbers, values, etcetera) in a changelog. If necessary, the PR can be tagged as wiki update for a wiki article to be written about it.
There also exist IC changelogs. These are presented in-game as a news article by NanoTrasen, and can be used to provide temporary fluff for in-game changes. To make use of these, simply put a IC changelog header into the description of the PR and write up the contents below it. An example:
A new weapon was commissioned by NanoTrasen. It is currently being tested aboard the NSS Aurora
Absolute pathing has to be used for type, proc, and verb definitions. This is to make searching and reading easier.
An example of properly pathed code:
/obj/item/device/cake
[cake code here]
/obj/item/device/cake/proc/eat_cake()
[proc code here]
An example of badly pathed code:
/obj/item/device/cake
[cake code here]
proc/eat_cake()
[proc code here]
Since the implementation of the Stoned Master Controller, overrides of the New()
proc have effectively become depracted in favour of Initialize()
and LateInitialize()
. In most cases, Initialize()
is a drop-in replacement for New()
, however, there are a few considerations to be taken into account when using this. Specifically, Initialize()
must always return a initialization hint and must always call the superior definition via ..()
. Usually these two are done together, either via the . = ..()
semantics or with explicit return ..()
statements.
LateInitialize()
can be used to manage race conditions during map loading. In the middle of the game, when mapload = FALSE
in Initialize()
, LateInitialize()
is called immediately after the specific atom's Initialize()
call. However, if mapload = TRUE
, which it does during map atom initialization, the LateInitialization()
of an atom is called once all atoms have finished their Initialization()
calls. Note that Initialize()
needs to return INITIALIZE_HINT_LATELOAD
in order for LateInitialization()
to be called in either case.
Refer to the wiki article for further information.
All objects with an applicable type need to be deleted by qdel()
, as opposed to the regular del()
proc. While conducting this action, make sure you remove all possible references to the object you assign for deletion after calling qdel()
. This will enable the garbage collector to handle the objects assigned to it at its own pace, thus reducing lag in the long run.
An example of how to use qdel()
:
/obj/item/plate
var/obj/item/cake/cake
/obj/item/plate/Initialize()
. = ..()
cake = new()
// Eat the cake and destroy the cake object.
/obj/item/plate/proc/eat_cake()
qdel(cake) // Call qdel()
cake = null // Set local reference to null to assist the GC.
The Destroy()
proc for objects should be defined, if there are any special operations that need to be conducted when an object is assigned for destruction with qdel()
. Normally, it would set all object references that that specific item may contain to null, and destroy them as necessary. It is important to know that the best case scenario for the garbage collector is this: an object passed to it should not reference, or be referenced by any other ingame object.
Note that any modified Destroy()
proc must always call its superior definition ..()
and must return a deletion hint. Usually the hint passed down from the superior definition is returned, via . = ..()
or an explicit return ..()
statement.
An example of how to define Destroy()
for an item that needs it:
/obj/item/plate
var/obj/item/cake/cake
/obj/item/plate/Initialize()
. = ..()
cake = new()
/obj/item/plate/Destroy()
if (src.cake) // We potentially have a reference.
qdel(cake) // Delete the referenced item -- this doesn't always have to be done.
cake = null // Set the pointer to null. This is the important bit.
// All pointers that the plate item contains are now null. This will speed up the GC.
return ..() // Return the original definition of the proc.
qdel()
is not capable of handling the following types of objects:
- file
- savefile
- SQLLite object
- list objects.
- turfs
- areas (Note that these shouldn't be deleted at all)
You will have to use the regular del()
proc to delete any object of that type (except for /turf
, which should use ChangeTurf()
).
More details on qdel, Garbage, and Destroy()
are available on the wiki.
All text output to the user, specially if the output operator <<
is used, should be formatted in proper HTML. DM text macros for styling, such as \red
and \blue
, are no longer to be used actively. This will enable the modification of used HTML styling later down the line, via the centralized .css files. It will also enable a switch from an output panel, to other output methods.
For reference, here are the standard span classes for user output, and the correlation between them and the DM text macros:
<span class='danger'></span>
corresponds to\red
and is bold.<span class='warning'></span>
also corresponds to\red
and is not bold.<span class='notice'></span>
corresponds to\blue
and is not bold.
There exist pre-processor macros for using these spans. span(class, text)
which is the equivalent of typing a string that looks like this: "<span class='[class]'>[text]</span>"
and macros such as SPAN_WARNING(text)
, SPAN_NOTICE(text)
, SPAN_DANGER(text)
. Using the SPAN_X() macros is preferred.
The stylesheet available for use within DM can be found in code/stylesheet.dm
.
In order to make Exited()
and Entered()
procs more reliable, the usage of forceMove()
when forcibly moving one item to another location, be it another item or turf, is required. Directly changing an item's loc values will skip over calls to the aforementioned procs, thus making them less useful and more unreliable.
An example of improper item moving:
/proc/some_proc(var/obj/A, var/obj/B)
A.loc = B // Simply move A inside B.
An example of proper item moving:
/proc/some_proc(var/obj/A, var/obj/B)
A.forceMove(B) // This will call A.loc.Exited() and B.Entered().
// The first method does not call either of those.
If at all possible, procs outside of verbs and Topic()
should avoid reliance on usr
, and instead use a custom argument to specify the user and its expected type. This makes it easier to reuse procs in chains where usr
is not always defined, and combats against potential security flaws that relying on usr
can bring.
usr
must also always be validated to be the expected type!
All BYOND procs have a set list of variables which are implicitly defined in each proc. However, the DM compiler will allow you to reuse these names as names for your custom variables. This should be avoided at all costs, to improve the readability and understanding of code.
A list of reserved argument names instantiated with all procs:
vars
being an alias ofsrc.vars
ifsrc
exists.args
usr
src
Due to the amount of instances in the world, utilizing an in world
loop should be avoided in all instances. In large projects, specially when crawling for types that are not optimized internally by BYOND (only core built-in types are), crawling in world
will surmount to a very large blocking loop and will almost certainly trigger the infinite loop warning. This forces the specific callstack to sleep at least once.
Note that a for-each loop without an explicit container specified will default to an in world
loop. This is to say, the following two examples are equivalent:
for (var/client/C)
qdel(C)
for (var/client/C in world)
qdel(C)
All tables for the database should be prefixed according to the following list:
ss13_
for tables in which ingame data is held.discord_
for tables in which BOREALIS data is held.
Due to our current situation with 5 different HTML UI systems we are now enforcing a policy that all new UIs should be made using the TGUI system. This policy also applies to editing existing UIs, with the following exceptions:
- Modification is security / severe bug fix.
- It is typo fix.
- Touched UI file is too large.
- TGUI can't accommodate that type of UI.
All globals must use the defines found in __defines/_globals.dm
. This is to store globals inside the Global Controller, allowing us to view and edit them at runtime. Here are a few examples.
GLOBAL_VAR(thing)
will create a global variable var/thing
accessed with GLOB.thing
.
GLOBAL_LIST_INIT(list_of_stuff, list("stuff", "thing"))
will create a global list var/list/list_of_stuff = list("stuff, thing")
accessed with GLOB.list_of_stuff
.
GLOBAL_DATUM_INIT(cake, /datum/cake, new)
will create a global /datum/cake/cake
and new
it, accessed with GLOB.cake
.