ID:2828780
 
A number of changes have been made in BYOND 515 that may impact games. These are some of the things to be aware of and adapt in your code when using the new version. It will be updated if new items come to light.

Proc lookup for call()

Many SS13 codebases used a method of looking up proc references such as .proc/MyProc and sent that to call(). However, in old versions call() would convert proc references to a name and call the proc by name; now it can be used to call a specific type path's version of the proc. Unfortunately, that breaks the following code:
call(src, .proc/MyProc)(arg1, arg2, arg3)

The way this breaks is very subtle. If the proc has been overridden, .proc/MyProc is a reference to the original proc, not the override. And if it's been overridden twice, /mytype/MyProc refers to the first override instead of the last, for reasons that are too complicated to explain (or change).

The correct way to handle this going forward is with the :: operator.
call(src, src::MyProc())(arg1, arg2, arg3)

That will grab a reference to the correct version of MyProc(), the one that would ordinarily be called if you did src.MyProc(arg1, arg2, arg3) directly.

call() vs. call_ext()

One of the most important breaking changes in BYOND 515 is that a new call_ext() proc has been introduced. The entire purpose of this is to make external .dll/.so calls less ambiguous, which is a safety feature. Anywhere your code currently uses call() to call out to an external library, it should use call_ext() instead.

The 515 compiler is smart enough to notice when the first argument in call() is a constant string intended to be used as a library name, and in that case it will compile as call_ext() but throw a warning. Other cases, like where the library name was a var, will produce runtime errors.

No src or usr in static vars

Static var initializers inside of a proc no longer have access to proc vars such as src and usr. This means code relying on them will not compile.

This doesn't sound like a big deal, but it impacts some SS13 codebases in particular. This was a common pattern being used by some of them, whose purpose was to throw a compiler error if a var name was changed:
#define NAMEOF(datum, X) (#X || ##datum.##X)

...

thing
var/shirt
var/pants
New()
var/static/bad_vars = list(NAMEOF(src, shirt), NAMEOF(src, pants))
...

The macro was causing const-folding so that NAMEOF(src, shirt) compiled as just "shirt", but the compiler would still try to evaluate a var path for src.shirt and would balk if it couldn't find the var.

This is the recommended pattern to replace that:
#define NAMEOF(datum, X) (#X || ##datum.##X)
#if DUNG_VERSION >= 515
#define NAMEOF_STATIC(datum, X) (#X || type::##X)
#else
#define NAMEOF_STATIC(datum, X) (#X || ##datum.##X)
#endif

...

thing
var/shirt
var/pants
New()
var/static/bad_vars = list(NAMEOF_STATIC(src, shirt), NAMEOF_STATIC(src, pants))
...

Inside of a static declaration, type::varname will still do the var validation that was being done before.

/list type abuse

Some games used the /list keyword in anticipation of a possible future feature where lists might have type hints. This a list might be defiend as var/list/foo/bar, which in the future might be interpreted as a list, named bar, whose items are expected to be type /foo. However, type checking was having some problems with this syntax, and mistakenly thought var/list/foo/bar meant that bar was itself type /foo. This has been fixed in the compiler.