mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-21 03:55:05 +01:00
docs: Add tutorial on bitflags (#27192)
* docs: Add tutorial on bitflags * add bit about max flags, also fix horrible admonition font size * accidentally a word * better terminology * include contra's docs, better distinction between value and variable (hopefully), better example without a leading 0 * fuck * this should stay higher up * fix operator
This commit is contained in:
committed by
GitHub
parent
b38c4bbd4c
commit
1328a3fe3e
@@ -26,3 +26,7 @@ header {
|
||||
|
||||
--md-typeset-a-color: #ff558d;
|
||||
}
|
||||
|
||||
.md-typeset .admonition {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# Advanced Bitwise Operations
|
||||
|
||||
Expanding on information covered in [Bitflags](./bitflags.md), this document
|
||||
further explores how other bitwise operators can be used. This document uses the
|
||||
prefix `0b` to denote a binary number, to distinguish them from numbers in base
|
||||
ten. For example, the base ten number 5 is `0b101`, and the base ten number
|
||||
1,110 is `0b10001010110`.
|
||||
|
||||
## Or Operator
|
||||
|
||||
The Or operator can be used merge bitflags, even if they contain the same flag.
|
||||
|
||||
```
|
||||
var/a = 5 // 0b101
|
||||
var/b = 6 // 0b110
|
||||
var/c = a | b // 0b111 = 7
|
||||
```
|
||||
|
||||
The Or operator can be used in conjunction with each other to reduce line count.
|
||||
This example from the previous bitflags document can be simplified.
|
||||
|
||||
```dm
|
||||
var/player_abilities = WALK
|
||||
player_abilities |= SING
|
||||
player_abilities |= READ
|
||||
```
|
||||
|
||||
Instead of assigning twice, we can simplify.
|
||||
|
||||
```dm
|
||||
var/player_abilities = DANCE
|
||||
player_abilities |= (SING|READ)
|
||||
```
|
||||
|
||||
Note that these are now wrapped in parentheses, primarily to make them easier to
|
||||
read that they are a pair of OR'd bitflags.
|
||||
|
||||
## And Operator
|
||||
|
||||
The And operator can be used to check if 2 variables contain the same bitflag.
|
||||
|
||||
```
|
||||
var/a = 5 // 0b101
|
||||
var/b = 6 // 0b110
|
||||
var/c = a & b // 0b100 = 4
|
||||
```
|
||||
|
||||
The only bit that stayed true was the bit that was in both inputs. This is
|
||||
different to the Or operator, where it only had to be in one input.
|
||||
|
||||
In the previous section, we turned a flag off using this example:
|
||||
|
||||
```dm
|
||||
player_abilities &= ~SING
|
||||
```
|
||||
|
||||
This done by using the & (And) operator and a negation of the bitflag. Negating
|
||||
this bitflag replace all the 1s with 0s and all the 0s with 1s.
|
||||
|
||||
```
|
||||
#define DANCE (1 << 4) // 0b10000 = 16
|
||||
to_chat(world, DANCE) // 0b10000 = 16, note that all the leading zeros have been hidden here.
|
||||
to_chat(world, ~DANCE) // 0b111111111111111111101111 = 16777199
|
||||
```
|
||||
|
||||
When the `&= ~DANCE` operation is performed, it takes negated version, where the
|
||||
flag is only zero and applies the and operation
|
||||
|
||||
```
|
||||
var/player_abilities = (DANCE|SING|SWIM)
|
||||
player_abilities &= ~DANCE // remove the dance flag
|
||||
to_chat(world, player_abilities) // now just contains (SING | SWIM)
|
||||
```
|
||||
|
||||
## Exclusive Or Operator.
|
||||
|
||||
The exclusive or operator `^` acts as a "toggle", and doesn't require knowledge
|
||||
of what is inside the flags, as seen in the example below.
|
||||
|
||||
```
|
||||
var/player_abilities = (DANCE|SING|SWIM)
|
||||
player_abilities ^= DANCE // remove the dance flag
|
||||
to_chat(world, player_abilities) // (SING | SWIM)
|
||||
player_abilities ^= DANCE // enable the dance flag
|
||||
to_chat(world, player_abilities) // (DANCE | SING | SWIM)
|
||||
```
|
||||
|
||||
When used between two variables, this can be used to check the differences
|
||||
between them.
|
||||
|
||||
```
|
||||
var/a = 3 // 0b010
|
||||
var/b = 5 // 0b101
|
||||
var/c = a ^ b // 0b110 = 6
|
||||
```
|
||||
|
||||
## Bitwise Shift Right
|
||||
|
||||
Finally, in conjunction to `<<` bitwise left shift, there is a bitwise right
|
||||
shift, used with `>>`. These shifts can also be used with numbers other than
|
||||
one, as shown in the example below.
|
||||
|
||||
```dm
|
||||
var/a = 5 // 0b101 = 5
|
||||
var/b = (a<<3) // 0b101000 = 40
|
||||
var/c = (b>>1) // 0b10100 = 20
|
||||
```
|
||||
|
||||
However, using these shifts are not common outside of defining bitflags, bitwise
|
||||
right shift is barely used.
|
||||
|
||||
Not to be confused, these operators (`<<` and `>>`) are also used as "output"
|
||||
and "input" operators for mobs, savefiles, etc. These are only rarely used,
|
||||
users should be using helper procs like to_chat() instead.
|
||||
@@ -0,0 +1,146 @@
|
||||
# Bitflags
|
||||
|
||||
Let's say we wanted to store the abilities of a player. We could create a set of
|
||||
variables like this:
|
||||
|
||||
```dm
|
||||
/mob/player
|
||||
var/can_walk = TRUE
|
||||
var/can_swim = FALSE
|
||||
var/can_fly = FALSE
|
||||
var/can_sing = TRUE
|
||||
var/can_dance = FALSE
|
||||
var/can_read = TRUE
|
||||
```
|
||||
|
||||
This isn't very clean or extensible. If we added a new ability, we'd have to add
|
||||
a new variable. Instead, we can store all of these in a single variable using
|
||||
_bitflags_.
|
||||
|
||||
Bitflags are a way of storing many pieces of information in a single number.
|
||||
|
||||
This is a simplification, but when you assign a number to a variable, that
|
||||
number is stored in binary. If you write:
|
||||
|
||||
```dm
|
||||
var/player_abilities = 41
|
||||
```
|
||||
|
||||
Internally, BYOND stores the number 41 as the binary value `101001`. If we think
|
||||
of every `0` and `1` in binary as representing an on/off switch, then we can
|
||||
store lots of switches in a single number, by associating every binary digit
|
||||
with a setting.
|
||||
|
||||
To be able to access each "on/off" switch in that number, we create a set of
|
||||
defines. Each one represents one thing we want to toggle. We use the _bitwise
|
||||
left shift_ operator, `<<`, to make reading them easier:
|
||||
|
||||
```dm
|
||||
#define WALK (1 << 0) // 000001 = 1 in binary
|
||||
#define SWIM (1 << 1) // 000010 = 2 in binary
|
||||
#define FLY (1 << 2) // 000100 = 4 in binary
|
||||
#define SING (1 << 3) // 001000 = 8 in binary
|
||||
#define DANCE (1 << 4) // 010000 = 16 in binary
|
||||
#define READ (1 << 5) // 100000 = 32 in binary
|
||||
```
|
||||
|
||||
A bitwise left shift "shifts" the value 1 to each place in a binary
|
||||
digit. If our binary value has 6 places, i.e. `000000`, then `(1 << 0)`
|
||||
represents a `1` in the "zeroth" place: `000001`. Then `(1 << 1)` represents a
|
||||
`1` in the first place: `000010`, and so on. These are still specific numbers;
|
||||
we are just representing them in a unique way. For example, `(1 << 3)` is equal
|
||||
to 8 in base ten, and is equal to `001000` in binary.
|
||||
|
||||

|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> Because of how BYOND represents numbers, a single number can only hold 24
|
||||
> flags. In other words, once the amount of flags you wish to represent in a
|
||||
> number reaches `(1 << 23)`, you have run out of available places to store
|
||||
> flags in that variable.
|
||||
>
|
||||
> The technical explanation is: BYOND has a single numeric datatype stored as
|
||||
> 32-bit IEEE 754 floating point. Performing bitwise operations on numbers in
|
||||
> BYOND converts the number to its integer representation, using the 24 bits of
|
||||
> the significand in the floating point representation, and then back to
|
||||
> floating point afterwards.
|
||||
|
||||
## Operating on Bitflags
|
||||
|
||||
There will be several kinds of operations you'll want to perform on bitflags.
|
||||
These operations are performed using _binary arithemetic operators_.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> This guide only describes the most common bitflag operations. For a deeper
|
||||
> dive into binary arithmetic and more complex operators, see the
|
||||
> [Advanced Bitflags](./adv_bitflags.md) reference.
|
||||
|
||||
### Setting and Unsetting
|
||||
|
||||
In order to set flags, use the OR bitwise operator, `|`:
|
||||
|
||||
```dm
|
||||
var/player_abilities = WALK | SING | READ
|
||||
```
|
||||
|
||||
This "toggles" the slots for the provided flags and returns the result. In this
|
||||
case, the value of `player_abilities` is now the number 41, because that is the
|
||||
sum of the values represented by those three individual flags.
|
||||
|
||||
In other words, the value of these two variables is the same:
|
||||
|
||||
```dm
|
||||
var/alice_abilities = 41
|
||||
var/brian_abilities = WALK | SING | READ
|
||||
```
|
||||
|
||||
The OR bitwise operator can also be used in assignment. For example:
|
||||
|
||||
```dm
|
||||
var/player_abilities = WALK
|
||||
player_abilities |= SING
|
||||
player_abilities |= READ
|
||||
```
|
||||
|
||||
This results in the same value as above.
|
||||
|
||||
If you have a flag you wish to toggle "off", you will use a combination of
|
||||
bitwise AND (`&`) and negation (`~`). For example, if we wanted to remove
|
||||
`SING` from the bitflag above:
|
||||
|
||||
```dm
|
||||
player_abilities &= ~SING
|
||||
```
|
||||
|
||||
This removes `SING` from the bitflag while keeping the other values set.
|
||||
|
||||
### Checking
|
||||
|
||||
In order to see if a bitflag has a specific flag toggled, use the bitwise AND
|
||||
(`&`) operator in a conditional:
|
||||
|
||||
```dm
|
||||
if(player_abilities & READ)
|
||||
world << "Player can read!"
|
||||
|
||||
if(!(player_abilities & SING))
|
||||
world << "Player can't sing!"
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Use flags for unique settings
|
||||
|
||||
Bitflags should be used when it makes sense that multiple flags can be set
|
||||
simultaneously. For example, it would not make sense to make the following
|
||||
bitflag:
|
||||
|
||||
```dm
|
||||
#define CAN_WALK (1 << 0)
|
||||
#define CANNOT_WALK (1 << 1)
|
||||
```
|
||||
|
||||
Because then both values can be toggled on in a single variable. Only use
|
||||
bitflags when it makes sense to toggle any or all of the flags simultaneously.
|
||||
@@ -64,6 +64,10 @@ An SS13 variable that saves the data of what is underneath if that that is
|
||||
removed. For example, under station floors there would be a space turf and under
|
||||
Lavaland turfs there would be lava.
|
||||
|
||||
## Bitflag
|
||||
A single variable made of individual TRUE/FALSE values. See
|
||||
[Bitflags](./bitflags.md) for an introduction on how to use bitflags.
|
||||
|
||||
## Buff
|
||||
A buff is a change to a gameplay mechanic that makes it more powerful or more
|
||||
useful. Generally the opposite of a [nerf](#nerf).
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -91,5 +91,7 @@ nav:
|
||||
- 'References':
|
||||
- 'Glossary': './references/glossary.md'
|
||||
- 'Autodoc Guide': './references/autodoc.md'
|
||||
- 'Bitflags': './references/bitflags.md'
|
||||
- 'Advanced Bitflags': './references/adv_bitflags.md'
|
||||
- 'Using Feedback Data': './references/feedback_data.md'
|
||||
- 'Tick Order': './references/tick_order.md'
|
||||
|
||||
Reference in New Issue
Block a user