code:
# Hard Deletes
1. [What is hard deletion](#What-is-hard-deletion)
2. [Causes of hard deletes](#causes-of-hard-deletes)
3. [Detecting hard deletes](#detecting-hard-deletes)
4. [Techniques for fixing hard deletes](#techniques-for-fixing-hard-deletes)
5. [Help my code is erroring how fix](#help-my-code-is-erroring-how-fix)

## What is Hard Deletion
Hard deletion is a very expensive operation that basically clears all references to some "thing" from memory. Objects that undergo this process are referred to as hard deletes, or simply harddels

What follows is a discussion of the theory behind this, why we would ever do it, and the what we do to avoid doing it as often as possible

I'm gonna be using words like references and garbage collection, but don't worry, it's not complex, just a bit hard to pierce

### Why do we need to Hard Delete?

Ok so let's say you're some guy called Jerry, and you're writing a programming language

You want your coders to be able to pass around objects without doing a full copy. So you'll store the pack of data somewhere in memory

```dm
/someobject
    var/id = 42
    var/name = "some shit"
```

Then you want them to be able to pass that object into say a proc, without doing a full copy. So you let them pass in the object's location in memory instead
This is called passing something by reference

```dm
someshit(someobject) //This isn't making a copy of someobject, it's passing in a reference to it
```

This of course means they can store that location in memory in another object's vars, or in a list, or whatever

```dm
/datum
    var/reference

/proc/someshit(mem_location)
    var/datum/some_obj = new()
    some_obj.reference = mem_location
```

But what happens when you get rid of the object we're passing around references to? If we just cleared it out from memory, everything that holds a reference to it would suddenly be pointing to nowhere, or worse, something totally different!

So then, you've gotta do something to clean up these references when you want to delete an object

We could hold a list of references to everything that references us, but god, that'd get really expensive wouldn't it

Why not keep count of how many times we're referenced then? If an object's ref count is ever 0, nothing whatsoever cares about it, so we can freely get rid of it

But if something's holding onto a reference to us, we're not gonna have any idea where or what it is

So I guess you should scan all of memory for that reference?

```dm
del(someobject) //We now need to scan memory until we find the thing holding a ref to us, and clear it
```

This pattern is about how BYOND handles this problem of hanging references, or Garbage Collection

It's not a broken system, but as you can imagine scanning all of memory gets expensive fast

What can we do to help that?

### How we can avoid hard deletes

If hard deletion is so slow, we're gonna need to clean up all our references ourselves

In our codebase we do this with `/datum/proc/Destroy()`, a proc called by `qdel()`, whose purpose I will explain later

This procs only job is cleaning up references to the object it's called on. Nothing more, nothing else. Don't let me catch you giving it side effects

There's a long long list of things this does, since we use it a TON. So I can't really give you a short description. It will always move the object to nullspace though

## Causes Of Hard Deletes

Now that you know the theory, let's go over what can actually cause hard deletes. Some of this is obvious, some of it's much less so.

The BYOND reference has a list [Here](https://secure.byond.com/docs/ref/#/DM/garbage), but it's not a complete one

* Stored in a var
* An item in a list, or associated with a list item
* Has a tag
* Is on the map (always true for turfs)
* Inside another atom's contents
* Inside an atom's vis_contents
* A temporary value in a still-running proc
* Is a mob with a key
* Is an image object attached to an atom

Let's briefly go over the more painful ones yeah?

### Sleeping procs

Any proc that calls `sleep()`, `spawn()`, or anything that creates a seperate "thread" (not technically a thread, but it's the same in these terms. Not gonna cause any race conditions tho) will hang references to any var inside it. This includes the usr it started from, the src it was called on, and any vars created as a part of processing

### Static vars

`/static` and `/global` vars count for this too, they'll hang references just as well as anything. Be wary of this, these suckers can be a pain to solve

### Range() and View() like procs

Some internal BYOND procs will hold references to objects passed into them for a time after the proc is finished doing work, because they cache the returned info to make some code faster. You should never run into this issue, since we wait for what should be long enough to avoid this issue as a part of garbage collection

This is what `qdel()` does by the by, it literally just means queue deletion. A reference to the object gets put into a queue, and if it still exists after 5 minutes or so, we hard delete it

### Walk() procs

Calling `walk()` on something will put it in an internal queue, which it'll remain in until `walk(thing, 0)` is called on it, which removes it from the queue

This sort is very cheap to harddel, since BYOND prioritizes checking this queue first when it's clearing refs, but it should be avoided since it causes false positives

You can read more about how BYOND prioritizes these things [Here](https://www.patreon.com/posts/diving-for-35855766)

## Detecting Hard Deletes

For very simple hard deletes, simple inspection should be enough to find them. Look at what the object does during `Initialize()`, and see if it's doing anything it doesn't undo later.
If that fails, search the object's typepath, and look and see if anything is holding a reference to it without regard for the object deleting

BYOND currently doesn't have the capability to give us information about where a hard delete is. Fortunately we can search for most all of then ourselves.
The procs to perform this search are hidden behind compile time defines, since they'd be way too risky to expose to admin button pressing

If you're having issues solving a harddel and want to perform this check yourself, go to `_compile_options.dm` and uncomment `TESTING`, `REFERENCE_TRACKING`, and `GC_FAILURE_HARD_LOOKUP`

You can read more about what each of these do in that file, but the long and short of it is if something would hard delete our code will search for the reference (This will look like your game crashing, just hold out) and print information about anything it finds to the runtime log, which you can find inside the round folder inside `/data/logs/year/month/day`

It'll tell you what object is holding the ref if it's in an object, or what pattern of list transversal was required to find the ref if it's hiding in a list of some sort

## Techniques For Fixing Hard Deletes

Once you've found the issue, it becomes a matter of making sure the ref is cleared as a part of Destroy(). I'm gonna walk you through a few patterns and discuss how you might go about fixing them

### Our Tools

First and simplest we have `Destroy()`. Use this to clean up after yourself for simple cases

```dm
/someobject/Initialize(mapload)
    . = ..()
    GLOB.somethings += src //We add ourselves to some global list

/someobject/Destroy()
    GLOB.somethings -= src //So when we Destroy() clean yourself from the list
    return ..()
```

Next, and slightly more complex, pairs of objects that reference each other

This is helpful when for cases where both objects "own" each other

```dm
/someobject
    var/someotherobject/buddy

/someotherobject
    var/someobject/friend

/someobject/Initialize(mapload)
    if(!buddy)
        buddy = new()
        buddy.friend = src

/someotherobject/Initialize(mapload)
    if(!friend)
        friend = new()
        friend.buddy = src

/someobject/Destroy()
    if(buddy)
        buddy.friend = null //Make sure to clear their ref to you
        buddy = null //We clear our ref to them to make sure nothing goes wrong

/someotherobject/Destroy()
    if(friend)
        friend.buddy = null //Make sure to clear their ref to you
        friend = null //We clear our ref to them to make sure nothing goes wrong
```

Something similar can be accomplished with `QDELETED()`, a define that checks to see if something has started being `Destroy()`'d yet, and `QDEL_NULL()`, a define that `qdel()`'s a var and then sets it to null

Now let's discuss something a bit more complex, weakrefs

You'll need a bit of context, so let's do that now

BYOND has an internal bit of behavior that looks like this

`var/string = "\ref[someobject]"`

This essentially gets that object's position in memory directly. Unlike normal references, this doesn't count for hard deletes. You can retrieve the object in question by using `locate()`

`var/someobject/someobj = locate(string)`

This has some flaws however, since the bit of memory we're pointing to might change, which would cause issues. Fortunately we've developed a datum to handle worrying about this for you, `/datum/weakref`

You can create one using the `WEAKREF()` proc, and use weakref.resolve() to retrieve the actual object

This should be used for things that your object doesn't "own", but still cares about

For instance, a paper bin would own the paper inside it, but the paper inside it would just hold a weakref to the bin

There's no need to clean these up, just make sure you account for it being null, since it'll return that if the object doesn't exist or has been queued for deletion

```dm
/someobject
    var/datum/weakref/our_coin

/someobject/proc/set_coin(/obj/item/coin/new_coin)
    our_coin = WEAKREF(new_coin)

/someobject/proc/get_value()
    if(!our_coin)
        return 0

    var/obj/item/coin/potential_coin = our_coin.resolve()
    if(!potential_coin)
        our_coin = null //Remember to clear the weakref if we get nothing
        return 0
    return potential_coin.value
```

Now, for the worst case scenario

Let's say you've got a var that's used too often to be weakref'd without making the code too expensive

You can't hold a paired reference to it because it's not like it would ever care about you outside of just clearing the ref

So then, we want to temporarily remember to clear a reference when it's deleted

This is where I might lose you, but we're gonna use signals

`qdel()`, the proc that sets off this whole deletion business, sends a signal called `COMSIG_PARENT_QDELETING`

We can listen for that signal, and if we hear it clear whatever reference we may have

Here's an example

```dm
/somemob
    var/mob/target

/somemob/proc/set_target(new_target)
    if(target)
        UnregisterSignal(target, COMSIG_PARENT_QDELETING) //We need to make sure any old signals are cleared
    target = new_target
    if(target)
        RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/clear_target) //Call clear_target if target is ever qdel()'d

/somemob/proc/clear_target(datum/source)
    SIGNAL_HANDLER
    set_target(null)
```

This really should be your last resort, since signals have some limitations. If some subtype of somemob also registered for parent_qdeleting on the same target you'd get a runtime, since signals don't support it

But if you can't do anything else for reasons of conversion ease, or hot code, this will work

## Help My Code Is Erroring How Fix

First, do a quick check.

Are you doing anything to the object in `Initialize()` that you don't undo in `Destroy()`? I don't mean like, setting its name, but are you adding it to any lists, stuff like that

If this fails, you're just gonna have to read over this doc. You can skip the theory if you'd like, but it's all pretty important for having an understanding of this problem


code:
## MAPS

/tg/station currently has five maps in rotation.
* [MetaStation](https://tgstation13.org/wiki/MetaStation)
* [DeltaStation](https://tgstation13.org/wiki/DeltaStation)
* [IceboxStation](https://tgstation13.org/wiki/IceboxStation)
* [KiloStation](https://tgstation13.org/wiki/KiloStation)
* [TramStation](https://tgstation13.org/wiki/Tramstation)

Outdated or removed maps.
* [BoxStation](https://tgstation13.org/wiki/Boxstation)
* [PubbyStation](https://tgstation13.org/wiki/PubbyStation)
* [DonutStation](https://tgstation13.org/wiki/Donutstation)
* [AsteroidStation](https://tgstation13.org/wiki/AsteroidStation)
* [BirdboatStation](https://tgstation13.org/wiki/BirdboatStation)
* [CereStation](https://tgstation13.org/wiki/CereStation)
* [DiscStation](https://tgstation13.org/wiki/Discstation)
* [DreamStation](https://tgstation13.org/wiki/Dreamstation)
* [EfficiencyStation](https://tgstation13.org/wiki/EfficiencyStation)
* [MiniStation](https://tgstation13.org/wiki/MiniStation)
* [OmegaStation](https://tgstation13.org/wiki/OmegaStation)

Debug maps.
* [RuntimeStation](https://tgstation13.org/wiki/RuntimeStation)
* [MultiZ](https://tgstation13.org/wiki/MultiZ)

All maps have their own code file that is in the base of the _maps directory. Maps are loaded dynamically when the game starts. Follow this guideline when adding your own map, to your fork, for easy compatibility.

The map that will be loaded for the upcoming round is determined by reading data/next_map.json, which is a copy of the json files found in the _maps tree. If this file does not exist, the default map from config/maps.txt will be loaded. Failing that, BoxStation will be loaded. If you want to set a specific map to load next round you can use the Change Map verb in game before restarting the server or copy a json from _maps to data/next_map.json before starting the server. Also, for debugging purposes, ticking a corresponding map's code file in Dream Maker will force that map to load every round.

If you are hosting a server, and want randomly picked maps to be played each round, you can enable map rotation in [config.txt](config/config.txt) and then set the maps to be picked in the [maps.txt](config/maps.txt) file.

Anytime you want to make changes to a map it's imperative you use the [Map Merging tools](https://tgstation13.org/wiki/Map_Merger)

## AWAY MISSIONS

/tg/station supports loading away missions however they are disabled by default.

Map files for away missions are located in the _maps/RandomZLevels directory. Each away mission includes it's own code definitions located in /code/modules/awaymissions/mission_code. These files must be included and compiled with the server beforehand otherwise the server will crash upon trying to load away missions that lack their code.

To enable an away mission open `config/awaymissionconfig.txt` and uncomment one of the .dmm lines by removing the #. If more than one away mission is uncommented then the away mission loader will randomly select one the enabled ones to load.

repo_name: AnalWerewolf/sundown

license: mit


code:
Welcome to this short guide to the POLICY config mechanism.

You are probably reading this guide because you have been informed your antagonist or ghost role needs to support policy configuration.

## Requirements
It is a requirement of /tg/station development that all ghost roles, antags, minor antags and event mobs of any kind must support the policy system when implemented.

## What is policy configuration
Policy configuration is a json file that the administrators of a server can edit, which contains a dictionary of keywords -> string message.

The policy text for a specific keyword should be displayed when relevant and appropriate, to allow server administrators to define the broad strokes of policy for some feature or mob.

It is okay to provide a default text when the config is not set, but you are required to provide the config in all cases of a ghost role or an antagonist, or minor event.

If you're in doubt about needing to support policy config, I suggest doing it anyway. This should replace all flavour text, ghost spawn messages and so forth that the player (i.e client) sees upon entering the role, mob, or feature that is meant to dictate how they are permitted to be played as/with.

## What does this mean?

Concretely, it means you need to display to the client taking control of the mob or ghost role a string of text, pulled via keyword from the policy config system.

You can access the string of text through the `get_policy(keyword)` proc, this takes a single keyword argument, which should be a text string unique to your feature.

This will return a configured string of text, or blank/null if no policy string is set.

This is also accessible to the user if they use `/client/verb/policy()` which will display to them a list of all the policy texts for keywords applicable to the mob, you can add/modify the list of keywords by modifying the `get_policy_keywords()` proc of a mob type where that is relevant. **If you cannot use the verb to get your policy at any point, the feature is considered to NOT be supported.**

### Relaying Specific Policy To Players, Example

Here is a simple example taken from the slime pyroclastic event to relay specific policy.
```DM
var/policy = get_policy(ROLE_PYROCLASTIC_SLIME)
if (policy)
	to_chat(S, policy)
```
It's recommended to use a define for your policy keyword to make it easily changeable by a developer


code:
# INSTALLATION
First-time installation should be fairly straightforward. First, you'll need
BYOND installed. You can get it from https://www.byond.com/download. Once you've done
that, extract the game files to wherever you want to keep them. This is a
sourcecode-only release, so the next step is to compile the server files.

Double-click `BUILD.bat` in the root directory of the source code. This'll take
a little while, and if everything's done right you'll get a message like this:

```
saving tgstation.dmb (DEBUG mode)
tgstation.dmb - 0 errors, 0 warnings
```

If you see any errors or warnings, something has gone wrong - possibly a corrupt
download or the files extracted wrong. If problems persist, ask for assistance
in irc://irc.rizon.net/coderbus

Once that's done, open up the config folder. You'll want to edit config.txt to
set the probabilities for different gamemodes in Secret and to set your server
location so that all your players don't get disconnected at the end of each
round. It's recommended you don't turn on the gamemodes with probability 0,
except Extended, as they have various issues and aren't currently being tested,
so they may have unknown and bizarre bugs. Extended is essentially no mode, and
isn't in the Secret rotation by default as it's just not very fun.

You'll also want to edit config/admins.txt to remove the default admins and add
your own. "Game Master" is the highest level of access, and probably the one
you'll want to use for now. You can set up your own ranks and find out more in
config/admin_ranks.txt

The format is

```
byondkey = Rank
```

where the admin rank must be properly capitalised.

This codebase also depends on a native library called rust-g. A precompiled
Windows DLL is included in this repository, but Linux users will need to build
and install it themselves. Directions can be found at the [rust-g
repo](https://github.com/tgstation/rust-g).

Finally, to start the server, run Dream Daemon and enter the path to your
compiled tgstation.dmb file. Make sure to set the port to the one you
specified in the config.txt, and set the Security box to 'Safe'. Then press GO
and the server should start up and be ready to join. It is also recommended that
you set up the SQL backend (see below).

## UPDATING

To update an existing installation, first back up your /config and /data folders
as these store your server configuration, player preferences and banlist.

Then, extract the new files (preferably into a clean directory, but updating in
place should work fine), copy your /config and /data folders back into the new
install, overwriting when prompted except if we've specified otherwise, and
recompile the game.  Once you start the server up again, you should be running
the new version.

## HOSTING

If you'd like a more robust server hosting option for tgstation and its
derivatives. Check out our server tools suite at
https://github.com/tgstation/tgstation-server

If you decide to go this route, here are /tg/ specific details on hosting with TGS.

- We have two directories which should be setup in the instance's `Configuration/GameStaticFiles` directory:
	- `config` should be where you place your production configuration. Overwrites the default contents of the repo's [config](../config) directory.
	- `data` should be initially created as an empty directory. The game stores persistent data here.
- You should incorporate our [custom build scripts for TGS4](../tools/tgs4_scripts) in the instance's `Configuration/EventScripts` directory. These handle including TGUI in the build and setting up rust-g on Linux.
- Deployment security level must be set to `Trusted` or it will likely fail due to our native library usage.
- We highly recommend using the BYOND version specified in [dependencies.sh](../dependencies.sh) to avoid potential unrecorded issues.

## SQL SETUP

The SQL backend requires a Mariadb server running 10.2 or later. Mysql is not supported but Mariadb is a drop in replacement for mysql. SQL is required for the library, stats tracking, admin notes, and job-only bans, among other features, mostly related to server administration. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql and /SQL/tgstation_schema_prefix.sql depending on if you want table prefixes.  More detailed setup instructions are located here: https://www.tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database

If you are hosting a testing server on windows you can use a standalone version of MariaDB pre load with a blank (but initialized) tgdb database. Find them here: https://tgstation13.download/database/ Just unzip and run for a working (but insecure) database server. Includes a zipped copy of the data folder for easy resetting back to square one.

## WEB/CDN RESOURCE DELIVERY

Web delivery of game resources makes it quicker for players to join and reduces some of the stress on the game server.

1. Edit compile_options.dm to set the `PRELOAD_RSC` define to `0`
1. Add a url to config/external_rsc_urls pointing to a .zip file containing the .rsc.
    * If you keep up to date with /tg/ you could reuse /tg/'s rsc cdn at http://tgstation13.download/byond/tgstation.zip. Otherwise you can use cdn services like CDN77 or cloudflare (requires adding a page rule to enable caching of the zip), or roll your own cdn using route 53 and vps providers.
	* Regardless even offloading the rsc to a website without a CDN will be a massive improvement over the in game system for transferring files.

### All In One Amazon Web Services Hosting and Content delivery network.
**Important Note**
It is very Importat to note that since AWS is all highly integrated its "easier" than some solutions. However the Price to ***Performance Ratio is terrible***.

/tg/ Using around 7TB of bandwidth a month. These costs add up. So AWS is probly only a solution for low to mid pop servers

**Please use [AWS Cost Estimator](https://calculator.s3.amazonaws.com/index.html) to determine if this solution is right for you.**

This also may not be the best AWS set up but it works.

This guide is also assuming you are setting up a production server and not a server that you bring up and down on weekends.
It is highly recommended to reference AWS support documentation while reading this guide. This guide is not a How to AWS.

**Required AWS Services**
1. Elastic Computer 2 (EC2)
	* What size and class is up to you but 4GB of RAM is a minimum.
1. Route53
	* Domain registration and assigning "Elastic" IP addresses to said web addresses
1. S3
	* This will be your storage point and distrobution point for your .RSC file
1. Identity and Access Management (IAM)
	* Required for EC2 to S3 file transfers

**Required Software**
1. Microsoft Windows
1. MariaDB
1. TGS4
1. Notepad++ or other code editor for writing batch scripts
1. AWS Command Line V2

**Instructions**
1. After you go through setting up an AWS account you will need to create an IAM role and an IAM user. the IAM user will be made for debug testing. The IAM role will be used as an internal credential for the EC2 instance to talk with S3
	* The role and user creation are almost identical. give them names, select programatic access, then you will click atatych existing policey, Here you can use admin access or S3 full access. both can be found via search. the difference for roles is that you will on the first step declare it for use with EC2 and this one will need full admin access
1. Create your Amazon EC2 instance. There will be a config option asking for an IAM role. use the IAM role created in the previous step.
	* A blank Windows Server is recommended
	* You will also need to define a security policey. 3 are recommended. a Remote Desktop Protocol Policey, a Maria DB Policey, and an SS13 policey. the Latter will use the port(s) of your dream demon settings. Make sure the SS13 policey whitelists all IP addresses.
1. Creat your S3 bucket. this is a very simple process. only thing you need to edit is making the bucket public and making sure its in the same region as your EC2 instance.
1. In the EC2 control panel, go to Elastic IP's. get one and assign it to your EC2 instance. This will result in the server IP address not changing and is required for joining the game via url instead of ip address
1. In Route 53 you will register a domain name. The you will create a hosted zone and tell your domain to use the IP address you used for your EC2 instance.
1. Install the required software
	* AWSCL2 you will need to run the configuration using the IAM User you created above.
	* TGS4: Make sure the TGS4 scripts from /tools/ are installed per tgs 4 instructions after you have set up your repository and done your first fetch. You will need to Also install a batch file similar to what i have provided into the event scripts folder. You can manually run the batch file to test connection to your S3 bucket.
	* Copy `compile_options.dm` into code overrides preserving the directory structure and altering the code as mentioned in the above CDN instructions.
	* Filename: DeploymentComplete.bat
```Batch
@echo off
cd "C:\Program Files\Amazon\AWSCLIV2"
aws s3 cp "C:\Instance_Path\Game\Live\tgstation.rsc" s3://BucketName/tgstation.rsc --acl public-read
```

7. In your TGS4's instance's static config files edit resources.txt to point to the resource file uploaded by the batch file. it should resemble `http://BucketName.s3.AWSRegion.amazonaws.com/tgstation.rsc` You can get this url from the S3 object management page after its been uploaded for the first time. *Make sure you do not use use HTTPS. Byond can not do encryption*
7. Tell TGS4 to fetch and deploy. If everything goes according to plan, your server will be compiled and the resource uploaded automatically to amazon S3. You can verify that by checking on the file your bucket via aws web management.
7. Test your client side connection.
	* Tell TGS4 to run the compiled server
	* Attempt to log in. AWS has a stupid fast transfer speed. you should download client side data faster than you can recognize it happened.

## IRC BOT SETUP

Included in the repository is a python3 compatible IRC bot capable of relaying adminhelps to a specified
IRC channel/server, see the /tools/minibot folder for more


code:
# Code Standards

These are our code standards. They include information about how to properly work with our code, rules about how to structure what you're writing, and some more general information about BYOND and Dream Maker.

As with the style guide, you are expected to follow these specifications in order to make everyone's lives easier. It'll save both your time and ours, by making sure you don't have to make any changes and we don't have to ask you to. Thank you for reading this file!

1. [General](#general)
2. [Structural](#structural)
3. [Optimization](#optimization)
4. [BYOND Quirks](#dream-maker-quirks/tricks)
5. [SQL](#sql)

## General

### Object Oriented Code

As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding content to it. If you don't know what "object-oriented" means, we highly recommend you do some light research to grasp the basics.

### Avoid hacky code
Hacky code, such as adding specific checks, is highly discouraged and only allowed when there is ***no*** other option. (Protip: "I couldn't immediately think of a proper way so thus there must be no other option" is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers exist for exactly that reason.)

You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and then overriding them as required.

### Develop Secure Code

* Player input must always be escaped safely, we recommend you use stripped_input in all cases where you would use input. Essentially, just always treat input from players as inherently malicious and design with that use case in mind

* Calls to the database must be escaped properly - use sanitizeSQL to escape text based database entries from players or admins, and isnum() for number based database entries from players or admins.

* All calls to topics must be checked for correctness. Topic href calls can be easily faked by clients, so you should ensure that the call is valid for the state the item is in. Do not rely on the UI code to provide only valid topic calls, because it won't.

* Information that players could use to metagame (that is, to identify round information and/or antagonist type via information that would not be available to them in character) should be kept as administrator only.

* It is recommended as well you do not expose information about the players - even something as simple as the number of people who have readied up at the start of the round can and has been used to try to identify the round type.

* Where you have code that can cause large-scale modification and *FUN*, make sure you start it out locked behind one of the default admin roles - use common sense to determine which role fits the level of damage a function could do.

### User Interfaces

* All new player-facing user interfaces must use TGUI.
* Raw HTML is permitted for admin and debug UIs.
* Documentation for TGUI can be found at:
	* [tgui/README.md](../tgui/README.md)
	* [tgui/tutorial-and-examples.md](../tgui/docs/tutorial-and-examples.md)

### Dont override type safety checks

The use of the : operator to override type safety checks is not allowed. You must cast the variable to the proper type.

### Do not use text/string based type paths

It is rarely allowed to put type paths in a text format, as there are no compile errors if the type path no longer exists. Here is an example:

```DM
//Good
var/path_type = /obj/item/baseball_bat

//Bad
var/path_type = "/obj/item/baseball_bat"
```

### Other Notes

* Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones (i.e. a new tool would go in the "tools.dm" file)

* Bloated code may be necessary to add a certain feature, which means there has to be a judgement over whether the feature is worth having or not. You can help make this decision easier by making sure your code is modular.

* You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs.

* Do not divide when you can easily convert it to multiplication. (ie `4/2` should be done as `4*0.5`)

* Separating single lines into more readable blocks is not banned, however you should use it only where it makes new information more accessible, or aids maintainability. We do not have a column limit, and mass conversions will not be received well.

* If you used regex to replace code during development of your code, post the regex in your PR for the benefit of future developers and downstream users.

* Changes to the `/config` tree must be made in a way that allows for updating server deployments while preserving previous behaviour. This is due to the fact that the config tree is to be considered owned by the user and not necessarily updated alongside the remainder of the code. The code to preserve previous behaviour may be removed at some point in the future given the OK by maintainers.

* The dlls section of tgs3.json is not designed for dlls that are purely `call()()`ed since those handles are closed between world reboots. Only put in dlls that may have to exist between world reboots.

## Structural
### No duplicated code (Don't repeat yourself)
Copying code from one place to another may be suitable for small, short-time projects, but /tg/station is a long-term project and highly discourages this.

Instead you can use object orientation, or simply placing repeated code in a function, to obey this specification easily.

### Prefer `Initialize()` over `New()` for atoms

Our game controller is pretty good at handling long operations and lag, but it can't control what happens when the map is loaded, which calls `New` for all atoms on the map. If you're creating a new atom, use the `Initialize` proc to do what you would normally do in `New`. This cuts down on the number of proc calls needed when the world is loaded. See here for details on `Initialize`: https://github.com/tgstation/tgstation/blob/34775d42a2db4e0f6734560baadcfcf5f5540910/code/game/atoms.dm#L166
While we normally encourage (and in some cases, even require) bringing out of date code up to date when you make unrelated changes near the out of date code, that is not the case for `New` -> `Initialize` conversions. These systems are generally more dependent on parent and children procs so unrelated random conversions of existing things can cause bugs that take months to figure out.

### Files

* Because runtime errors do not give the full path, try to avoid having files with the same name across folders.

* File names should not be mixed case, or contain spaces or any character that would require escaping in a uri.

* Files and path accessed and referenced by code above simply being #included should be strictly lowercase to avoid issues on filesystems where case matters.

### Signal Handlers

All procs that are registered to listen for signals using `RegisterSignal()` must contain at the start of the proc `SIGNAL_HANDLER` eg;
```
/type/path/proc/signal_callback()
	SIGNAL_HANDLER
	// rest of the code
```
This is to ensure that it is clear the proc handles signals and turns on a lint to ensure it does not sleep.

Any sleeping behaviour that you need to perform inside a `SIGNAL_HANDLER` proc must be called asynchronously (e.g. with `INVOKE_ASYNC()`) or be redone to work asynchronously.

### Enforcing parent calling

When adding new signals to root level procs, eg;
```
/atom/proc/setDir(newdir)
	SHOULD_CALL_PARENT(TRUE)
	SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir)
	dir = newdir
```
The `SHOULD_CALL_PARENT(TRUE)` lint should be added to ensure that overrides/child procs call the parent chain and ensure the signal is sent.

### Avoid unnecessary type checks and obscuring nulls in lists

Typecasting in `for` loops carries an implied `istype()` check that filters non-matching types, nulls included. The `as anything` key can be used to skip the check.

If we know the list is supposed to only contain the desired type then we want to skip the check not only for the small optimization it offers, but also to catch any null entries that may creep into the list.

Nulls in lists tend to point to improperly-handled references, making hard deletes hard to debug. Generating a runtime in those cases is more often than not positive.

This is bad:
```DM
var/list/bag_of_atoms = list(new /obj, new /mob, new /atom, new /atom/movable, new /atom/movable)
var/highest_alpha = 0
for(var/atom/thing in bag_of_atoms)
	if(thing.alpha <= highest_alpha)
		continue
	highest_alpha = thing.alpha
```

This is good:
```DM
var/list/bag_of_atoms = list(new /obj, new /mob, new /atom, new /atom/movable, new /atom/movable)
var/highest_alpha = 0
for(var/atom/thing as anything in bag_of_atoms)
	if(thing.alpha <= highest_alpha)
		continue
	highest_alpha = thing.alpha
```

### All `process` procs need to make use of delta-time and be frame independent

In a lot of our older code, `process()` is frame dependent. Here's some example mob code:

```DM
/mob/testmob
	var/health = 100
	var/health_loss = 4 //We want to lose 2 health per second, so 4 per SSmobs process

/mob/testmob/process(delta_time) //SSmobs runs once every 2 seconds
	health -= health_loss
```

As the mobs subsystem runs once every 2 seconds, the mob now loses 4 health every process, or 2 health per second. This is called frame dependent programming.

Why is this an issue? If someone decides to make it so the mobs subsystem processes once every second (2 times as fast), your effects in process() will also be two times as fast. Resulting in 4 health loss per second rather than 2.

How do we solve this? By using delta-time. Delta-time is the amount of seconds you would theoretically have between 2 process() calls. In the case of the mobs subsystem, this would be 2 (As there is 2 seconds between every call in `process()`). Here is a new example using delta-time:

```DM
/mob/testmob
	var/health = 100
	var/health_loss = 2 //Health loss every second

/mob/testmob/process(delta_time) //SSmobs runs once every 2 seconds
	health -= health_loss * delta_time
```

In the above example, we made our health_loss variable a per second value rather than per process. In the actual process() proc we then make use of deltatime. Because SSmobs runs once every  2 seconds. Delta_time would have a value of 2. This means that by doing health_loss * delta_time, you end up with the correct amount of health_loss per process, but if for some reason the SSmobs subsystem gets changed to be faster or slower in a PR, your health_loss variable will work the same.

For example, if SSmobs is set to run once every 4 seconds, it would call process once every 4 seconds and multiply your health_loss var by 4 before subtracting it. Ensuring that your code is frame independent.

## Optimization
### Startup/Runtime tradeoffs with lists and the "hidden" init proc

First, read the comments in [this BYOND thread](http://www.byond.com/forum/?post=2086980&page=2#comment19776775), starting where the link takes you.

There are two key points here:

1) Defining a list in the variable's definition calls a hidden proc - init. If you have to define a list at startup, do so in New() (or preferably Initialize()) and avoid the overhead of a second call (Init() and then New())

2) It also consumes more memory to the point where the list is actually required, even if the object in question may never use it!

Remember: although this tradeoff makes sense in many cases, it doesn't cover them all. Think carefully about your addition before deciding if you need to use it.

### Icons are for image manipulation and defining an obj's `.icon` var, appearances are for everything else.

BYOND will allow you to use a raw icon file or even an icon datum for underlays, overlays, and what not (you can even use strings to refer to an icon state on the current icon). The issue is these get converted by BYOND to appearances on every overlay insert or removal involving them, and this process requires inserting the new appearance into the global list of appearances, and informing clients about them.

Converting them yourself to appearances and storing this converted value will ensure this process only has to happen once for the lifetime of the round. Helper functions exist to do most of the work for you.

Bad:
```dm
/obj/machine/update_overlays(blah)
	if (stat & broken)
		add_overlay(icon(broken_icon))  //this icon gets created, passed to byond, converted to an appearance, then deleted.
		return
	if (is_on)
		add_overlay("on") //also bad, the converstion to an appearance still has to happen
	else
		add_overlay(iconstate2appearance(icon, "off")) //this might seem alright, but not storing the value just moves the repeated appearance generation to this proc rather then the core overlay management. It would only be acceptable (and to some degree perferred) if this overlay is only ever added once (like in init code)
```

Good:
```dm
/obj/machine/update_overlays(var/blah)
	var/static/on_overlay
	var/static/off_overlay
	var/static/broken_overlay
	if(isnull(on_overlay)) //static vars initialize with global variables, meaning src is null and this won't pass integration tests unless you check.
		on_overlay = iconstate2appearance(icon, "on")
		off_overlay = iconstate2appearance(icon, "off")
		broken_overlay = icon2appearance(broken_icon)
	if (stat & broken)
		add_overlay(broken_overlay)
		return
	if (is_on)
		add_overlay(on_overlay)
	else
		add_overlay(off_overlay)
	...
```

Note: images are appearances with extra steps, and don't incur the overhead in conversion.

### Do not abuse associated lists.

Associated lists that could instead be variables or statically defined number indexed lists will use more memory, as associated lists have a 24 bytes per item overhead (vs 8 for lists and most vars), and are slower to search compared to static/global variables and lists with known indexes.

Bad:
```dm
/obj/machine/update_overlays(var/blah)
	var/static/our_overlays
	if(isnull(our_overlays)
		our_overlays = list("on" = iconstate2appearance(overlay_icon, "on"), "off" = iconstate2appearance(overlay_icon, "off"), "broken" = iconstate2appearance(overlay_icon, "broken"))
	if (stat & broken)
		add_overlay(our_overlays["broken"])
		return
	...
```

Good:
```dm
#define OUR_ON_OVERLAY 1
#define OUR_OFF_OVERLAY 2
#define OUR_BROKEN_OVERLAY 3
/obj/machine/update_overlays(var/blah
	var/static/our_overlays
	if(isnull(our_overlays)
		our_overlays = list(iconstate2appearance(overlay_icon, "on"), iconstate2appearance(overlay_icon, "off"), iconstate2appearance(overlay_icon, "broken"))
	if (stat & broken)
		add_overlay(our_overlays[OUR_BROKEN_OVERLAY])
		return
	...

#undef OUR_ON_OVERLAY
#undef OUR_OFF_OVERLAY
#undef OUR_BROKEN_OVERLAY
```
Storing these in a flat (non-associated) list saves on memory, and using defines to reference locations in the list saves CPU time searching the list.

Also good:
```dm
/obj/machine/update_overlays(var/blah)
	var/static/on_overlay
	var/static/off_overlay
	var/static/broken_overlay
	if(isnull(on_overlay))
		on_overlay = iconstate2appearance(overlay_icon, "on")
		off_overlay = iconstate2appearance(overlay_icon, "off")
		broken_overlay = iconstate2appearance(overlay_icon, "broken")
	if (stat & broken)
		add_overlay(broken_overlay)
		return
	...
```
Proc variables, static variables, and global variables are resolved at compile time, so the above is equivalent to the second example, but is easier to read, and avoids the need to store a list.

Note: While there has historically been a strong impulse to use associated lists for caching of computed values, this is the easy way out and leaves a lot of hidden overhead. Please keep this in mind when designing core/root systems that are intended for use by other code/coders. It's normally better for consumers of such systems to handle their own caching using vars and number indexed lists, than for you to do it using associated lists.

## Dream Maker Quirks/Tricks

Like all languages, Dream Maker has its quirks, some of them are beneficial to us, some are harmful.

### Loops
#### In-To for-loops

`for(var/i = 1, i <= some_value, i++)` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but DM's `for(var/i in 1 to some_value)` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. (Note, the `to` keyword is inclusive, so it automatically defaults to replacing `<=`; if you want `<` then you should write it as `1 to some_value-1`).

HOWEVER, if either `some_value` or `i` changes within the body of the for (underneath the `for(...)` header) or if you are looping over a list AND changing the length of the list then you can NOT use this type of for-loop!

#### `for(var/A in list)` versus `for(var/i in 1 to list.len)`

The former is faster than the latter, as shown by the following profile results:
https://file.house/zy7H.png
Code used for the test in a readable format:
https://pastebin.com/w50uERkG

### Dot variable

Like other languages in the C family, DM has a `.` or "Dot" operator, used for accessing variables/members/functions of an object instance.
eg:
```DM
var/mob/living/carbon/human/H = YOU_THE_READER
H.gib()
```
However, DM also has a dot variable, accessed just as `.` on its own, defaulting to a value of null. Now, what's special about the dot operator is that it is automatically returned (as in the `return` statement) at the end of a proc, provided the proc does not already manually return (`return count` for example.) Why is this special?

With `.` being everpresent in every proc, can we use it as a temporary variable? Of course we can! However, the `.` operator cannot replace a typecasted variable - it can hold data any other var in DM can, it just can't be accessed as one, although the `.` operator is compatible with a few operators that look weird but work perfectly fine, such as: `.++` for incrementing `.'s` value, or `.[1]` for accessing the first element of `.`, provided that it's a list.

### BYOND hellspawn

What follows is documentation of inconsistent or strange behavior found in our engine, BYOND.
It's listed here in the hope that it will prevent fruitless debugging in future.

#### Icon hell

The ‘transparent’ icon state causes fucked visual behavior when used on turfs, something to do with underlays and overlays.

## SQL

* Do not use the shorthand sql insert format (where no column names are specified) because it unnecessarily breaks all queries on minor column changes and prevents using these tables for tracking outside related info such as in a connected site/forum.

* All changes to the database's layout(schema) must be specified in the database changelog in SQL, as well as reflected in the schema files

* Any time the schema is changed the `schema_revision` table and `DB_MAJOR_VERSION` or `DB_MINOR_VERSION` defines must be incremented.

* Queries must never specify the database, be it in code, or in text files in the repo.

* Primary keys are inherently immutable and you must never do anything to change the primary key of a row or entity. This includes preserving auto increment numbers of rows when copying data to a table in a conversion script. No amount of bitching about gaps in ids or out of order ids will save you from this policy.

* The ttl for data from the database is 10 seconds. You must have a compelling reason to store and reuse data for longer then this.

* Do not write stored and transformed data to the database, instead, apply the transformation to the data in the database directly.
	* ie: SELECTing a number from the database, doubling it, then updating the database with the doubled number. If the data in the database changed between step 1 and 3, you'll get an incorrect result. Instead, directly double it in the update query. `UPDATE table SET num = num*2` instead of `UPDATE table SET num = [num]`.
	* if the transformation is user provided (such as allowing a user to edit a string), you should confirm the value being updated did not change in the database in the intervening time before writing the new user provided data by checking the old value with the current value in the database, and if it has changed, allow the user to decide what to do next.


code:
# Style Guide
This is the style you must follow when writing code. It's important to note that large parts of the codebase do not consistently follow these rules, but this does not free you of the requirement to follow them.

1. [General Guidelines](#general-guidelines)
2. [Paths and Inheritence](#paths-and-inheritence)
3. [Variables](#variables)
4. [Procs](#procs)
5. [Things that do not matter](#things-that-do-not-matter)

## General Guidelines

### Tabs, not spaces
You must use tabs to indent your code, NOT SPACES.

Do not use tabs/spaces for indentation in the middle of a code line. Not only is this inconsistent because the size of a tab is undefined, but it means that, should the line you're aligning to change size at all, we have to adjust a ton of other code. Plus, it often time hurts readability.

```dm
// Bad
#define SPECIES_MOTH			"moth"
#define SPECIES_LIZARDMAN		"lizardman"
#define SPECIES_FELINID			"felinid"

// Good
#define SPECIES_MOTH "moth"
#define SPECIES_LIZARDMAN "lizardman"
#define SPECIES_FELINID "felinid"
```

### Control statements
(if, while, for, etc)

* No control statement may contain code on the same line as the statement (`if (blah) return`)
* All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse (eg: `if (count <= 10)` not `if (10 >= count)`)

### Operators
#### Spacing
* Operators that should be separated by spaces
	* Boolean and logic operators like &&, || <, >, ==, etc (but not !)
	* Bitwise AND &
	* Argument separator operators like , (and ; when used in a forloop)
	* Assignment operators like = or += or the like
* Operators that should not be separated by spaces
	* Bitwise OR |
	* Access operators like . and :
	* Parentheses ()
	* logical not !

Math operators like +, -, /, *, etc are up in the air, just choose which version looks more readable.

#### Use
* Bitwise AND - '&'
	* Should be written as `variable & CONSTANT` NEVER `CONSTANT & variable`. Both are valid, but the latter is confusing and nonstandard.
* Associated lists declarations must have their key value quoted if it's a string
	* WRONG: `list(a = "b")`
	* RIGHT: `list("a" = "b")`

### Use static instead of global
DM has a var keyword, called global. This var keyword is for vars inside of types. For instance:

```DM
/mob
	var/global/thing = TRUE
```
This does NOT mean that you can access it everywhere like a global var. Instead, it means that that var will only exist once for all instances of its type, in this case that var will only exist once for all mobs - it's shared across everything in its type. (Much more like the keyword `static` in other languages like PHP/C++/C#/Java)

Isn't that confusing?

There is also an undocumented keyword called `static` that has the same behaviour as global but more correctly describes BYOND's behaviour. Therefore, we always use static instead of global where we need it, as it reduces suprise when reading BYOND code.

### Use early returns
Do not enclose a proc in an if-block when returning on a condition is more feasible
This is bad:
````DM
/datum/datum1/proc/proc1()
	if (thing1)
		if (!thing2)
			if (thing3 == 30)
				do stuff
````
This is good:
````DM
/datum/datum1/proc/proc1()
	if (!thing1)
		return
	if (thing2)
		return
	if (thing3 != 30)
		return
	do stuff
````
This prevents nesting levels from getting deeper then they need to be.

### No magic numbers or strings
This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that more clearly states what it's for. For instance:
````DM
/datum/proc/do_the_thing(thing_to_do)
	switch(thing_to_do)
		if(1)
			(...)
		if(2)
			(...)
````
There's no indication of what "1" and "2" mean! Instead, you'd do something like this:
````DM
#define DO_THE_THING_REALLY_HARD 1
#define DO_THE_THING_EFFICIENTLY 2
/datum/proc/do_the_thing(thing_to_do)
	switch(thing_to_do)
		if(DO_THE_THING_REALLY_HARD)
			(...)
		if(DO_THE_THING_EFFICIENTLY)
			(...)
````
This is clearer and enhances readability of your code! Get used to doing it!

### Use our time defines

The codebase contains some defines which will automatically multiply a number by the correct amount to get a number in deciseconds. Using these is preffered over using a literal amount in deciseconds.

The defines are as follows:
* SECONDS
* MINUTES
* HOURS

This is bad:
````DM
/datum/datum1/proc/proc1()
	if(do_after(mob, 15))
		mob.dothing()
````

This is good:
````DM
/datum/datum1/proc/proc1()
	if(do_after(mob, 1.5 SECONDS))
		mob.dothing()
````

## Paths and Inheritence
### All BYOND paths must contain the full path
(i.e. absolute pathing)

DM will allow you nest almost any type keyword into a block, such as:

```DM
// Not our style!
datum
	datum1
		var
			varname1 = 1
			varname2
			static
				varname3
				varname4
		proc
			proc1()
				code
			proc2()
				code

		datum2
			varname1 = 0
			proc
				proc3()
					code
			proc2()
				. = ..()
				code
```

The use of this is not allowed in this project as it makes finding definitions via full text searching next to impossible. The only exception is the variables of an object may be nested to the object, but must not nest further.

The previous code made compliant:

```DM
// Matches /tg/station style.
/datum/datum1
	var/varname1
	var/varname2
	var/static/varname3
	var/static/varname4

/datum/datum1/proc/proc1()
	code
/datum/datum1/proc/proc2()
	code
/datum/datum1/datum2
	varname1 = 0
/datum/datum1/datum2/proc/proc3()
	code
/datum/datum1/datum2/proc2()
	. = ..()
	code
```

### Type paths must begin with a `/`
eg: `/datum/thing`, not `datum/thing`

### Type paths must be snake case
eg: `/datum/blue_bird`, not `/datum/BLUEBIRD` or `/datum/BlueBird` or `/datum/Bluebird` or `/datum/blueBird`

### Datum type paths must began with "datum"
In DM, this is optional, but omitting it makes finding definitions harder.

## Variables

### Use `var/name` format when declaring variables
While DM allows other ways of declaring variables, this one should be used for consistency.

### Use descriptive and obvious names
Optimize for readability, not writability. While it is certainly easier to write `M` than `victim`, it will cause issues down the line for other developers to figure out what exactly your code is doing, even if you think the variable's purpose is obvious.

### Don't use abbreviations
Avoid variables like C, M, and H. Prefer names like "user", "victim", "weapon", etc.

```dm
// What is M? The user? The target?
// What is A? The target? The item?
/proc/use_item(mob/M, atom/A)

// Much better!
/proc/use_item(mob/user, atom/target)
```

Unless it is otherwise obvious, try to avoid just extending variables like "C" to "carbon"--this is slightly more helpful, but does not describe the *context* of the use of the variable.

### Naming things when typecasting
When typecasting, keep your names descriptive:
```dm
var/mob/living/living_target = target
var/mob/living/carbon/carbon_target = living_target
```

Of course, if you have a variable name that better describes the situation when typecasting, feel free to use it.

Note that it's okay, semantically, to use the same variable name as the type, e.g.:
```dm
var/atom/atom
var/client/client
var/mob/mob
```

Your editor may highlight the variable names, but BYOND, and we, accept these as variable names:

```dm
// This functions properly!
var/client/client = CLIENT_FROM_VAR(usr)
// vvv this may be highlighted, but it's fine!
client << browse(...)
```

### Name things as directly as possible
`was_called` is better than `has_been_called`. `notify` is better than `do_notification`.

### Avoid negative variable names
`is_flying` is better than `is_not_flying`. `late` is better than `not_on_time`.
This prevents double-negatives (such as `if (!is_not_flying)` which can make complex checks more difficult to parse.

### Exceptions to variable names

Exceptions can be made in the case of inheriting existing procs, as it makes it so you can use named parameters, but *new* variable names must follow these standards. It is also welcome, and encouraged, to refactor existing procs to use clearer variable names.

Naming numeral iterator variables `i` is also allowed, but do remember to [Avoid unnecessary type checks and obscuring nulls in lists](./STANDARDS.md#avoid-unnecessary-type-checks-and-obscuring-nulls-in-lists), and making more descriptive variables is always encouraged.

```dm
// Bad
for (var/datum/reagent/R as anything in reagents)

// Good
for (var/datum/reagent/deadly_reagent as anything in reagents)

// Allowed, but still has the potential to not be clear. What does `i` refer to?
for (var/i in 1 to 12)

// Better
for (var/month in 1 to 12)

// Bad, only use `i` for numeral loops
for (var/i in reagents)
```

## Procs

### Getters and setters

* Avoid getter procs. They are useful tools in languages with that properly enforce variable privacy and encapsulation, but DM is not one of them. The upfront cost in proc overhead is met with no benefits, and it may tempt to develop worse code.

This is bad:
```DM
/datum/datum1/proc/simple_getter()
	return gotten_variable
```
Prefer to either access the variable directly or use a macro/define.

* Make usage of variables or traits, set up through condition setters, for a more maintainable alternative to compex and redefined getters.

These are bad:
```DM
/datum/datum1/proc/complex_getter()
	return condition ? VALUE_A : VALUE_B

/datum/datum1/child_datum/complex_getter()
	return condition ? VALUE_C : VALUE_D
```

This is good:
```DM
/datum/datum1
	var/getter_turned_into_variable

/datum/datum1/proc/set_condition(new_value)
	if(condition == new_value)
		return
	condition = new_value
	on_condition_change()

/datum/datum1/proc/on_condition_change()
	getter_turned_into_variable = condition ? VALUE_A : VALUE_B

/datum/datum1/child_datum/on_condition_change()
	getter_turned_into_variable = condition ? VALUE_C : VALUE_D
```

### When passing vars through New() or Initialize()'s arguments, use src.var
Using src.var + naming the arguments the same as the var is the most readable and intuitive way to pass arguments into a new instance's vars. The main benefit is that you do not need to give arguments odd names with prefixes and suffixes that are easily forgotten in `new()` when sending named args.

This is very bad:
```DM
/atom/thing
	var/is_red

/atom/thing/Initialize(mapload, enable_red)
	is_red = enable_red

/proc/make_red_thing()
	new /atom/thing(null, enable_red = TRUE)
```

Future coders using this code will have to remember two differently named variables which are near-synonyms of eachother. One of them is only used in Initialize for one line.

This is bad:
```DM
/atom/thing
	var/is_red

/atom/thing/Initialize(mapload, _is_red)
	is_red = _is_red

/proc/make_red_thing()
	new /atom/thing(null, _is_red = TRUE)
```

`_is_red` is being used to set `is_red` and yet means a random '_' needs to be appended to the front of the arg, same as all other args like this.

This is good:
```DM
/atom/thing
	var/is_red

/atom/thing/Initialize(mapload, is_red)
	src.is_red = is_red

/proc/make_red_thing()
	new /atom/thing(null, is_red = TRUE)
```

Setting `is_red` in args is simple, and directly names the variable the argument sets.

## Things that do not matter
The following coding styles are not only not enforced at all, but are generally frowned upon to change for little to no reason:

* English/British spelling on var/proc names
	* Color/Colour - both are fine, but keep in mind that BYOND uses `color` as a base variable
* Spaces after control statements
	* `if()` and `if ()` - nobody cares!


code:
name: CI Suite
on:
  push:
    branches:
    - master
  pull_request:
    branches:
    - master
jobs:
  run_linters:
    if: "!contains(github.event.head_commit.message, '[ci skip]')"
    name: Run Linters
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - name: Restore SpacemanDMM cache
        uses: actions/cache@v2
        with:
          path: ~/SpacemanDMM
          key: ${{ runner.os }}-spacemandmm-${{ secrets.CACHE_PURGE_KEY }}
      - name: Restore Yarn cache
        uses: actions/cache@v2
        with:
          path: tgui/.yarn/cache
          key: ${{ runner.os }}-yarn-${{ secrets.CACHE_PURGE_KEY }}-${{ hashFiles('tgui/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ secrets.CACHE_PURGE_KEY }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-
      - name: Install Tools
        run: |
          pip3 install setuptools
          bash tools/ci/install_node.sh
          bash tools/ci/install_spaceman_dmm.sh dreamchecker
          tools/bootstrap/python -c ''
      - name: Run Linters
        run: |
          bash tools/ci/check_filedirs.sh tgstation.dme
          bash tools/ci/check_changelogs.sh
          bash tools/ci/check_grep.sh
          bash tools/ci/check_misc.sh
          tools/build/build --ci lint tgui-test
          tools/bootstrap/python -m dmi.test
          tools/bootstrap/python -m mapmerge2.dmm_test
          ~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1
      - name: Annotate Lints
        uses: yogstation13/DreamAnnotate@v1
        if: always()
        with:
          outputFile: output-annotations.txt

  compile_all_maps:
    if: "!contains(github.event.head_commit.message, '[ci skip]')"
    name: Compile Maps
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - name: Restore BYOND cache
        uses: actions/cache@v2
        with:
          path: ~/BYOND
          key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }}
      - name: Compile All Maps
        run: |
          bash tools/ci/install_byond.sh
          source $HOME/BYOND/byond/bin/byondsetup
          tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS

  run_all_tests:
    if: "!contains(github.event.head_commit.message, '[ci skip]')"
    name: Integration Tests
    runs-on: ubuntu-20.04
    services:
      mysql:
        image: mysql:latest
        env:
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    steps:
      - uses: actions/checkout@v2
      - name: Restore BYOND cache
        uses: actions/cache@v2
        with:
          path: ~/BYOND
          key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }}
      - name: Setup database
        run: |
          sudo systemctl start mysql
          mysql -u root -proot -e 'CREATE DATABASE tg_ci;'
          mysql -u root -proot tg_ci < SQL/tgstation_schema.sql
          mysql -u root -proot -e 'CREATE DATABASE tg_ci_prefixed;'
          mysql -u root -proot tg_ci_prefixed < SQL/tgstation_schema_prefixed.sql
      - name: Install rust-g
        run: |
          sudo dpkg --add-architecture i386
          sudo apt update || true
          sudo apt install -o APT::Immediate-Configure=false libssl1.1:i386
          bash tools/ci/install_rust_g.sh
      - name: Compile and run tests
        run: |
          bash tools/ci/install_byond.sh
          source $HOME/BYOND/byond/bin/byondsetup
          tools/build/build --ci dm -DCIBUILDING
          bash tools/ci/run_server.sh

  test_windows:
    if: "!contains(github.event.head_commit.message, '[ci skip]')"
    name: Windows Build
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v2
      - name: Restore Yarn cache
        uses: actions/cache@v2
        with:
          path: tgui/.yarn/cache
          key: ${{ runner.os }}-yarn-${{ secrets.CACHE_PURGE_KEY }}-${{ hashFiles('tgui/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ secrets.CACHE_PURGE_KEY }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-
      - name: Compile
        run: pwsh tools/ci/build.ps1
        env:
          DM_EXE: "C:\\byond\\bin\\dm.exe"
      - name: Create artifact
        run: |
          md deploy
          bash tools/deploy.sh ./deploy
      - name: Deploy artifact
        uses: actions/upload-artifact@v2
        with:
          name: deploy
          path: deploy


code:
name: Codeowner Reviews

# Controls when the workflow will run
on: pull_request_target

jobs:
  assign-users:

    runs-on: ubuntu-latest

    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so the job can access it
      - uses: actions/checkout@v2

      #Parse the Codeowner file
      - name: CodeOwnersParser
        id: CodeOwnersParser
        uses: tgstation/CodeOwnersParser@v1

      #Request reviews
      - name: Request reviews
        if: steps.CodeOwnersParser.outputs.owners != ''
        uses: tgstation/RequestReviewFromUser@v1
        with:
          separator: ' '
          users: ${{ steps.CodeOwnersParser.outputs.owners }}


code:
name: Compile changelogs

on:
  schedule:
  - cron: "0 0 * * *"

jobs:
  compile:
    name: "Compile changelogs"
    runs-on: ubuntu-20.04
    steps:
      - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
        id: value_holder
        env:
          ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
        run: |
          unset SECRET_EXISTS
          if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
          echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS"
      - name: "Setup python"
        if: steps.value_holder.outputs.ACTIONS_ENABLED
        uses: actions/setup-python@v1
        with:
          python-version: '3.x'
      - name: "Install deps"
        if: steps.value_holder.outputs.ACTIONS_ENABLED
        run: |
          python -m pip install --upgrade pip
          python -m pip install pyyaml
          sudo apt-get install  dos2unix
      - name: "Checkout"
        if: steps.value_holder.outputs.ACTIONS_ENABLED
        uses: actions/checkout@v1
        with:
          fetch-depth: 25
      - name: "Compile"
        if: steps.value_holder.outputs.ACTIONS_ENABLED
        run: |
          python tools/ss13_genchangelog.py html/changelogs
      - name: Commit
        if: steps.value_holder.outputs.ACTIONS_ENABLED
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "Changelogs"
          git pull origin master
          git add html/changelogs
          git commit -m "Automatic changelog compile [ci skip]" -a || true
      - name: "Push"
        if: steps.value_holder.outputs.ACTIONS_ENABLED
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}


code:
name: 'Check for merge conflicts'
on:
  push:
    branches:
      - master
jobs:
  triage:
    runs-on: ubuntu-20.04
    steps:
      - uses: mschilde/auto-label-merge-conflicts@2e8fcc76c6430272ec8bb64fb74ec1592156aa6a
        with:
          CONFLICT_LABEL_NAME: 'Merge Conflict'
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          WAIT_MS: 10000


code:
name: Docker Build

on:
  push:
    branches:
      - master

jobs:
  publish:
    if: "!contains(github.event.head_commit.message, '[ci skip]')"
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2

      - name: Build and Publish Docker Image to Registry
        uses: elgohr/Publish-Docker-Github-Action@master
        with:
          name: tgstation/tgstation
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
          dockerfile: Dockerfile
          tags: "latest"


code:
name: GBP
on:
  pull_request_target:
    types: [closed, opened]
jobs:
  gbp:
    runs-on: ubuntu-latest
    steps:
    - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
      id: value_holder
      env:
        ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
      run: |
        unset SECRET_EXISTS
        if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
        echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS"
    - name: Checkout
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      uses: actions/checkout@v2
    - name: Setup git
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      run: |
        git config --global user.name "gbp-action"
        git config --global user.email "<>"
    - name: Checkout alternate branch
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      uses: actions/checkout@v2
      with:
        ref: "gbp-balances" # The branch name
        path: gbp-balances
    # This is to ensure we keep the gbp.toml from master
    # without having to update our separate branch.
    - name: Copy configuration
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
    - name: GBP action
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      uses: tgstation/gbp-action@master
      with:
        branch: "gbp-balances"
        directory: ./gbp-balances
        token: ${{ secrets.GITHUB_TOKEN }}


code:
name: GBP Collection
# Every hour at the :20 minute mark. GitHub tells us to pick odd hours, instead of just using the start.
on:
  schedule:
  - cron: "20 * * * *"
  workflow_dispatch:
jobs:
  gbp_collection:
    runs-on: ubuntu-latest
    steps:
    - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
      id: value_holder
      env:
        ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
      run: |
        unset SECRET_EXISTS
        if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
        echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS"
    - name: Checkout
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      uses: actions/checkout@v2
    - name: Setup git
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      run: |
        git config --global user.name "github-actions[bot]"
        git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
    - name: Checkout alternate branch
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      uses: actions/checkout@v2
      with:
        ref: "gbp-balances" # The branch name
        path: gbp-balances
    # This is to ensure we keep the gbp.toml from master
    # without having to update our separate branch.
    - name: Copy configuration
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
    - name: GBP action
      if: steps.value_holder.outputs.ACTIONS_ENABLED
      uses: tgstation/gbp-action@master
      with:
        collect: "true"
        directory: ./gbp-balances
        token: ${{ secrets.GITHUB_TOKEN }}


code:
name: Generate documentation
on:
  push:
    branches:
    - master
jobs:
  generate_documentation:
    if: "!contains(github.event.head_commit.message, '[ci skip]')"
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - name: Setup cache
        uses: actions/cache@v2
        with:
          path: ~/SpacemanDMM
          key: ${{ runner.os }}-spacemandmm-${{ secrets.CACHE_PURGE_KEY }}
      - name: Install SpacemanDMM
        run: bash tools/ci/install_spaceman_dmm.sh dmdoc
      - name: Generate documentation
        run: |
          ~/dmdoc
          touch dmdoc/.nojekyll
          echo codedocs.tgstation13.org > dmdoc/CNAME
      - name: Deploy
        uses: JamesIves/github-pages-deploy-action@3.7.1
        with:
          BRANCH: gh-pages
          CLEAN: true
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SINGLE_COMMIT: true
          FOLDER: dmdoc


code:
name: PR emoji stripper
on:
  pull_request_target:
    types: [opened, edited]

permissions:
  pull-requests: write

jobs:
  title_and_changelog:
    runs-on: ubuntu-20.04
    steps:
    - uses: Wayland-Smithy/emoji-stripper-action@8f4c2fe9748bb9b02f105be4e72a1a42b0f34d84
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        title: true
        body: true
        body-after: ":cl:"
        body-before: "/:cl:"


code:
name: "Round ID Linker"
on:
  issues:
    types: [opened]

jobs:
  link_rounds:
    runs-on: ubuntu-20.04
    steps:
    - uses: tgstation/round_linker@master
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}


code:
name: Mark stale issues and pull requests

on:
  schedule:
  - cron: "0 0 * * *"

jobs:
  stale:

    runs-on: ubuntu-20.04

    steps:
    - uses: actions/stale@v4
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        stale-pr-message: "This PR has been inactive for long enough to be automatically marked as stale. This means it is at risk of being auto closed in ~ 7 days, please address any outstanding review items and ensure your PR is finished, if these are all true and you are auto-staled anyway, you need to actively ask maintainers if your PR will be merged. Once you have done any of the previous actions then you should request a maintainer remove the stale label on your PR, to reset the stale timer. If you feel no maintainer will respond in that time, you may wish to close this PR youself, while you seek maintainer comment, as you will then be able to reopen the PR yourself"
        days-before-stale: 7
        days-before-close: 7
        stale-pr-label: 'Stale'
        days-before-issue-stale: -1
        stale-issue-label: 'Cleanup Flagged'
        remove-issue-stale-when-updated: false
        exempt-pr-labels: 'RED LABEL,Good First PR'


code:
name: Update TGS DMAPI

on:
  schedule:
    - cron: "0 0 * * *"
  workflow_dispatch:

jobs:
  update-dmapi:
    runs-on: ubuntu-20.04
    name: Update the TGS DMAPI
    steps:
    - name: Clone
      uses: actions/checkout@v2

    - name: Branch
      run: |
        git branch -f tgs-dmapi-update
        git checkout tgs-dmapi-update
        git reset --hard master

    - name: Apply DMAPI update
      uses: tgstation/tgs-dmapi-updater@v2
      with:
        header-path: 'code/__DEFINES/tgs.dm'
        library-path: 'code/modules/tgs'

    - name: Commit and Push
      continue-on-error: true
      run: |
        git config user.name tgstation-server
        git config user.email tgstation-server@users.noreply.github.com
        git add .
        git commit -m 'Update TGS DMAPI'
        git push -f -u origin tgs-dmapi-update

    - name: Create Pull Request
      uses: repo-sync/pull-request@v2
      if: ${{ success() }}
      with:
        source_branch: "tgs-dmapi-update"
        destination_branch: "master"
        pr_title: "Automatic TGS DMAPI Update"
        pr_body: "This pull request updates the TGS DMAPI to the latest version. Please note any breaking or unimplemented changes before merging."
        pr_label: "Tools"
        pr_allow_empty: false
        github_token: ${{ secrets.COMFY_ORANGE_PAT }}


code:
###Files and folders specified here will never be tracked.

#Ignore everything in datafolder and subdirectories
/data/**/*
/tmp/**/*

#Ignore byond config folder.
/cfg/**/*

# Ignore compiled linux libs in the root folder, e.g. librust_g.so
/*.so

#Ignore compiled files and other files generated during compilation.
*.mdme
*.mdme.*
*.dmb
*.rsc
*.m.dme
*.test.dme
*.lk
*.int
*.backup
### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Global/Linux.gitignore

*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Global/Vim.gitignore

# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Python.gitignore

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-*.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# IntelliJ IDEA / PyCharm (with plugin)
.idea

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Global/Windows.gitignore

# Windows image file caches
Thumbs.db
ehthumbs.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows shortcuts
*.lnk

### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Global/OSX.gitignore

.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

*.before

#Sublime
*.sublime-project
*.sublime-workspace

#Visual studio stuff
*.vscode/*
/tools/MapAtmosFixer/MapAtmosFixer/obj/*
/tools/MapAtmosFixer/MapAtmosFixer/bin/*
/tools/CreditsTool/bin/*
/tools/CreditsTool/obj/*

#GitHub Atom
.atom-build.json

#KDevelop and Kate
*.kdev4*
*.kate-swp

#extra map stuff
/_maps/**/backup/
/_maps/templates.dm

#dmdoc default folder
/dmdoc

#Linux docker
/tools/LinuxOneShot/SetupProgram/obj/*
/tools/LinuxOneShot/SetupProgram/bin/*
/tools/LinuxOneShot/SetupProgram/.vs
/tools/LinuxOneShot/Database
/tools/LinuxOneShot/TGS_Config
/tools/LinuxOneShot/TGS_Instances
/tools/LinuxOneShot/TGS_Logs

path: .gitignore


code:
build:
    stage: build
    rules:
      - if: $CI_MERGE_REQUEST_IID || $CI_COMMIT_REF_NAME == "master"
        changes:
          - tgui/**/*.js
          - tgui/**/*.scss
        when: always
    image: node:lts
    script:
        - tgui/bin/tgui --ci


code:
static_files:
  - name: config
    populate: true
  - name: data
linux_scripts:
  PreCompile.sh: tools/tgs4_scripts/PreCompile.sh
windows_scripts:
  PreCompile.bat: tools/tgs4_scripts/PreCompile.bat


Launch DreamSeeker

type: byond

request: launch

preLaunchTask: Build All


type: process

command: tools/build/build

problemMatcher: $dreammaker $tsc $eslint-stylish

kind: build

dependsOn: dm: reparse

label: Build All

type: dreammaker

group: build

label: dm: build - tgstation.dme

label: dm: reparse

type: shell

command: bin/tgui-build

problemMatcher: $tsc $eslint-stylish

label: tgui: build

command: bin/tgui-dev

label: tgui: dev server

command: bin/tgui-bench

label: tgui: bench

command: bin/tgui-sonar

label: tgui: sonar


code:
@echo off
call "%~dp0\tools\build\build.bat" --wait-on-error build %*


code:
# base = ubuntu + full apt update
FROM ubuntu:xenial AS base
RUN dpkg --add-architecture i386 \
    && apt-get update \
    && apt-get upgrade -y \
    && apt-get dist-upgrade -y \
    && apt-get install -y --no-install-recommends \
        ca-certificates

# byond = base + byond installed globally
FROM base AS byond
WORKDIR /byond

RUN apt-get install -y --no-install-recommends \
        curl \
        unzip \
        make \
        libstdc++6:i386

COPY dependencies.sh .

RUN . ./dependencies.sh \
    && curl "http://www.byond.com/download/build/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -o byond.zip \
    && unzip byond.zip \
    && cd byond \
    && sed -i 's|install:|&\n\tmkdir -p $(MAN_DIR)/man6|' Makefile \
    && make install \
    && chmod 644 /usr/local/byond/man/man6/* \
    && apt-get purge -y --auto-remove curl unzip make \
    && cd .. \
    && rm -rf byond byond.zip

# build = byond + tgstation compiled and deployed to /deploy
FROM byond AS build
WORKDIR /tgstation

RUN apt-get install -y --no-install-recommends \
        curl

COPY . .

RUN env TG_BOOTSTRAP_NODE_LINUX=1 tools/build/build \
    && tools/deploy.sh /deploy

# rust = base + rustc and i686 target
FROM base AS rust
RUN apt-get install -y --no-install-recommends \
        curl && \
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal \
    && ~/.cargo/bin/rustup target add i686-unknown-linux-gnu

# rust_g = base + rust_g compiled to /rust_g
FROM rust AS rust_g
WORKDIR /rust_g

RUN apt-get install -y --no-install-recommends \
        pkg-config:i386 \
        libssl-dev:i386 \
        gcc-multilib \
        git \
    && git init \
    && git remote add origin https://github.com/tgstation/rust-g

COPY dependencies.sh .

RUN . ./dependencies.sh \
    && git fetch --depth 1 origin "${RUST_G_VERSION}" \
    && git checkout FETCH_HEAD \
    && env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --release --target i686-unknown-linux-gnu

# final = byond + runtime deps + rust_g + build
FROM byond
WORKDIR /tgstation

RUN apt-get install -y --no-install-recommends \
        libssl1.0.0:i386 \
        zlib1g:i386

COPY --from=build /deploy ./
COPY --from=rust_g /rust_g/target/i686-unknown-linux-gnu/release/librust_g.so ./librust_g.so

VOLUME [ "/tgstation/config", "/tgstation/data" ]
ENTRYPOINT [ "DreamDaemon", "tgstation.dmb", "-port", "1337", "-trusted", "-close", "-verbose" ]
EXPOSE 1337

path: Dockerfile