diff --git a/src/en/space-station-14/cartridge-loaders.md b/src/en/space-station-14/cartridge-loaders.md index bab1e94c7..bf73bccb4 100644 --- a/src/en/space-station-14/cartridge-loaders.md +++ b/src/en/space-station-14/cartridge-loaders.md @@ -1,5 +1,4 @@ -# Завантажувачі картриджів - +# Cartridge Loaders Cartridge loaders are things like PDAs and PCs. Cartridges will be interchangeably referred to as program or cartridge depending on if they are installed onto the cartridge loader or just inserted into it. diff --git a/src/ua/assets/images/admin/adminmenu.png b/src/ua/assets/images/admin/adminmenu.png new file mode 100644 index 000000000..80581ead9 Binary files /dev/null and b/src/ua/assets/images/admin/adminmenu.png differ diff --git a/src/ua/assets/images/admin/ahelp.png b/src/ua/assets/images/admin/ahelp.png new file mode 100644 index 000000000..761a40805 Binary files /dev/null and b/src/ua/assets/images/admin/ahelp.png differ diff --git a/src/ua/assets/images/admin/console.png b/src/ua/assets/images/admin/console.png new file mode 100644 index 000000000..787d0e428 Binary files /dev/null and b/src/ua/assets/images/admin/console.png differ diff --git a/src/ua/assets/images/admin/debug.png b/src/ua/assets/images/admin/debug.png new file mode 100644 index 000000000..9c3efa582 Binary files /dev/null and b/src/ua/assets/images/admin/debug.png differ diff --git a/src/ua/assets/images/admin/logs.png b/src/ua/assets/images/admin/logs.png new file mode 100644 index 000000000..73df83dc3 Binary files /dev/null and b/src/ua/assets/images/admin/logs.png differ diff --git a/src/ua/assets/images/admin/notes.png b/src/ua/assets/images/admin/notes.png new file mode 100644 index 000000000..9b4507b52 Binary files /dev/null and b/src/ua/assets/images/admin/notes.png differ diff --git a/src/ua/assets/images/admin/smite.png b/src/ua/assets/images/admin/smite.png new file mode 100644 index 000000000..f39ad63bd Binary files /dev/null and b/src/ua/assets/images/admin/smite.png differ diff --git a/src/ua/assets/images/admin/ss14admin.png b/src/ua/assets/images/admin/ss14admin.png new file mode 100644 index 000000000..000ea1814 Binary files /dev/null and b/src/ua/assets/images/admin/ss14admin.png differ diff --git a/src/ua/assets/images/admin/verbs.png b/src/ua/assets/images/admin/verbs.png new file mode 100644 index 000000000..94b9fa374 Binary files /dev/null and b/src/ua/assets/images/admin/verbs.png differ diff --git a/src/ua/assets/images/admin/vv.png b/src/ua/assets/images/admin/vv.png new file mode 100644 index 000000000..2f2a8b1bf Binary files /dev/null and b/src/ua/assets/images/admin/vv.png differ diff --git a/src/ua/assets/images/build-configurations.png b/src/ua/assets/images/build-configurations.png new file mode 100644 index 000000000..265769758 Binary files /dev/null and b/src/ua/assets/images/build-configurations.png differ diff --git a/src/ua/assets/images/codebase-server-example.png b/src/ua/assets/images/codebase-server-example.png new file mode 100644 index 000000000..05e762379 Binary files /dev/null and b/src/ua/assets/images/codebase-server-example.png differ diff --git a/src/ua/assets/images/debug-gcserver.png b/src/ua/assets/images/debug-gcserver.png new file mode 100644 index 000000000..4434358df Binary files /dev/null and b/src/ua/assets/images/debug-gcserver.png differ diff --git a/src/ua/assets/images/debug-rider-opts.png b/src/ua/assets/images/debug-rider-opts.png new file mode 100644 index 000000000..f5ca26d20 Binary files /dev/null and b/src/ua/assets/images/debug-rider-opts.png differ diff --git a/src/ua/assets/images/debug-run-configs.png b/src/ua/assets/images/debug-run-configs.png new file mode 100644 index 000000000..d84694af5 Binary files /dev/null and b/src/ua/assets/images/debug-run-configs.png differ diff --git a/src/ua/assets/images/grids-overlay.jpg b/src/ua/assets/images/grids-overlay.jpg new file mode 100644 index 000000000..c84ebf3f8 Binary files /dev/null and b/src/ua/assets/images/grids-overlay.jpg differ diff --git a/src/ua/assets/images/hosting-log-in-with-ss14.png b/src/ua/assets/images/hosting-log-in-with-ss14.png new file mode 100644 index 000000000..69afa6cf3 Binary files /dev/null and b/src/ua/assets/images/hosting-log-in-with-ss14.png differ diff --git a/src/ua/assets/images/lighting-and-fov/after-apply-fov.png b/src/ua/assets/images/lighting-and-fov/after-apply-fov.png new file mode 100644 index 000000000..20c77fd5d Binary files /dev/null and b/src/ua/assets/images/lighting-and-fov/after-apply-fov.png differ diff --git a/src/ua/assets/images/lighting-and-fov/after-apply-lighting.png b/src/ua/assets/images/lighting-and-fov/after-apply-lighting.png new file mode 100644 index 000000000..7a15f1908 Binary files /dev/null and b/src/ua/assets/images/lighting-and-fov/after-apply-lighting.png differ diff --git a/src/ua/assets/images/lighting-and-fov/low-wall-corners.png b/src/ua/assets/images/lighting-and-fov/low-wall-corners.png new file mode 100644 index 000000000..e88e170c6 Binary files /dev/null and b/src/ua/assets/images/lighting-and-fov/low-wall-corners.png differ diff --git a/src/ua/assets/images/lighting-and-fov/wall-bleed.png b/src/ua/assets/images/lighting-and-fov/wall-bleed.png new file mode 100644 index 000000000..0483577ba Binary files /dev/null and b/src/ua/assets/images/lighting-and-fov/wall-bleed.png differ diff --git a/src/ua/assets/images/mapping/mapping-release-dropdown.png b/src/ua/assets/images/mapping/mapping-release-dropdown.png new file mode 100644 index 000000000..5f38b9ff2 Binary files /dev/null and b/src/ua/assets/images/mapping/mapping-release-dropdown.png differ diff --git a/src/ua/assets/images/mapping/oversized-hallway.png b/src/ua/assets/images/mapping/oversized-hallway.png new file mode 100644 index 000000000..f93c2883a Binary files /dev/null and b/src/ua/assets/images/mapping/oversized-hallway.png differ diff --git a/src/ua/assets/images/mapping/parkway-example.png b/src/ua/assets/images/mapping/parkway-example.png new file mode 100644 index 000000000..a19fa3bb1 Binary files /dev/null and b/src/ua/assets/images/mapping/parkway-example.png differ diff --git a/src/ua/assets/images/mapping/silver-tiles-hell-except-good.png b/src/ua/assets/images/mapping/silver-tiles-hell-except-good.png new file mode 100644 index 000000000..17ad48332 Binary files /dev/null and b/src/ua/assets/images/mapping/silver-tiles-hell-except-good.png differ diff --git a/src/ua/assets/images/mapping/silver-tiles-hell.png b/src/ua/assets/images/mapping/silver-tiles-hell.png new file mode 100644 index 000000000..dbf79a3e2 Binary files /dev/null and b/src/ua/assets/images/mapping/silver-tiles-hell.png differ diff --git a/src/ua/assets/images/meta-artifact-download.png b/src/ua/assets/images/meta-artifact-download.png new file mode 100644 index 000000000..e23ebd634 Binary files /dev/null and b/src/ua/assets/images/meta-artifact-download.png differ diff --git a/src/ua/assets/images/meta-create-fork.png b/src/ua/assets/images/meta-create-fork.png new file mode 100644 index 000000000..a95bd65b0 Binary files /dev/null and b/src/ua/assets/images/meta-create-fork.png differ diff --git a/src/ua/assets/images/meta-edit-file.png b/src/ua/assets/images/meta-edit-file.png new file mode 100644 index 000000000..b51f2be52 Binary files /dev/null and b/src/ua/assets/images/meta-edit-file.png differ diff --git a/src/ua/assets/images/meta-edit-page-button.png b/src/ua/assets/images/meta-edit-page-button.png new file mode 100644 index 000000000..13e741cdd Binary files /dev/null and b/src/ua/assets/images/meta-edit-page-button.png differ diff --git a/src/ua/assets/images/miku.png b/src/ua/assets/images/miku.png new file mode 100644 index 000000000..4837133e9 Binary files /dev/null and b/src/ua/assets/images/miku.png differ diff --git a/src/ua/assets/images/readme-example-1.png b/src/ua/assets/images/readme-example-1.png new file mode 100644 index 000000000..829c29f0e Binary files /dev/null and b/src/ua/assets/images/readme-example-1.png differ diff --git a/src/ua/assets/images/readme-example-2.png b/src/ua/assets/images/readme-example-2.png new file mode 100644 index 000000000..fc6d82f17 Binary files /dev/null and b/src/ua/assets/images/readme-example-2.png differ diff --git a/src/ua/assets/images/region_map_ss14.png b/src/ua/assets/images/region_map_ss14.png new file mode 100644 index 000000000..c4ae1106e Binary files /dev/null and b/src/ua/assets/images/region_map_ss14.png differ diff --git a/src/ua/assets/images/router-address-bar.png b/src/ua/assets/images/router-address-bar.png new file mode 100644 index 000000000..b7dfb6050 Binary files /dev/null and b/src/ua/assets/images/router-address-bar.png differ diff --git a/src/ua/assets/images/router-forward-add.png b/src/ua/assets/images/router-forward-add.png new file mode 100644 index 000000000..89a999916 Binary files /dev/null and b/src/ua/assets/images/router-forward-add.png differ diff --git a/src/ua/assets/images/router-login.png b/src/ua/assets/images/router-login.png new file mode 100644 index 000000000..2f15fa830 Binary files /dev/null and b/src/ua/assets/images/router-login.png differ diff --git a/src/ua/assets/images/setup-rider-attach-existing-folder.png b/src/ua/assets/images/setup-rider-attach-existing-folder.png new file mode 100644 index 000000000..3491d6f68 Binary files /dev/null and b/src/ua/assets/images/setup-rider-attach-existing-folder.png differ diff --git a/src/ua/assets/images/setup-rider-configurations.png b/src/ua/assets/images/setup-rider-configurations.png new file mode 100644 index 000000000..f0f332ade Binary files /dev/null and b/src/ua/assets/images/setup-rider-configurations.png differ diff --git a/src/ua/assets/images/ss14-by-example/door-layers.png b/src/ua/assets/images/ss14-by-example/door-layers.png new file mode 100644 index 000000000..140cef64f Binary files /dev/null and b/src/ua/assets/images/ss14-by-example/door-layers.png differ diff --git a/src/ua/assets/images/ss14-by-example/door-overlays.png b/src/ua/assets/images/ss14-by-example/door-overlays.png new file mode 100644 index 000000000..9a9fbc0e5 Binary files /dev/null and b/src/ua/assets/images/ss14-by-example/door-overlays.png differ diff --git a/src/ua/assets/images/ss14-by-example/hard-to-read-style.png b/src/ua/assets/images/ss14-by-example/hard-to-read-style.png new file mode 100644 index 000000000..3a9b1a049 Binary files /dev/null and b/src/ua/assets/images/ss14-by-example/hard-to-read-style.png differ diff --git a/src/ua/assets/images/tips-markup-example.png b/src/ua/assets/images/tips-markup-example.png new file mode 100644 index 000000000..fc5610ea4 Binary files /dev/null and b/src/ua/assets/images/tips-markup-example.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-box-left-example.png b/src/ua/assets/images/user-interface/escape-menu-box-left-example.png new file mode 100644 index 000000000..613cd510e Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-box-left-example.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-button-rect-example-2.png b/src/ua/assets/images/user-interface/escape-menu-button-rect-example-2.png new file mode 100644 index 000000000..c4d17e033 Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-button-rect-example-2.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-button-rect-example.png b/src/ua/assets/images/user-interface/escape-menu-button-rect-example.png new file mode 100644 index 000000000..3ab8478c0 Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-button-rect-example.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-center-example.png b/src/ua/assets/images/user-interface/escape-menu-center-example.png new file mode 100644 index 000000000..b96bb0ed8 Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-center-example.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-expand-example-1.png b/src/ua/assets/images/user-interface/escape-menu-expand-example-1.png new file mode 100644 index 000000000..3034429b7 Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-expand-example-1.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-expand-example-2.png b/src/ua/assets/images/user-interface/escape-menu-expand-example-2.png new file mode 100644 index 000000000..acc537209 Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-expand-example-2.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-expand-example-3.png b/src/ua/assets/images/user-interface/escape-menu-expand-example-3.png new file mode 100644 index 000000000..2145d381e Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-expand-example-3.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-left-example.png b/src/ua/assets/images/user-interface/escape-menu-left-example.png new file mode 100644 index 000000000..8a51bb7c0 Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-left-example.png differ diff --git a/src/ua/assets/images/user-interface/escape-menu-ui.png b/src/ua/assets/images/user-interface/escape-menu-ui.png new file mode 100644 index 000000000..8b054c2dc Binary files /dev/null and b/src/ua/assets/images/user-interface/escape-menu-ui.png differ diff --git a/src/ua/assets/misc/dungeon_template.yml b/src/ua/assets/misc/dungeon_template.yml new file mode 100644 index 000000000..542c40f8f --- /dev/null +++ b/src/ua/assets/misc/dungeon_template.yml @@ -0,0 +1,190 @@ +meta: + format: 3 + name: DemoStation + author: Space-Wizards + postmapinit: false +tilemap: + 0: Space + 1: FloorArcadeBlue + 2: FloorArcadeBlue2 + 3: FloorArcadeRed + 4: FloorAsteroidCoarseSand0 + 5: FloorAsteroidCoarseSandDug + 6: FloorAsteroidIronsand1 + 7: FloorAsteroidIronsand2 + 8: FloorAsteroidIronsand3 + 9: FloorAsteroidIronsand4 + 10: FloorAsteroidSand + 11: FloorAsteroidTile + 12: FloorBar + 13: FloorBasalt + 14: FloorBasaslt + 15: FloorBlue + 16: FloorBlueCircuit + 17: FloorBoxing + 18: FloorCarpetClown + 19: FloorCarpetOffice + 20: FloorCave + 21: FloorCaveDrought + 22: FloorClown + 23: FloorDark + 24: FloorDarkDiagonal + 25: FloorDarkDiagonalMini + 26: FloorDarkHerringbone + 27: FloorDarkMini + 28: FloorDarkMono + 29: FloorDarkOffset + 30: FloorDarkPavement + 31: FloorDarkPavementVertical + 32: FloorDarkPlastic + 33: FloorDesert + 34: FloorDirt + 35: FloorEighties + 36: FloorElevatorShaft + 37: FloorFlesh + 38: FloorFreezer + 39: FloorGlass + 40: FloorGold + 41: FloorGrass + 42: FloorGrassDark + 43: FloorGrassJungle + 44: FloorGrassLight + 45: FloorGreenCircuit + 46: FloorGym + 47: FloorHydro + 48: FloorKitchen + 49: FloorLaundry + 50: FloorLino + 51: FloorLowDesert + 52: FloorMetalDiamond + 53: FloorMime + 54: FloorMono + 55: FloorPlanetGrass + 56: FloorPlastic + 57: FloorRGlass + 58: FloorReinforced + 59: FloorRockVault + 60: FloorShowroom + 61: FloorShuttleBlue + 62: FloorShuttleOrange + 63: FloorShuttlePurple + 64: FloorShuttleRed + 65: FloorShuttleWhite + 66: FloorSilver + 67: FloorSnow + 68: FloorSteel + 69: FloorSteelDiagonal + 70: FloorSteelDiagonalMini + 71: FloorSteelDirty + 72: FloorSteelHerringbone + 73: FloorSteelMini + 74: FloorSteelMono + 75: FloorSteelOffset + 76: FloorSteelPavement + 77: FloorSteelPavementVertical + 78: FloorTechMaint + 79: FloorTechMaint2 + 80: FloorTechMaint3 + 81: FloorWhite + 82: FloorWhiteDiagonal + 83: FloorWhiteDiagonalMini + 84: FloorWhiteHerringbone + 85: FloorWhiteMini + 86: FloorWhiteMono + 87: FloorWhiteOffset + 88: FloorWhitePavement + 89: FloorWhitePavementVertical + 90: FloorWhitePlastic + 91: FloorWood + 92: FloorWoodTile + 93: Lattice + 94: Plating +entities: +- uid: 0 + components: + - type: MetaData + - type: Transform + - index: 2 + type: Map + - type: PhysicsMap + - type: Broadphase + - type: OccluderTree + - chunks: + -1,-1: + ind: -1,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAA== + 0,0: + ind: 0,0 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAA== + 0,1: + ind: 0,1 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAA== + 0,-1: + ind: 0,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAA== + -1,0: + ind: -1,0 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAA== + -1,1: + ind: -1,1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAA== + 1,-1: + ind: 1,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAA== + 1,0: + ind: 1,0 + tiles: RAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAA== + 1,1: + ind: 1,1 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAA== + -1,2: + ind: -1,2 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAA== + -1,3: + ind: -1,3 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 0,2: + ind: 0,2 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAA== + 0,3: + ind: 0,3 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 1,2: + ind: 1,2 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 1,3: + ind: 1,3 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 2,0: + ind: 2,0 + tiles: RAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAA== + 3,0: + ind: 3,0 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 2,-1: + ind: 2,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAA== + 3,-1: + ind: 3,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 3,1: + ind: 3,1 + tiles: PgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 3,2: + ind: 3,2 + tiles: PgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 2,2: + ind: 2,2 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + 2,1: + ind: 2,1 + tiles: RAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAEQAAABEAAAARAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAA== + type: MapGrid + - nextUpdate: 425.2742676 + type: GridPathfinding + - gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + type: Gravity + - chunkCollection: {} + type: DecalGrid +... diff --git a/src/ua/assets/misc/icon-trans.svg b/src/ua/assets/misc/icon-trans.svg new file mode 100644 index 000000000..5b8055878 --- /dev/null +++ b/src/ua/assets/misc/icon-trans.svg @@ -0,0 +1,2 @@ + +image/svg+xmlLogoLogo \ No newline at end of file diff --git a/src/ua/assets/misc/role_hierarchy.xml b/src/ua/assets/misc/role_hierarchy.xml new file mode 100644 index 000000000..26db99b79 --- /dev/null +++ b/src/ua/assets/misc/role_hierarchy.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ua/community/admin.md b/src/ua/community/admin.md new file mode 100644 index 000000000..ee4e7cbdd --- /dev/null +++ b/src/ua/community/admin.md @@ -0,0 +1,3 @@ +# Admin + +This section contains information on admin tooling and policy, **as it pertains to Wizards Den servers, hosted by Space Wizards.** These policies do not apply to other servers. **Tooling is likely still relevant**, as it should be present in any forks. \ No newline at end of file diff --git a/src/ua/community/admin/admin-tooling.md b/src/ua/community/admin/admin-tooling.md new file mode 100644 index 000000000..53ad3817f --- /dev/null +++ b/src/ua/community/admin/admin-tooling.md @@ -0,0 +1,205 @@ +# Admin Tooling + +The admin tooling in SS14 is hopefully not too complicated, but some things could do with some explanation. + +## Admin Menu + +![adminmenu.png](../../assets/images/admin/adminmenu.png) + +--- + +The Admin Menu can be opened by pressing F7, or using the button bar at the top. + +The *Admin* tab contains useful actions for general admin use. + +The *Adminbus* tab gives you control of many things--including spawning entities, tiles, or decals, as well as running/stopping station events. + +The *Atmos* tab lets you modify the atmos for the current grid. You shouldn't need to use this when regularly admining. + +The *Round* tab lets you control the flow of the current round. You can start, end, or restart the round using this menu. There are equivalent commands for all of these. + +The *Server* tab lets you shutdown/restart the entire server, or toggle OOC/LOOC. This is only available to admins with the proper flags. + +The *Player* tab lets you view all of the players in a round. You can also toggle the **Player Overlay** here, which draws some basic information (ckey, character name, is antag) over all players in game. + +## Verbs + +### Admin Verbs + +![verbs.png](../../assets/images/admin/verbs.png) + +--- +There are multiple admin verbs available. These are all fairly self-explanatory. *Message* opens the ahelp window targeted at the player, *Open Admin Notes* opens the user's notes. *Teleport Here/To* do as they say, and *Freeze* stops them from moving or interacting until they are unfrozen by an admin. + +### Debug Verbs + +![debug.png](../../assets/images/admin/debug.png) + +--- + +The debug verbs are primarily for debugging, but are also very useful for normal admins. + +*Control mob* gives you control of the entity, if they are sentient. + +Not shown here, *Make sentient* makes an entity able to be controlled. + +Not shown here, *Make ghost role* turns an entity into a controllable ghost role. + +*Delete* deletes an entity. + +*Edit solutions* lets you modify the solutions and reagents on an entity, e.g. bloodstream, beaker contents, etc. + +*Rejuvenate* fully heals an entity. Only use when you have to. + +*Set outfit* lets you change the attire/items that an entity is wearing. Useful for running events or respawning people. + +**View variables** is a very, very useful tool that allows you to view all marked variables on every component that an entity has, as well as modify some. Useful for debugging, but also has great use for adminning as it allows you to add or remove arbitrary components, for example `OwOAccent`. Further explanation of how to use **View variables** (VV) is given below. + +### Smite Verbs + +![smite.png](../../assets/images/admin/smite.png) + +--- + +Use sparingly. Not all smites available are pictured here. All of these mess with someone's round severely in some way. + +## Admin Help + +![ahelp.png](../../assets/images/admin/ahelp.png) + +--- + +The **Admin Help** menu can be opened by pressing `Esc -> Admin Help`, by using the `Player Actions Panel` in the **Admin Menu**, or by using the `Message` admin verb. + +Select the player to message using the menu on the left. The 'sword' icon lets you know if they're an antagonist. It also shows their current character name as well as ckey. + +The *kick*, *ban*, *respawn*, and *teleport to* buttons at the bottom should be self explanatory. **Kick has no confirmation, so be careful.** + +## Admin Logs + +![logs.png](../../assets/images/admin/logs.png) + +--- + +The admin logs menu shows you all interactions that have occurred during the round as well as which entities and players were involved. It can be opened in the *Admin* tab of the **Admin Menu**. + +The far left menu lets you filter by log type. The second-from-the-left menu lets you filter by player/entity who was involved in the log. + +The top left *Round* spinbox lets you change which round you're looking at, though this rarely needs to be done except for retroactive ahelps. + +You can filter by *log impact* at the top, though this is rarely needed if you know what you're looking for. + +You can view the actual log data on the far right menu, and search for logs. If the log you're looking for doesn't show up, try hitting *refresh* (you'll likely need to do this when changing which player or log type you're looking for*. If it still doesn't show up, press *Next* and wait for a couple seconds. + +The *Pop out* button pops the log menu into a separate OS window. Useful for saving some space on your main monitor. + +## Admin Notes + +![notes.png](../../assets/images/admin/notes.png) + +--- + +Admin notes give admins a way to store information about a user when a ban would be too harsh of a punishment. Notes can be edited (and show their edit information), and can be deleted if the note is no longer relevant. + +Notes can be accessed using the `Admin` verb category, or at the bottom of the AHelp menu. + +## View Variables + +![vv.png](../../assets/images/admin/vv.png) + +--- + +A couple things to discuss about how SS14 works before the VV menu can really be useful to you: + +- An 'entity' is an object in game--walls, players, projectiles, items, etc. Each entity has a unique `EntityUid` corresponding to them, which is the number `15199` you see at the top left next to the entity's name. +- Each entity (generally) has a prototype ID, or which 'type' of entity it is. This is the `MobObserver` string you see at the top left. +- Entities have **components**, which give some kind of behavior to the entity. The component for a 'player controlled' entity is `ActorComponent`, for a weapon it is `MeleeWeaponComponent`, for damageable objects it is `DamageableComponent`, etc. You can dynamically add and remove components whenever you want. +- The game is separated into 'client' and 'server', with some shared stuff between them. Client is whats running on your computer, server is whats running on the game server, obviously. + +Most of the time, you'll want to view the **Server Components** list, as you can add/remove/modify components there and every client will see the effects. + +I recommend that you simply try and use the VV menu to get a feel for how it works. It's a little daunting, but it is very powerful. + +### VV Tips + +- Press `enter` after modifying a variable to save it's value. +- Some variables may look non-modifiable, but there may be a modifiable version of them farther down the list. +- `TransformComponent` holds the entity's position, rotation, etc. +- `Grids` (the station) are entities too, and their components can be changed +- You can modify the `zoom` on the client-side `EyeComponent` to see farther. +- `MindComponent` contains information like antag roles, jobs, objectives, etc. +- An entity's name/description can be changed using `Server Variables`, or on the server-side `MetaDataComponent`. + +## Game Console + +![console.png](../../assets/images/admin/console.png) + +--- + +The console can be opened using the tilde `~` key, unless rebound. Any player can open it, but I'm just mentioning it here in case you were unaware of that fact. Commands can be run by typing the command and then the arguments, like any shell. Important client logs will show up here as well, so it's useful for debugging. + +Use the `list` command to list all available commands, and the `help ` command to view instructions for the command. + +### Useful Commands + +| Command | Description | Syntax | +|----------- |----------- |----------- | +| `aghost` | Turn yourself into an admin ghost, or back again. | | +| `deadmin` | Lets you play the game without getting admin notifications. Always use before plaaying yourself.| | +| `readmin` | Returns all admin functions to the user after they `deadmin`. Not necessary to run upon joining.| | +| `help` | Shows information about the specified command. | `help ` | +| `list` | Lists all commands in the game. | | +| `list ` | Lists all commands with the specified keyword. | `list ` | +| `quit` | Closes the game. | | +| `adminwho` | List all admins online. | | +| `togglehealthoverlay` | Toggles a visual layer for the playerhealth. | `togglehealthoverlay ` | +| `scale` | Make things bigger or smaller. Relative to current size (2 twice is the same as 4 once).| `scale `| +| `adduplink` | Adds an uplink to a PDA and links it to the user. | `adduplink ` | +| `lsobjective` | Shows all objectives of the targeted player. | `lsobjective ` | +| `addobjective` | Adds an objective, requires the objective ID. | `addobjective ` | +| `fixgridatmos` | Resets all atmos on the grid to breathable and clean. | `fixgridatmos ` (use autofill) | +| `tpto` | Teleport all targets to the first `` in the command. | `tpto ` | +| `tp` | Teleports yourself to the coordinates on the specified map. | `tp ` | +| `setmind` | Puts the player's soul into the ID (may require the entity to have a mind). | `setmind ` | +| `customvote` | Creates a custom vote for all players, requires at least two choices. Always do this before running any important events. | `customvote <option1> <option2> ...` | +| `addhand` | Adds a hand to the entity, not all entities are supported, requires the "Hands" component in the entity. | `addhand <uid>` | +| `adjstationjob` | Adds or removes jobs. Can be used if a member of command leaves. | `adjstationjob <STATION_ID> <job> <amount>` | +| `addgamerule` | Adds the chosen game event to the game. | `addgamerule <gameruleName>` | +| `endgamerule` | Removes the chosen gamerule frome the game. Usefull for repeating false events. | `endgamerule <gameruleName>` | +| `rename` | Correct way to rename a person (includes ID, PDA, and, cloner entries). | `rename <ckey/uid> <"FIRSTNAME LASTNAME">` | +| `setadminooc` | Changes the color of your adminchat while being an admin. | `setadminooc <HEX Code>` | +|`addcomp` | Adds a component to an entity. | `addcomp <uid> <component name>` | +|`banlist` | List all bans for a user. | `banlist <ckey>` | +|`dock` | Tries to dock two docking airlocks together. | `dock <airlock uid 1> <airlock uid 2>` | +|`forcemap` | Forces the next map to the given map. | `forcemap <map prototype id>` | +|`forcepreset` | Forces the next game preset to the given preset. | `forcepreset <preset prototype id>` | +|`loadgamemap` | Loads a new game map into the given coordinates on the given map ID. Useful for impromptu nukies.|`loadgamemap <map prototype id> <mapid> <x> <y>`| +|`vv` | Open the view variables menu on an entity. Useful for VV-ing grids. | `vv <uid>` | + + +## SS14.Admin + +![ss14admin.png](../../assets/images/admin/ss14admin.png) + +--- + +If you're an official Space Wizards :tm: Admin, you can access the online admin panel at `central.spacestation14.io/admin`. Log in using your SS14 account. Third-party servers can also set up this panel for themselves using the repository, so thats why this information is here and not secret. + +The *Bans* tab at the top is the most useful. This shows all current bans. You can search by active bans, or total bans, for any player. **This menu shows PII (personally identifying info) such as HWID and IP, so do not ever reveal this information to others.** + +You can see the ban time, reason, and when it expires. You can also check *ban hits*, or whether someone attempted to join and was denied because of a ban, as well as what username/IP/HWID they tried to join with. Useful for detecting ban evaders. You can also unban users on this panel. + +The *Players* tab just lets you search by username, IP, HWID, etc for any user. **Contains lots of PII, be careful.** + +The *Connections* tab shows you the most recent connections to the servers as well as information for each. + +## Admin Ghost + +Admin ghosts are special observer entities that take your ckeys name, have hands, can interact with any object ingame, and have all access. You can turn yourself into an aghost using the **Admin Menu** or by using the `aghost` command. + +You can return to your initial body + +## Deadmin / Readmin + +If you want to play the game normally, its recommended that you deadmin using the `deadmin` command. If you want to perform admin duties again, use the `readmin` command. diff --git a/src/ua/community/admin/admin-tooling/admin-command-cookbook.md b/src/ua/community/admin/admin-tooling/admin-command-cookbook.md new file mode 100644 index 000000000..ccf07329b --- /dev/null +++ b/src/ua/community/admin/admin-tooling/admin-command-cookbook.md @@ -0,0 +1,68 @@ +# Admin Command Cookbook + +## Delete PA & Singularity Generator +``` + entities named "PA .*" do delete $ID + deleteewi SingularityGenerator +``` +## Uncurse all lockers +`entities with CursedEntityStorage do rmcomp $ID CursedEntityStorage; addcomp $ID EntityStorage` +## Remove all ghostroles +`entities with GhostTakeoverAvailable do rmcomp $ID GhostTakeoverAvailable; rmcomp $ID Mind` +## Clean up assorted body parts +`entities with MapGrid children with BodyPart do delete $ID` +## Disarm the station's potassium supply +`entities hasreagent Potassium do delete $ID` +## Remove all of a specific character's insulated gloves +`entities with Mind named "NAME GOES HERE" rchildren named "insulated gloves" do delete $ID` + +# Ghost Roles +## Sentient vendors +`entities with Advertise do makeghostrole $ID "$NAME" "You're a vending machine, use your speaker to annoy people."; rmcomp $ID Advertise` +## Sentient soap +`entities with Slippery prototyped SoapOmega do makeghostrole $ID "$NAME" "You're a bar of soap. Slip absolutely everyone."; addcomp $ID MobState; addcomp $ID PlayerMobMover; addcomp $ID PlayerInputMover` +## Please don't +`entities with Item do makeghostrole $ID "$NAME" "You're a random, talking item. What the fuck?"; addcomp $ID MobState; addcomp $ID PlayerMobMover; addcomp $ID PlayerInputMover` +## Your heart would like a word. +`entities with Mechanism do makeghostrole $ID "$NAME" "You are some unfortunate soul's $NAME"` +## Ghostularity +`entities with Singularity do makeghostrole $ID "Singularity" "FUCK"; addcomp $ID MobState; addcomp $ID MovementIgnoreGravity; addcomp $ID PlayerInputMover; addcomp $ID PlayerMobMover` + +# Probably make everyone unhappy +## Cursed locker abuse +### Cursed lockers +`entities with EntityStorage named ".*closet$|^.*locker" do rmcomp $ID EntityStorage; addcomp $ID CursedEntityStorage` +### Haunted lockers +`entities with EntityStorage named ".*closet$|^.*locker" do rmcomp $ID EntityStorage; addcomp $ID CursedEntityStorage; makeghostrole $ID "$NAME" "You're a haunted locker. Consume people."; addcomp $ID MobState; addcomp $ID PlayerMobMover; addcomp $ID PlayerInputMover` +## Mess with station access +### All Access Day +`entities with AccessReader do rmcomp $ID AccessReader` +### All access bridge on Saltern +`entities with Airlock named "Bridge" near 6 with Airlock do rmcomp $ID AccessReader` +## Swap around jobs/outfits +### Clown Day +`entities with Mind prototyped MobHuman do setoutfit $ID ClownGear; addcomp $ID Clumsy` +### Clownitis +`entities with Clumsy near 1 with Body prototyped MobHuman not with Clumsy do setoutfit $ID ClownGear; addcomp $ID Clumsy` +### All but two of us are clowns, who is it +`entities with Mind alive prototyped MobHuman not with Clumsy not select 2 do setoutfit $ID ClownGear; addcomp $ID Clumsy` +### Mime Day +`entities with Mind prototyped MobHuman do setoutfit $ID MimeGear; rmcomp $ID Speech` +## Containers +### Remove someone's lungs +`entities with Body prototyped MobHuman named "NAME GOES HERE" rchildren named "lungs" do delete $ID` +or to drop them on the floor: +`entities with Body prototyped MobHuman named "NAME GOES HERE" rchildren named "lungs" do rmmechanism $ID` +## Mess with station structure +### Make the station see-through +`entities named ".*wall" do spawn ReinforcedWindow $ID; spawn Grille $ID; spawn CableApcExtension $ID; delete $ID` +### Electric Avenue +`entities named ".*wall" do spawn spawn Grille $ID; spawn CableApcExtension $ID` + +# Make everyone unhappy +## Summon God +`entities with Body prototyped MobHuman alive select 1 do addcomp $ID Singularity; addcomp $ID MovementIgnoreGravity; godmode $ID` +## bang +`entities with MapGrid rchildren do explode $WX $WY 1 1 1 1` +## bang, but funnier +`entities with MapGrid children with Item not anchored do addcomp $ID RoguePointingArrow` \ No newline at end of file diff --git a/src/ua/community/admin/wizards-den-admin-policy.md b/src/ua/community/admin/wizards-den-admin-policy.md new file mode 100644 index 000000000..e702c1b0f --- /dev/null +++ b/src/ua/community/admin/wizards-den-admin-policy.md @@ -0,0 +1,59 @@ +# Wizards Den Admin Policy + +```admonish +This applies only to our first-party Wizard's Den servers. +``` + +## 1. General Adminning +General admin-guidelines. These apply to all ranks of admins: Wizard, Badmin, and Game Admins. +#### 1.1 **Administrators will be held responsible for their actions.** +Administrators are **not unaccountable**, and should be held to higher standards than the playerbase. If you have a problem with an admin's behavior, discuss with the head admin. All administrators are expected to have a solid reason behind their actions. +#### 1.2 **Be professional, polite and welcoming.** +Professionalism is important, and in general will help reduce the number of issues you run into as staff. We expect you to **deescalate**, rather than escalate situations. +As staff, you're often the first person that a player with an issue will talk to. No matter your opinion on the player, do your best to be respectful towards them. +#### 1.3 **Do not leak or share sensitive information without permission.** +This *does* pertain to any information posted in the game admin Discord chats, information discussed in admin chat in-game, as well as PII (IP, HWID) accessible through the central ban DB. This does *not* pertain to basic ban info (time, reason, count), admin notes, or admin log information. +#### 1.4 **Ban Appeals** +Ban appeals are covered by the [Banning Policy](./wizards-den-banning-policy.md). + +## 2. Game-adminning +Rules specific to adminning a round of SS14. +#### 2.1 **Do not ever process a case you are/were a part of.** +Even if you've started adminning after dying, do not process a case you were involved in. You may ask badmins/wizards for permission to do this if there are no other staff online. Otherwise, ahelp yourself and get the other staff to handle it. +#### 2.2 **Superseded by banning policy** +Bans and ban appeals are covered by the [Banning Policy](./wizards-den-banning-policy.md). +#### 2.3 **Superseded by banning policy** +Bans and ban appeals are covered by the [Banning Policy](./wizards-den-banning-policy.md). +#### 2.4 **Deadmin when you play the game.** +Don't misuse your game admin tools to metagame, this is pretty self-explanatory. +Do not use your admin powers to message players whilst IC. If something happens that breaks the rules, you should engage with admins as a normal player via the regular ahelp command. +Exceptions to this are: +- You may start adminning a round after you have died. Do note that policy 2.1 still applies! +- You may use admintools when running adminevents if necessary, even if you are playing a character for it. +#### 2.5 **Admin events should be done in moderation without heavily altering the flow of the round.** +An "*event*" here is generally meant to be any admin intervention in the round that affects more than a handful of people: spawning a cookie for a prayer *no*, spawning a sentient monkey that runs around and messes with people *yes*. + +Heavily round-altering admin events (e.g. powerful wizard invasion, nations) should be voted for by the playerbase with `customvote` and only done on Extended (that means forcing the preset before the round starts!). + +Events overall should not be something that occur every round. Log what events you've done in Discord (just when & what, and no need to log very minor stuff), and try not to overwhelm players. That said, a lot of lenience is given towards what kinds of events can be run. + +#### 2.6 **Do not interfere unless you are needed.** +If it looks like the situation will be able to resolve itself, or escalated naturally; and nothing actually actionable has happened, then there is no reason to interfere. **Reminder: The official servers are LRP, outside of Salamander.** +#### 2.7 **Check with other admins before enacting bans outside of guidelines.** +Bans and ban appeals are covered by the [Banning Policy](./wizards-den-banning-policy.md). Administrators who place bans outside of the guidelines are required to be able to justify the decision to the admin team. +#### 2.8 **Use notes as consideration for punishment, and give notes frequently** +If a player is AHelped for some behavior, but this behavior skirts the rules and is not explicitly bannable, you should always give them a note for it. When handling AHelps, you should always check their notes before interrogation or applying punishment. If a user is noted to have been skirting the rules multiple times previously in their notes, you may apply a ban for this behavior. + +## 3. Head Admin Policy + +#### 3.1 **Hold vote / discussion threads for all major decisions made** +In discord, just make a new private thread and ping game admins. If it's related to trialmin promotion/accepting, remove all current trialmins from the thread. Not all of them need to be votes, and votes can often hamper actual discussion, but they should always be done. Some discretion is given on what counts as a 'major' enough decision to warrant a thread. + +#### 3.2 **Trialmin discussions should be held at least every two weeks.** +Can happen more frequently. These don't necessarily need to be votes, but just a check-up on how all of the current trials are doing, what we can nudge them on, whether anyone is already promotable, etc. + +#### 3.3 **Vote threads relating to admins in Discord should always be archived/locked after the fact** +Any information that needs to be long-term from these should be outside of the thread. + +#### 3.4 **Attempts should be made to further discussion on ban appeals older than one week** +Don't have to forcibly come to agreement on them, but its the headmin's job to ensure that some discussion gets started on these. The actual discussion and specific decision making can be delegated to others obviously but the headmin is working in a managerial position here. diff --git a/src/ua/community/admin/wizards-den-banning-policy.md b/src/ua/community/admin/wizards-den-banning-policy.md new file mode 100644 index 000000000..b7e1db777 --- /dev/null +++ b/src/ua/community/admin/wizards-den-banning-policy.md @@ -0,0 +1,297 @@ +# Wizards Den Banning Policy + +```admonish info +This is the ban policy for Wizard's Den servers. It applies only to our first-party Wizard's Den servers. The admin policy can be found at [Admin Policy](./wizards-den-admin-policy.md) +``` + +# Definitions +- **Indefinite:** Refers to a ban with no defined end time. This type of ban generally requires a successful appeal for it to be removed. Indefinite bans which have no extra requirements may be called *appeal bans*. +- **Voucher:** Confirmation from a member of the admin team of another well known SS14 or SS13 server that you have played on that server for a significant amount of time without any recent major issues. A voucher is required when appealing a *voucher ban*. +- **Voucher ban:** A ban which requires a *voucher* to appeal. These bans can also typically only be appealed after at least 6 months from the date of the ban. They are often used as an alternative to a *permanent ban* that allows players to return if they can demonstrate an ability to follow a server's rules. +- **Warning:** A warning is a clear communication from an admin that some behavior is not acceptable. Warnings should always be paired with an account note making note of the warning and behavior. +- **Permanent ban:** A ban which cannot be appealed. These bans are sometimes called *perma bans*. +- **Game ban:** A ban from the servers. A player who is *game banned* cannot connect to the servers during the ban. +- **Role ban:** A ban from a specific role or roles. +- **Ban evasion:** Attempting to circumvent an active ban. + +## Special Banning Requirements + +### Permanent Bans + +Permanent bans should typically only be placed as the result of an unsuccessful appeal of an indefinite ban or a voucher ban. Except for cases of ban evasion after an accepted voucher ban, a vote by the admin team is required to place a permanent ban. Placing a permanent ban may be an option in a vote for the appeal. It is recommended that any indefinite ban after a prior voucher ban within 6 months of the date of the current ban be upgraded to a permanent ban. + +### Voucher Bans + +Voucher bans should typically only be placed as the result of an unsuccessful appeal of an indefinite ban. Except for cases of ban evasion, a vote by the admin team is required to place a voucher ban. Placing a voucher ban may be an option in a vote for the appeal. Unless the prior indefinite bans were solely for contacting the player, it is recommended that any indefinite ban after a prior indefinite ban within 6 months of the date of the current ban be upgraded to a voucher ban. + +# Banning Guidelines + +```admonish info +Administrators are not required to follow the times suggested by the banning guidelines when placing a ban, but bans placed within the guidelines are presumed to be of an appropriate length. +``` + +```admonish info +All instructions in the subsections of the *Banning Guidelines* section only refer to the guidelines, not policy. A statement saying "X is not permitted" means it is not permitted under the guidelines, not that it may never be done. +``` + +```admonish warning +Administrators who place bans outside of the guidelines are required to be able to justify the decision to the admin team. +``` + +```admonish info +Consulting the admin team via Discord, or multiple (2 or more) other game admins in-game through admin chat or ahelp does not result in those bans being presumed to be of an appropriate length, but is sufficient justification for straying from the guidelines. +``` + +```admonish info +Any total suggested time greater than, not equal to, 7 days can be substituted with an indefinite ban and still be considered within guidelines. +``` + +## Evading AHelp + +A player is considered to have evaded an ahelp if: +- They disconnect almost immediately after breaking a rule or after being arrested/killed for breaking a rule. +- They disconnect during an ahelp where they should have reasonably believed the administrator was investigating an issue that they contributed to or were suspected of contributing to. +- Without responding, they continue to play after receiving at least 2 reasonably spaced ahelp messages asking them a question since their last response. +- They do not respond at least 5 minutes after receiving at least 2 reasonably spaced ahelp messages asking them a question since their last response. + +If a player evades an ahelp: +- Their ban should be extended by at least 7 days. +- Their ban may be extended up to indefinite. +- If their ban is extended, it must include an instruction to appeal to explain the situation or continue the ahelp. + +## Ban reasons + +In most cases, ban reasons should clearly communicate the reason for the ban to the banned player whenever they are not evading a ban. Admins should be able to easily identify the reason for a ban through either the ban or account's notes. + +Alternate account bans should typically be in the form `Alt of OriginalUsername` or `AltUsername alt of OriginalUsername`. + +Bans of additional non-username information should typically include another ban on the GUID of the targeted player and should have a reason with just the account username. The earliest placed ban's reason will appear for the player when trying to connect. + +## Offense Table + +```admonish info +First, second, third, and fourth offense refers to the amount of offenses in the grouping category in the last 6 months. Suggestions for subsequent offenses are double the last defined suggestion. +``` + +```admonish info +Bolded suggestions in ranges are recommended for most cases. +``` + +```admonish info +Rule violations not in the offense table can still have bans applied, but have no guidelines. Administrators can look to guidelines of similar offenses to aid in determining a response. +``` + +| Abreviation | Meaning | +|-------------|------------| +| W | Warning | +| RB | Role ban or department ban | +| GB | Game ban | +| Indef | Indefinite | + +----------------------------- + +| Grouping Category | Offense | First Offense | Second Offense | Third Offense | Fourth Offense | +|-------------------|------------------------------------------------------------------------------------------------------------|----------------|--------------------------------------------------------------------------------------------------------------------------|---------------|----------------| +| Escalation | [Over escalation](https://wiki.spacestation14.io/wiki/Server_Rules#Follow_escalation_rules,_don't_make_Cargonia)[^eachVictim] | W | 12hr GB | 3d GB | **7d** - 7.5d GB | +| Escalation | [RDM](https://wiki.spacestation14.io/wiki/Server_Rules#Follow_escalation_rules,_don't_make_Cargonia)[^eachVictim] | 12hr GB | 3d GB | **7d** - 7.5d GB | | +| Escalation | [Over escalation or RDM that is a secondary result of station sabotage](https://wiki.spacestation14.io/wiki/Server_Rules#Follow_escalation_rules,_don't_make_Cargonia)[^stationSabotageRDM] | 12hr GB | 3d GB | **7d** - 7.5d GB | | +| Self-antag | [Self-antag](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_intentionally_make_everything_worse_[Self-Antagonism])[^excludingEscalationIssues] | W - 12hr GB | 12hr - 3d GB | **7d** - 7.5d GB | | +| Self-antag | [Station sabotage](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_intentionally_make_everything_worse_[Self-Antagonism])[^stationSabotage] | W - 3d GB | 12hr - 7d GB | 14d - 15d GB | | +| Self-antag | [Cults/riots/revolutions](https://wiki.spacestation14.io/wiki/Server_Rules#Follow_escalation_rules,_don't_make_Cargonia) | **12hr** - 3d GB | 12hr - **3d** - 7d GB | **7d** - 7.5d GB | | +| Self-antag | [Cooperating with known antags](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_intentionally_make_everything_worse_[Self-Antagonism]) | 12hr GB | 3d GB | **7d** - 7.5d GB | | +| Non-grouping | [Harassing staff through the game](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_abuse/ignore_the_admin-help_relay) | Indef GB | | | | +| Non-grouping | [Slurs, excluding "retard" and variants](https://wiki.spacestation14.io/wiki/Server_Rules#No_hate_speech,_slurs,_bigotry,_racism,_specism,_sexism,_etc.) | Indef GB | | | | +| Non-grouping | ["Retard" and variants](https://wiki.spacestation14.io/wiki/Server_Rules#No_hate_speech,_slurs,_bigotry,_racism,_specism,_sexism,_etc.) | W | 1d - 3d GB | Indef GB | | +| Non-grouping | [Bigotry/discrimination](https://wiki.spacestation14.io/wiki/Server_Rules#No_hate_speech,_slurs,_bigotry,_racism,_specism,_sexism,_etc.)[^discrimination] | Indef GB | | | | +| Non-grouping | [ERP/Sexual content](https://wiki.spacestation14.io/wiki/Server_Rules#No_erotic_roleplay_(ERP)_or_sexual_content/themes) | Indef GB | | | | +| Non-grouping | [Ban Evasion](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_attempt_to_evade_bans) | Voucher Ban | If after an accepted voucher ban, permanent ban.<br/>Otherwise, extend voucher ban to 6 months from evasion attempt. | | | +| Language | [Non-english chat](https://wiki.spacestation14.io/wiki/Server_Rules#English_only) | W | W - 12hr GB | 3d GB | **7d** - 7.5d GB | +| Language | [Solely non-english chat](https://wiki.spacestation14.io/wiki/Server_Rules#English_only) | W | Indef GB | | | +| Non-grouping | [Bugs/exploits](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_exploits_or_crash_the_server) | **W** - 7d GB | 12hr - 7d GB | 3d - 15d GB | 7d - 15d GB | +| Non-grouping | [Use of macros](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_exploits_or_crash_the_server) | W | W - 12hr GB | 3d GB | **7d** - 7.5d GB | +| Non-grouping | [Multi-keying](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_multiple_SS14_accounts_[Multi-keying]) | W - **Indef** GB | Indef GB | | | +| Non-grouping | [Ahelp misuse in bad faith](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_abuse/ignore_the_admin-help_relay)[^badFaith] | **W** - 12hr GB | 12hr - 3d GB | **7d** - 7.5d GB | | +| Non-grouping | [Bad character name](https://wiki.spacestation14.io/wiki/Server_Rules#Act_like_a_human_being)[^requiresIntent] | **W** - 12hr GB | **12hr** - 3d GB | **7d** - 7.5d GB | | +| Metacomms | [Metacommunications](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_external_means_to_communicate_with_other_players_[Metacomming]) | Indef GB | | | | +| Metacomms | [IC in OOC](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_outside_information_to_gain_an_advantage_[Metagaming]) | W | W - 12hr GB | 3d GB | **7d** - 7.5d GB | +| Immersion | [Text speak](https://wiki.spacestation14.io/wiki/Server_Rules#Act_like_a_human_being) | W | W | **W** - 12hr GB | W - 12hr GB | +| Immersion | [OOC terms IC](https://wiki.spacestation14.io/wiki/Server_Rules#Act_like_a_human_being)[^teachingException] | W | **W** - 12hr GB | **12hr** - 3d GB | 3d - 7.5d GB | +| Immersion | [Bypassing chat restrictions](https://wiki.spacestation14.io/wiki/Server_Rules#Act_like_a_human_being) | W | W - **4hr** - 12hr GB | 12hr - 3d GB | 3d - 7.5d GB | +| Griefing | [Damage/disruption to arrivals/arrivals shuttle](https://wiki.spacestation14.io/wiki/Server_Rules#Don't_be_a_dick) | 12hr - 3d GB | 3d - 7d GB | 7d - 15d GB | | +| Griefing | [Harassing a player/role/department outside of reasonable conflicts](https://wiki.spacestation14.io/wiki/Server_Rules#Don't_be_a_dick) | W - 12hr GB | 12hr - 3d GB | **7d** - 7.5d GB | | +| Griefing | [Round stalling](https://wiki.spacestation14.io/wiki/Server_Rules#Don't_be_a_dick) | **W** - 12hr GB | **12hr** - 3d GB | 3d - 7d GB | **7d** - 7.5d GB | +| Griefing | [Early massive station sabotage](https://wiki.spacestation14.io/wiki/Server_Rules#Antagonist-Specific_Rules:_Traitor)[^antagOnly] | W - 12hr GB | 12hr - 3d GB | **7d** - 7.5d GB | | +| Griefing | [Antagonist team sabotage](https://wiki.spacestation14.io/wiki/Server_Rules#Antagonist-Specific_Rules:_Nuclear_Operatives)[^antagOnly] | 12hr - 3d GB | 3d - Indef GB | 7d - Indef GB | | +| Griefing | [Grief as minor antag](https://wiki.spacestation14.io/wiki/Server_Rules#Antagonist-Specific_Rules:_Minor_Antagonists)[^antagOnly] | **W** - 12hr GB | 12hr - 3d GB | 3d - 7d GB | 7d - 15d GB | +| Griefing | [Abandoning a role](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_suicide_out_of_or_waste_important_roles,_including_antagonist_roles)[^abandoningRole] | W - 3d RB | 3d - 7d RB | Indef RB | | +| Griefing | [Antag rolling](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_suicide_out_of_or_waste_important_roles,_including_antagonist_roles) | 12hr - 3d GB | 3d - 7d GB | **7d** - 7.5d GB | | +| Griefing | [Friendly antag](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_suicide_out_of_or_waste_important_roles,_including_antagonist_roles)[^antagOnly] | **W** - 12hr GB | 12hr - 3d GB | **7d** - 7.5d GB | | +| Metagaming | [Using info from death](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_outside_information_to_gain_an_advantage_[Metagaming])[^infoFromDeath] | W - 12hr GB | 12hr - 3d GB | 3d - 7.5d GB | | +| Metagaming | [Using info from past life](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_outside_information_to_gain_an_advantage_[Metagaming]) | 12hr - 48hr GB | 3d GB | **7d** - 7.5d GB | | +| Metagaming | [Metagaming round type](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_outside_information_to_gain_an_advantage_[Metagaming]) | W - 12hr GB | 12hr - 3d GB | 3d - 7.5d GB | | +| Metagaming | [Preemptive PDA swapping](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_use_outside_information_to_gain_an_advantage_[Metagaming]) | W | 3d - 7d RB | 7d - 15d RB | | +| Metagaming | [Preparing items not needed IC](https://wiki.spacestation14.io/wiki/Server_Rules#Do_not_pre-emptively_rush_for_weapons_and_equipment_[Powergaming]) | W | W - 12hr GB | 12hr - 3d GB | 3d - 7.5d GB | +| Competence | [Unreasonable incompetence in role](https://wiki.spacestation14.io/wiki/Server_Rules#Department_Specific_Behavior_Issues) | W - **3d** - 7d RB | 7d - 15d RB | Indef RB | | +| Competence | [Unreasonable failure of security/command to maintain order](https://wiki.spacestation14.io/wiki/Server_Rules#Command_&_Security_are_held_to_a_higher_standard) | 3d - 7d RB | 7d - 15d RB | Indef RB | | +| Competence | [Abuse of a position of authority](https://wiki.spacestation14.io/wiki/Server_Rules#Command_&_Security_are_held_to_a_higher_standard) | 3d - 7d RB | 7d - 15d RB | Indef RB | | +| Competence | [Taking actions a reasonable person would view as to be to the detriment of the station as security/command](https://wiki.spacestation14.io/wiki/Server_Rules#Command_&_Security_are_held_to_a_higher_standard) | 3d - 7d RB | 7d - 15d RB | Indef RB | | +| Competence | [Failure of security to give medical aid to prisoners](https://wiki.spacestation14.io/wiki/Server_Rules#Command_&_Security_engagement_rules) | W - **3d** - 7d RB | 7d - 15d RB | Indef RB | | +| Competence | [Unauthorized execution](https://wiki.spacestation14.io/wiki/Server_Rules#Command_&_Security_will_be_reasonable_with_punishments)[^stackEscalation][^applyToChain] | W | 3d - 7.5d RB | Indef RB | | +| AI | [Minor failure to follow silicon laws](https://wiki.spacestation14.io/wiki/Server_Rules#Cyborg,_AI,_and_Silicon_Rules) | W | W - 3d RB | 3d - 7.5d RB | Indef RB | +| AI | [Major failure to follow silicon laws](https://wiki.spacestation14.io/wiki/Server_Rules#Cyborg,_AI,_and_Silicon_Rules) | W - 3d RB | 3d - 7.5d RB | Indef RB | | + +[^eachVictim]: Guideline is multiplied by the number of victims. + +[^stationSabotageRDM]: This should be used in cases where there are deaths that result from a station sabotage offense, like people being killed by an AME being overloaded or dying in a loosed singularity. + +[^excludingEscalationIssues]: Does not include escalation issues. + +[^stationSabotage]: Acts that result in station wide, or near station wide disruption. Spacing a hallway is not station sabotage, but cutting HV or destroying substations likely is. Arrivals or arrivals terminal sabotage is not sufficient to apply the station sabotage offense. + +[^discrimination]: Includes IC racism/speciesism. + +[^badFaith]: To qualify as bad faith, there should be no *reasonable* way that something is being done with good intentions. It is not required for the person to *actually* be acting in bad faith, only that it is *unreasonable* for them to think that they are. + +[^antagOnly]: Only applies to antagonists. + +[^stackEscalation]: Should be combined with an escalation offense from the perspective of the offender. Typically RDM if an execution occurs for no reason, or over escalation if there is a poor reason. + +[^teachingException]: Use of admin discretion to not enact penalties is highly recommended in cases where the offending player only commits an offense after attempting and failing to teach a new player appropriately. + +[^applyToChain]: The offense may be applied to anyone in the chain of those who requested or approved the execution up to the executioner. + +[^infoFromDeath]: This includes any information that a character should have not known from the same "life". Depending on server rules, this may include information like the information leading to your death. A "life" ends when a player takes a different role, like a ghost role. For the purposes of banning guidelines, a "life" does not end on cloning. + +[^abandoningRole]: This includes frequently disconnecting in an important role, disconnecting in an important role without notification when there is reason to believe that the disconnection was not urgent, and failing to attempt to perform the duties of a role. + +[^requiresIntent]: Offenses where the admin does not believe a violation to have been intentional may be reduced to a warning and do not need to be considered a past offense when evaluating guidelines for future offenses. + +## Modifiers Table + +```admonish info +> Modifiers can be applied to each offense that meets their conditions. They are typically in the form of multipliers. +``` + +```admonish info +Warnings cannot be multiplied. An offense which lists W as a suggestion cannot have that suggestion multiplied into a GB, but the guideline can still be strayed from with the same conditions as other parts of the guidelines can be strayed from. +``` + +```admonish info +An offense which lists W - 12h GB as a suggestion that is affected by a 2x multiplier would become a W - 24h GB suggestion. +``` + +-------------------- + +| Modifier | Modification | +|----------|--------------| +| Metagrudging | 2x multiplier if the offense is the result of metagrudging by the offender. | +| Repeat game bans | A multiplier equal to 1 plus the number of game bans in the last 6 months which resulted from offenses from other grouping categories. This multiplier can only apply to game ban suggestions, not role ban suggestions. | +| Prior indefinite ban | Up to 7d can be added to the total game ban length if the player has had a prior indefinite ban in the last 6 months. Excluding bans used only for contact and ones where they were found to be not at fault. | +| Round removal | 2x multiplier for any offense which results in someone being permanently removed from a round, including an attempt to do so and actions likely to result in permanent round removal. | +| Ban request/demand | Any player who demands or requests a ban can be banned indefinitely. | +| Lying in ahelp | 24h + 1-3x multiplier if the offender maliciously lies in the ahelp. You should be certain that they have lied. The multiplier may be applied after the 24h addition is made. | +| Role specific | Any issue that is likely to be prevented by a role ban should include a role ban if a game ban is applied. The role ban can be applied in addition to or as an alternative to the suggested game ban. Game ban suggestions can be converted to role ban suggestion times by doubling the time. | +| Command/Security | 1-2x multiplier if the offender is in command or security | +| Intentional rule breaking | 2-3x multiplier. Includes any rule breaking where the player intentionally breaks a rule knowing they are breaking a rule, knowing they will get banned, or claims to not care if they get banned. Any reasonably clear rule violation can be presumed to be intentional if the player was told to read the rules in the last 12 hours. | +| New player | Anything from a warning to the maximum suggested ban can be given to a player new to the game if they are told to read the rules, if they have not been previously told to do so, and if the minimum suggestion is not an indefinite ban. | +| Valid Rule Clarification | No more than a warning should be given to a player that justifies the offense with a reasonably cited active rule clarification, even if it is not up to date with current rules. | +| Caught before round effects | If there are no earlier similar issues, any issue caught before it affects the round and other players can be reduced to a warning at admin discretion. | +| Self report | Reduce to warning. Applies to any offense where the player reports themselves and where the offense was unlikely to be identified otherwise. | +| Admin intervention | Any reduction, including to nothing, may be applied for any offense which is plausibly the result of admin intervention. | + +## Grouping and Stacking + +- When separate offenses occur, the suggested time should be determined by summing the suggestions for each separate offense. +- Offenses across multiple rounds can always be treated as separate offenses, but are not required to be. +- Offenses within the same round are "grouped", and not separate offenses if there is no relevant ahelp between them[^ahelpDelimited] and if any of the following are true: + - the offenses are non-grouping and one is necessary for the other to have occurred, or + - the offenses are in the same grouping category. +- Grouped offenses should use the guideline for the most specific offense. + +[^ahelpDelimited]: This means that if two otherwise grouped offenses occur within the same round, but the player is ahelped between the two offenses occurring about the first offense, the second offense does not need to be grouped with the first. The second offense may also be considered as a prior offense for determining suggested ban times. + +## Role Ban Rounding + +The total time of a role ban may be rounded to the nearest available autofill. + +## Examples + +### AME Sabotage +A technical assistant sets the AME to 50. Their offenses are: +- Self antag +- Station sabotage +- Unreasonable incompetence in role +Self antag and station sabotage are part of the same grouping category, so are treated as only station sabotage. Both remaining offenses can have the new player modifier applied if the player is new to the game, which would allow the offenses to result in only a warning and an instruction to read the rules. The station sabotage offense may optionally have the role specific modifier applied. Any of the following total suggestions are valid: +- With new player modifier + - W - 3d GB + W - 7d RB +- With role specific modifier + - W - 3d GB + W - 13d RB if the role ban is applied in addition to the game ban, or + - W - 13d RB if the role ban is applied as an alternative + +### RDM + Lying +A player RDMs another. When asked why they killed the other player, they say they haven't done anything all round other than walk around, despite having just beaten the victim to death with a bat. Their offenses are: +- 1x RDM + +The lying in ahelp modifier applies. The resulting guideline is: +- 36hr - 4.5d GB + +### Over escalation with history of issues +A player over escalates in a way unlikely to result in the victim's round removal. They have a prior offense of RDM, a prior offense of self-antagging, and a prior offense of griefing the arrivals terminal. They have no prior bans. The offenses in this case are: +- 1x over escalation + +No modifiers apply. Only the prior RDM offense is relevant because it is the only one in the same grouping category. The prior RDM offense means that for the current over escalation incident, second offense guidelines are used. + +# Appeals + +## Appeals of Mistaken Bans + +If the appeal disputes the events which were used to justify the ban, the first appeal of a voucher or permanent ban may only be declined after it has been verified that it was appropriately placed. + +## Appeal Hijacking + +If an appeal is currently being processed by someone, it is generally best to let them finish processing the it. Cases where it may be acceptable to "hijack" an appeal are: +- the processor has not responded to the appeal recently, +- the processor has somehow indicated that they are not going to process the appeal, or +- a head game admin has told you that you can process the appeal. + +## Appeal Procedure + +1. If you are a trial admin, you cannot process the appeal. +2. Check appeal format. +3. Checking for ban evasion is highly recommended, but not required. +4. If you placed the ban, you must meet the following criteria to continue: + - [ ] the player does not dispute the facts that led to the ban, + - [ ] the player does not claim bias, + - [ ] the ban is within ban guidelines, and + - [ ] any of the following are true: + - [ ] the ban is less than or equal to 7 days long, + - [ ] the appeal is older than 7 days with no post on it by another admin in the last 7 days, or + - [ ] all of the following are true: + - [ ] this is the first valid appeal of the ban, + - [ ] the ban was placed because the player was offline or evaded an ahelp, and + - [ ] you are accepting the appeal by either removing the ban or reducing it to be less than or equal to 14 days from the start of the ban being appealed. +5. Collect information + 1. Check the player's history of appeals. + 2. Make a reasonable attempt to verify any claims made in the appeal by the player, or accept them to be true. + 3. Check the player's note and ban history. + 4. Reading the ahelp that led to the ban is highly recommended. + 5. Ask the player questions that are important for the processing of the appeal. + 6. Attempt to allow the player to respond to information which will be considered in the appeal that it would be unfair to not allow them the opportunity to address. +6. If any of the following criteria are met, you may skip to step 8 and process the appeal using the speedy appeal process: + - the ban length is less than or equal to 14 days, + - this appeal is older than 7 days and no other admin is handling the appeal, or + - the result of the appeal is obvious. + - Similar appeals of bans for similar situations are almost always accepted. + - So little effort was made in the appeal, that it is improbable it would be accepted. +7. Run a vote + - Votes must run at least 24 hours. + - Votes must not be closed if there is ongoing discussion. + - Votes must be made in one of the designated internal appeals discussion channels. + - Votes should present as much relevant information as possible. + - Votes should indicate if the ban is within guidelines, preferably by presenting the guideline range for the ban. +8. If the result is to deny the appeal, deny the appeal. + - The player should be told when they can re-appeal. This should typically be double the time they were given for the last appeal for the same ban, or two weeks if there was no prior appeal for the same ban. + - If the appeal was processed using the speedy appeal process, use the "Speedy Appeal" saved action to notify the player. +9. Check for ban evasion and deny the appeal if it is found. +10. Accept the appeal. + - If the appeal was processed using the speedy appeal process and the ban time was within guidelines, you must be able to articulate why the appeal was accepted and should do this on the appeal itself. diff --git a/src/ua/community/discord-rich-presence-repository.md b/src/ua/community/discord-rich-presence-repository.md new file mode 100644 index 000000000..ba701d8ba --- /dev/null +++ b/src/ua/community/discord-rich-presence-repository.md @@ -0,0 +1,5 @@ +# Discord Rich Presence Repository + +## Custom Assets + +If you want to display custom assets like a server icon in Rich Presence, we must upload them to the Discord app assets. Please see [this repo](https://github.com/space-wizards/asset-dump/tree/master/discord-app-assets) for details. \ No newline at end of file diff --git a/src/ua/community/infrastructure-reference/grafana-dashboards.md b/src/ua/community/infrastructure-reference/grafana-dashboards.md new file mode 100644 index 000000000..5640725ad --- /dev/null +++ b/src/ua/community/infrastructure-reference/grafana-dashboards.md @@ -0,0 +1,2891 @@ +# Grafana Dashboards + +Contains the export for our Grafana dashboards at the time of writing. You will probably have to edit these to make them work with your server. + +## Game Servers +```json +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": false, + "panels": [ + { + "aliasColors": { + "wizards_den_eu_west": "blue" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 3, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 10, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": true, + "targets": [ + { + "expr": "robust_player_count", + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Player Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": { + "wizards_den_eu_west": "blue" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 3, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 10, + "x": 10, + "y": 0 + }, + "hiddenSeries": false, + "id": 23, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": true, + "targets": [ + { + "exemplar": true, + "expr": "sum(robust_player_count)", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Total Player Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 28 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "median" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "4m", + "frequency": "1m", + "handler": 1, + "name": "TPS alert", + "noDataState": "no_data", + "notifications": [ + { + "uid": "N5nihcmMk" + } + ] + }, + "aliasColors": { + "PlayerCount wizards_den_us_west": "dark-orange", + "wizards_den_eu_west": "blue" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 0, + "y": 7 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(robust_server_curtick[30s])", + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 28, + "visible": true + } + ], + "timeRegions": [], + "title": "TPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "hertz", + "logBase": 1, + "max": "35", + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 10, + "y": 7 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(process_cpu_seconds_total[10s])", + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "percentunit", + "logBase": 1, + "max": "1", + "min": "0", + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 520850446 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "message": "Memory Usage Alert", + "name": "Managed Memory alert", + "noDataState": "no_data", + "notifications": [ + { + "uid": "N5nihcmMk" + } + ] + }, + "aliasColors": { + "wizards_den_eu_west": "blue" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 0, + "y": 13 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dotnet_total_memory_bytes", + "format": "time_series", + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 520850446, + "visible": true + } + ], + "timeRegions": [], + "title": "Managed Memory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:560", + "format": "bytes", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:561", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "description": "", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dthms" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 10, + "y": 13 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "exemplar": true, + "expr": "ss14_round_length{job=\"wizards_den_lizard\"}", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "US West Round Duration", + "type": "stat" + }, + { + "description": "US west playercount", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Players", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 15, + "y": 13 + }, + "id": 15, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "robust_player_count", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "title": "USW", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "wizards_den_lizard" + ] + } + } + } + ], + "type": "stat" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 18, + "y": 13 + }, + "id": 25, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "exemplar": true, + "expr": "sum(robust_player_count)", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Total Players", + "type": "stat" + }, + { + "description": "", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dthms" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 10, + "y": 16 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "ss14_round_length{job=\"wizards_den_eu_west\"}", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "EU West Round Duration", + "type": "stat" + }, + { + "description": "EU west playercount", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Players", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 15, + "y": 16 + }, + "id": 16, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "robust_player_count", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "title": "EUW", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "wizards_den_eu_west" + ] + } + } + } + ], + "type": "stat" + }, + { + "aliasColors": { + "wizards_den_eu_west": "blue" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 0, + "y": 19 + }, + "hiddenSeries": false, + "id": 22, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "robust_server_uptime", + "format": "time_series", + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Server Uptime", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:560", + "format": "s", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:561", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "description": "", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dthms" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 10, + "y": 19 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "ss14_round_length{job=\"wizards_den_eu_west_2\"}", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "EU West 2 Round Duration", + "type": "stat" + }, + { + "description": "EU west 2 playercount", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Players", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 15, + "y": 19 + }, + "id": 17, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "robust_player_count", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "title": "EUW2", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "wizards_den_eu_west_2" + ] + } + } + } + ], + "type": "stat" + }, + { + "description": "", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dthms" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 10, + "y": 22 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "ss14_round_length{job=\"wizards_den_centipede\"}", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Oceania Round Duration", + "type": "stat" + }, + { + "description": "Oceania playercount", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Players", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 15, + "y": 22 + }, + "id": 18, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "robust_player_count", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "title": "OCE", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "wizards_den_centipede" + ] + } + } + } + ], + "type": "stat" + }, + { + "description": "", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dthms" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 10, + "y": 25 + }, + "id": 14, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "ss14_round_length{job=\"raspberry\"}", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Raspberry Round Duration", + "type": "stat" + }, + { + "description": "Gradient/Raspberry playercount", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Players", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 15, + "y": 25 + }, + "id": 19, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "robust_player_count", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "title": "RSP", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "raspberry", + "Time" + ] + } + } + } + ], + "type": "stat" + } + ], + "refresh": false, + "schemaVersion": 33, + "style": "dark", + "tags": [ + "game servers" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Game Servers", + "uid": "K0LTdKmMk", + "version": 52, + "weekStart": "" +} +``` + +## Perf Metrics + +```json +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "iteration": 1640259621461, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "DSc4tCGMk" + }, + "description": "", + "gridPos": { + "h": 7, + "w": 19, + "x": 0, + "y": 0 + }, + "id": 34, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "expr": "{App=\"Robust.Server\",Server=\"$Server\"}", + "refId": "A" + } + ], + "title": "Logs", + "transformations": [], + "type": "logs" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 7 + }, + "id": 24, + "panels": [], + "title": "CPU", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 13, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(process_cpu_seconds_total{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:957", + "decimals": 0, + "format": "percentunit", + "logBase": 1, + "max": "1.05", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:958", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dthms" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 6, + "y": 8 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "robust_server_uptime{job=\"$Server\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Uptime", + "type": "stat" + }, + { + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dthms" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 10, + "y": 8 + }, + "id": 16, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "robust_server_curtime{job=\"$Server\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "CurTime", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 5, + "x": 14, + "y": 8 + }, + "hiddenSeries": false, + "id": 36, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "robust_player_count{job=\"$Server\"}", + "interval": "", + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Player Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:145", + "decimals": 0, + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:146", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 12 + }, + "id": 45, + "options": { + "displayMode": "gradient", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "text": {} + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(robust_entity_physics_controller_before_solve_bucket{job=\"$Server\"}[5m])) by (le, controller))", + "interval": "", + "legendFormat": "{{controller}}", + "refId": "A" + } + ], + "title": "Controller Tick Breakdown", + "type": "bargauge" + }, + { + "cards": {}, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateGreens", + "exponent": 0.5, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "description": "How long gameloop ticks take.", + "gridPos": { + "h": 6, + "w": 7, + "x": 5, + "y": 12 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 2, + "legend": { + "show": false + }, + "reverseYBuckets": false, + "targets": [ + { + "expr": "rate(robust_game_loop_frametime_bucket{job=\"$Server\"}[10s])", + "format": "heatmap", + "interval": "", + "legendFormat": "{{le}}", + "refId": "A" + } + ], + "title": "Tick Duration", + "tooltip": { + "show": true, + "showHistogram": false + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "yAxis": { + "decimals": 0, + "format": "s", + "logBase": 1, + "show": true + }, + "yBucketBound": "auto" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 7, + "x": 12, + "y": 12 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(robust_server_curtick{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Tickrate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:350", + "format": "hertz", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:351", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 0.001 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 7, + "x": 5, + "y": 18 + }, + "id": 41, + "options": { + "displayMode": "gradient", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": true + }, + "showUnfilled": true, + "text": {} + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "topk(10, histogram_quantile(0.95, sum(rate(robust_entity_systems_update_usage_bucket{job=\"$Server\"}[5m])) by (le, system)))", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{system}}", + "refId": "A" + } + ], + "title": "95% System Tick Breakdown", + "transformations": [], + "type": "bargauge" + }, + { + "aliasColors": {}, + "breakPoint": "50%", + "combine": { + "label": "Others", + "threshold": 0 + }, + "decimals": 0, + "fontSize": "80%", + "format": "s", + "gridPos": { + "h": 8, + "w": 7, + "x": 12, + "y": 18 + }, + "id": 39, + "legend": { + "percentage": true, + "show": true, + "sort": "current", + "sortDesc": true, + "values": true + }, + "legendType": "Right side", + "links": [], + "nullPointMode": "connected", + "pieType": "pie", + "pluginVersion": "7.1.5", + "strokeWidth": "0.25", + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(robust_server_update_usage_bucket[1m])) by (le, area))", + "interval": "", + "legendFormat": "{{area}}", + "refId": "A" + } + ], + "title": "95% Main Tick Timing", + "type": "grafana-piechart-panel", + "valueName": "current" + }, + { + "aliasColors": {}, + "breakPoint": "50%", + "combine": { + "label": "Others", + "threshold": 0 + }, + "description": "", + "fontSize": "50%", + "format": "s", + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 20 + }, + "id": 43, + "legend": { + "header": "", + "show": false, + "sideWidth": 10, + "values": true + }, + "legendType": "On graph", + "links": [], + "nullPointMode": "connected", + "pieType": "pie", + "pluginVersion": "7.3.2", + "strokeWidth": "1", + "targets": [ + { + "expr": "topk(10, histogram_quantile(0.95, sum(rate(robust_entity_systems_update_usage_bucket{job=\"$Server\"}[5m])) by (le, system)))", + "interval": "", + "legendFormat": "{{system}}", + "refId": "A" + } + ], + "title": "95% System Tick Chart", + "type": "grafana-piechart-panel", + "valueName": "current" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 22, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": { + "Send": "blue" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "description": "", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 7, + "x": 0, + "y": 27 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(robust_net_recv_bytes{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Receive", + "refId": "A" + }, + { + "expr": "rate(robust_net_sent_bytes{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Send", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Network Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:268", + "decimals": 0, + "format": "Bps", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:269", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": { + "Receive Messages": "dark-red", + "Receive Packets": "red", + "Send": "blue", + "Send Messages": "semi-dark-blue", + "Send Packets": "blue" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "description": "* Packets: real UDP packets.\n* Messages: Lidgren messages.\n\nLidgren can combine multiple messages into one UDP packet so these numbers are not necessarily equal. ", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 7, + "x": 7, + "y": 27 + }, + "hiddenSeries": false, + "id": 7, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(robust_net_recv_packets{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Receive Packets", + "refId": "A" + }, + { + "expr": "rate(robust_net_sent_packets{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Send Packets", + "refId": "B" + }, + { + "expr": "rate(robust_net_sent_messages{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Send Messages", + "refId": "C" + }, + { + "expr": "rate(robust_net_recv_messages{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Receive Messages", + "refId": "D" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Packets", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:268", + "decimals": 0, + "format": "pps", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:269", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "description": "How many messages are currently stored by Lidgren.\n\n* Stored: Reliable messages that are stored in case they need to be re-sent.\n* Unsent: Messages that have not yet been sent because of network overload.", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 33 + }, + "hiddenSeries": false, + "id": 28, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "robust_net_stored{job=\"$Server\"}", + "interval": "", + "legendFormat": "Stored", + "refId": "A" + }, + { + "expr": "robust_net_unsent{job=\"$Server\"}", + "interval": "", + "legendFormat": "Unsent", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Stored Messages", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:134", + "decimals": 0, + "format": "none", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:135", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "description": "How many reliable messages are being re-sent by Lidgren.", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 33 + }, + "hiddenSeries": false, + "id": 30, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(robust_net_resent_delay{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Delay", + "refId": "A" + }, + { + "expr": "rate(robust_net_resent_hole{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "Holes", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Resent Messages", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:614", + "decimals": 0, + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:615", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "description": "How many messages are getting dropped by Lidgren. This usually indicates duplicates or sequencing issues.", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 33 + }, + "hiddenSeries": false, + "id": 32, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(robust_net_dropped{job=\"$Server\"}[10s])", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Dropped Messages", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:701", + "decimals": 0, + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:702", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 20, + "panels": [], + "title": "Memory", + "type": "row" + }, + { + "description": "How often the GC is running, by generation.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1.5 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 14, + "x": 0, + "y": 39 + }, + "id": 18, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.3.2", + "targets": [ + { + "expr": "rate(dotnet_collection_count_total{job=\"$Server\"}[1m])", + "interval": "", + "legendFormat": "{{generation}}", + "refId": "A" + } + ], + "title": "GC Per Minute", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "description": "Managed and working set memory", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 44 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "dotnet_total_memory_bytes{job=\"$Server\"}", + "interval": "", + "legendFormat": "Managed memory", + "refId": "A" + }, + { + "exemplar": true, + "expr": "process_working_set_bytes{job=\"$Server\"}-dotnet_total_memory_bytes{job=\"$Server\"}", + "hide": false, + "interval": "", + "legendFormat": "Working Set (Extra)", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Memory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:449", + "format": "bytes", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:450", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 44 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "robust_entities_count{job=\"$Server\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Game Entity Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:678", + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:679", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + } + ], + "refresh": false, + "schemaVersion": 33, + "style": "dark", + "tags": [ + "game servers" + ], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "wizards_den_eu_west", + "value": "wizards_den_eu_west" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "Server", + "options": [ + { + "selected": true, + "text": "wizards_den_eu_west", + "value": "wizards_den_eu_west" + }, + { + "selected": false, + "text": "wizards_den_lizard", + "value": "wizards_den_lizard" + }, + { + "selected": false, + "text": "raspberry", + "value": "raspberry" + }, + { + "selected": false, + "text": "wizards_den_eu_west_2", + "value": "wizards_den_eu_west_2" + }, + { + "selected": false, + "text": "wizards_den_centipede", + "value": "wizards_den_centipede" + } + ], + "query": "wizards_den_eu_west, wizards_den_lizard, raspberry, wizards_den_eu_west_2, wizards_den_centipede", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Perf Metrics", + "uid": "qyFqpFiGk", + "version": 44, + "weekStart": "" +} +``` \ No newline at end of file diff --git a/src/ua/community/infrastructure-reference/publishing-scripts.md b/src/ua/community/infrastructure-reference/publishing-scripts.md new file mode 100644 index 000000000..04357e16e --- /dev/null +++ b/src/ua/community/infrastructure-reference/publishing-scripts.md @@ -0,0 +1,208 @@ +# Publishing Scripts + +These are the shitty scripts we use to host official SS14 builds. In hindsight I should have published these earlier. I am not saying these are well engineered, I am not saying you should do it like this. They're provided as a reference into how our spaghetti mess works, it's not an official supported way to do things. **You should properly read and understand these before using them**. + +`/home/robust-build-push/push.ps1` +```ps1 +#!/usr/bin/env pwsh + +$buildsBaseDir = "/home/wizards-build-push/builds_dir/" +$buildsJson = join-path $buildsBaseDir "manifest.json" +$buildsHtml = join-path $buildsBaseDir "builds.html" +$buildsDir = join-path $buildsBaseDir builds +$urlFmt = "https://cdn.centcomm.spacestation14.com/builds/wizards/builds/{0}/{1}" + +$version = $args[0] + +$versionBuildsDir = join-path $buildsDir $version + +$clientFile = join-path $versionBuildsDir "SS14.Client.zip" + +$build = @{ + "time" = (Get-Date -Format "o"); + "server" = @{}; + "client" = @{ + "url" = $urlFmt -f ($version, "SS14.Client.zip"); + "sha256" = Get-FileHash -Algorithm SHA256 $clientFile | Select-Object -ExpandProperty Hash + } +} + +get-childitem $versionBuildsDir | % { + Write-Host $_ + $match = [regex]::Match($_.Name, "SS14.Server_(.+)\.zip"); + if (-not $match.Success) { + return; + } + + $rid = $match.Groups[1].Value + $url = $urlFmt -f ($version, $_.Name) + $sha256 = Get-FileHash -Algorithm SHA256 $_ | Select-Object -ExpandProperty Hash + + $build["server"][$rid] = @{ + "url" = $url; + "sha256" = $sha256 + } +} + +$build | convertto-json -depth 10 | Write-Host + +$jsonDat = (get-content $buildsJson | convertfrom-json -AsHashtable) ?? @{"builds" = @{}} +$jsonDat["builds"][$version] = $build +$jsonDat | convertto-json -compress -depth 10 | set-content $buildsJson + +./notify_update.sh +./update_last_build_page.py $buildsJson $buildsHtml +``` + +`/home/robust-build-push/notify_update.sh` +```bash +#!/bin/bash + +# Robust.Cdn +curl -X POST -d "" -H 'Authorization: Bearer <token>' "http://localhost:27690/control/update" +# Game servers +curl -X POST -d "" -H "Authorization: Basic <token>" "https://lizard.spacestation14.io/watchdog/instances/wizards_den_lizard/update" +curl -X POST -d "" -H "Authorization: Basic <token>" "https://lizard.spacestation14.io/watchdog/instances/salamander/update" +curl -X POST -d "" -H "Authorization: Basic <token>" "https://mommi.spacestation14.io/watchdog/instances/wizards_den_eu_west/update" +curl -X POST -d "" -H "Authorization: Basic <token>" "https://miros.spacestation14.io/watchdog/instances/wizards_den_eu_west_2/update" +curl -X POST -d "" -H "Authorization: Basic <token>" "https://centipede.spacestation14.io/watchdog/instances/centipede/update" +``` + +`/home/robust-build-push/delete_old.ps1` +```ps1 +#!/usr/bin/env pwsh + +$buildsBaseDir = "/home/wizards-build-push/builds_dir" +$buildsJson = join-path $buildsBaseDir "manifest.json" +$buildsDir = join-path $buildsBaseDir builds + +$jsonDat = (get-content $buildsJson | convertfrom-json -AsHashtable) +$interval = [System.TimeSpan]::FromDays(30) +$delBefore = [System.DateTimeOffset]::Now - $interval + +$toDel = $jsonDat["builds"].GetEnumerator() | where {[DateTimeOffset]::Parse($_.Value["time"]) -lt $delBefore} | select -exp Key + +$toDel | % { $jsonDat["builds"].Remove($_) } +$toDel | % { rm -r $(Join-Path $buildsDir $_) } + +$jsonDat | convertto-json -compress -depth 10 | set-content $buildsJson +``` + +`/home/robust-build-push/update_last_build_page.py` +```python +#!/usr/bin/env python3 +import json +import sys +from datetime import timezone +from dateutil.parser import parse +from html import escape + +TEMPLATE = """ +<!DOCTYPE html> +<html lang="en-US"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <title>SS14 Server Builds + + + +

+ Here you can find the latest server build available for Space Station 14. +

+

+ The latest build is from $BUILDDATE. Pick whatever build suits the operating system you're trying to host on: +

+ +
    + $BUILDS +
+ +

+ The version is $VERSION +

+ +

+ If you somehow ended up here without reading the Server Hosting Tutorial, go read that first. It tells you how to actually host a server. +

+ + +""" + +RIDMAP = { + "linux-x64": "Linux x64", + "linux-arm64": "Linux ARM64", + "win-x64": "Windows x64", + "osx-x64": "macOS x64" +} + +if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} ") + exit(1) + +manifestPath = sys.argv[1] +outputPath = sys.argv[2] + +with open(manifestPath, "r") as f: + manifest = json.load(f) + +last_build = max( + manifest["builds"].items(), + key=lambda x: parse(x[1]["time"])) + +print(f"Last build: {last_build[0]}: {last_build[1]['time']}") + +buildVersion = last_build[0] +buildInfo = last_build[1] + +buildDate = parse(buildInfo["time"]) +buildDateUtc = buildDate.astimezone(timezone.utc) +buildDateFmt = f"{buildDateUtc:%A, %d %B %Y at %H:%M UTC}" + +listHtml = "" + +for (rid, info) in sorted(buildInfo["server"].items(), key=lambda x: RIDMAP.get(x[0], x[0])): + url = info["url"] + listHtml += f"
  • {RIDMAP.get(rid, rid)}
  • " + +finalHtml = TEMPLATE \ + .replace("$BUILDS", listHtml) \ + .replace("$VERSION", buildVersion) \ + .replace("$BUILDDATE", buildDateFmt) + +with open(outputPath, "w") as f: + f.write(finalHtml) +``` + +## FAQ + +### Why the fuck would you use Powershell + +It's better than Bash. \ No newline at end of file diff --git a/src/ua/community/infrastructure-reference/wizards-den-infrastructure.md b/src/ua/community/infrastructure-reference/wizards-den-infrastructure.md new file mode 100644 index 000000000..ed73071e2 --- /dev/null +++ b/src/ua/community/infrastructure-reference/wizards-den-infrastructure.md @@ -0,0 +1,70 @@ +# Wizard's Den Infrastructure + +This is a reference for how the official Wizard's Den game server infrastructure works. It may help you make educated decisions about hardware required or how to set up your infrastructure. This is not an endorsement of any technique or provider, it's just what we use. + +## Hardware + +If I don't list something like network bandwidth it's cuz I'm too lazy to check. You're not gonna run out, don't worry. + +### Centcomm + +Centcomm is our central server that hosts both the central SS14 infrastructure like authentication, aswell as Wizard's Den infrastructure like the game database, wiki, forum, etc... It is hosted in US East. + +Specs: +* **CPU:** Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz +* **RAM:** 16 GB +* **SSD:** 256 GB +* **HDD:** 2 TB +* **Network:** 150 TB bandwidth, gigabit + +This serves all CDN downloads and it comes up to less than 2 TB/month bandwidth. + +### Lizard + +Hosts Wizard's Den Lizard and Wizard's Den Salamander in US West. + +Specs: +* **CPU:** AMD Ryzen 5 5600X 6-Core Processor +* **RAM:** 16 GB +* **SSD:** 256 GB +* **Network:** 150 TB bandwidth, gigabit + +This is our most loaded server, and it comes out to 5 TB/month bandwidth max. + +### Spider + +Home server of PJB, so shared with some non-SS14 infra and other cruft (somebody please play Xonotic with me). Hosted in Belgium. + +Specs (it's just an old PC): +* **CPU:** Intel(R) Core(TM) i5-3570K CPU @ 3.40GHz +* **RAM:** 16 GB +* **SSD:** 1 TB + +### Miros + +Auctioned server rented from Hetzner, hosted in Germany. + +* **CPU:** Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz +* **RAM:** 64 GB +* **SSD:** 512x2 GB (RAID 1) + +### Centipede + +Hosted by Austation in Australia + +* **CPU:** 2 cores of an Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz +* **RAM:** 4 GB +* **SSD:** 64 GB + +### Noodle + +Noodle acts as a hypervisor and runs two separate systems, Leviathan and OpenDream1. Hosted in Miami. + +* **CPU:** AMD Ryzen 5 5600X 6-Core Processor +* **RAM:** 64GB +* **SSD:** 512 GB + +Each VM guest has 6 CPUs and 16 GB RAM assigned. + + + diff --git a/src/ua/community/infrastructure-references.md b/src/ua/community/infrastructure-references.md new file mode 100644 index 000000000..9fbc75a11 --- /dev/null +++ b/src/ua/community/infrastructure-references.md @@ -0,0 +1,3 @@ +# Infrastructure References + +This section contains references on how to set up server infrastructure directly from how Wizard's Den infrastructure is managed. \ No newline at end of file diff --git a/src/ua/community/space-wizards-hub-rules.md b/src/ua/community/space-wizards-hub-rules.md new file mode 100644 index 000000000..d9b3d1d12 --- /dev/null +++ b/src/ua/community/space-wizards-hub-rules.md @@ -0,0 +1,108 @@ + + +# Space Wizards Hub Rules +This document specifies the rules that need to be followed when listing your community on our server hub[^hub]. We will try to be as transparent and reasonable as possible when enforcing these rules. If you are not sure whether something is allowed, or you think that something that is not allowed by the current rules should be allowed, feel free to contact us. We will clarify and possibly amend the rules. + +We may change these rules from time to time, for example to add clarifications. When we do, it will be announced on [the forum](https://forum.spacestation14.io/index.php?/forum/29-hub-administrative/). + +[^hub]: effectively, the server browser in the launcher. + +## How These Rules Are Applied + +### Strike System + +Breaking some rules will lead to your community receiving one or more *strikes*. Strikes expire a year after their cause or causes are resolved. If you have **three or more active strikes**, your community will be permanently removed from the hub. + +You will be allowed to be relisted after enough strikes have expired to put you below three again. You must also have resolved the issue that caused the last strike. Contact us when this is the case, as we may not automatically relist you ourselves. + +### Scope of Rules + +These rules apply to any communities that have at least one server listed on the server hub. Our jurisdiction is as far as to remove your server(s) from the hub, and nothing more. + +We expect you to enforce some of these rules in other places than simply on-hub game servers. For example, 18+ rules may need to be enforced in your Discord or on any off-hub servers you have. + +We draw the line at what encapsulates a **"community"** on a case-by-case basis. This is based on shared branding, community spaces (e.g. Discord), infrastructure, and staff. For example, if you are a host that merely provides infrastructure for another community you will get some degree of separation from said community. However, severe enough offenses may still make us come to you. + +When deciding whether an issue is your community's fault we will look at factors such as: +- The status of the perpetrators in your community +- The severity of the issue +- Whether your community facilitates the issue or punished people for it. + +Some examples: +- Not accountable: a random player trashtalking another server after being banned there. +- Accountable: a staff member perpetuating lies about another server to the point it becomes an issue for them. +- Accountable: harassment perpetuated by a large group of members, with staff aware but letting it happen. + +### Enforcement Procedure + +Depending on the scope and nature of the violation in question we will try to contact you privately and resolve things cleanly without a lasting strike. This depends on the kind offense in question and how cooperative you were to resolve the matter. + +If we do decide to hand out a strike, we expect you to resolve the problem within a reasonable timeframe. Failing this we may delist your server until the root problem is solved, regardless of total strikes remaining. + +We expect to be able to contact you about matters of the hub rules. You should have some form of **contact information** on your server: something as simple as a Discord or website link we can follow as a paper trail is good enough. If we are unable to contact you we may be forced to go immediately to delisting. + +## The Rules + +### Section 1 (high severity) +These are the most important rules. Breaking one of them will lead to your community receiving up to three strikes at once. If you have a history of breaking these rules, that alone might be enough to disallow you from listing your server on the hub. + +**1.1 Do not harass, bully, brigade, defame, or doxx players, developers, or communities.** +- Sharing IPs or hardware IDs with other server hosts for the purpose of banning is allowed. +- Some guidance on what "harassment" constitutes: if somebody wants you to stop talking to or about them, you probably should. + +**1.2. Do not attempt anything that is illegal in the United States of America.** +- For example: + - network attacks such as distributed denial of service or unauthorized access, + - distribution of malware, + - phishing. +- If applicable, we may report you to government authorities. +- Security vulnerabilities can be reported to our security team by joining our Discord and pinging or talking to someone with the `@Wizard` or `@SS14 Maintainer` role. +- Do not violate software licences of other servers on the hub. This includes re-distributing the source code of secret repos or other license violations. + +**1.3. Do not abuse the hardware or internet connection of players for purposes that are not directly related to playing the game or collecting telemetry, even with consent.** +- For example, do not mine cryptocurrency using the players' hardware. +- Redirecting the players' connection to another server you control or have permission to redirect to is allowed, as long as you have player consent. + +**1.4. Servers containing mature elements such as erotic roleplay MUST clearly be marked as being 18+.** +- Do this by putting `[18+]` clearly in the server name somewhere. + +**1.5. If your server or community as a whole is 18+, you must do due diligence to remove underage players** +- Age checks are not mandatory, though removing players if you learn they are underage is. +- If you do not have mature themes (rule 1.4) and choose to mark your server as 18+ anyways, you still cannot make exceptions here. + +**1.6. Server staff members must not have a record of grooming or other predatory behavior.** +- This counts for all servers, not just exclusively 18+ servers. + +**1.7. Do not send false information to the hub.** +- For example, do not lie about player count or about what region your server is in. + +**1.8. Do not attempt to abuse loopholes in the hub rules or circumvent rulings** +- For example: trying to circumvent strikes by making a "new" community. + +### Section 2 (medium severity) +Breaking one of these rules will lead to your community receiving at most one strike. + +**2.1. Keep your server names and descriptions clean** +- They are visible to every player who uses the hub. +- Keep it safe for work and free from vulgarity. +- No bigotry, hate speech, descrimination, etc... + +**2.2. Do not impersonate other servers, developers, or organizations.** +- If there is a reasonable way for players to be confused about the affiliations of your server, you're probably breaking this. + +**2.3. Don't intentionally cause any sort of technical trouble for other communities, servers, the hub, or players.** + + +### Section 3 (low severity) +Breaking these rules will not lead your community to receiving any strikes, unless they are repeatedly broken or not resolved within a reasonable timeframe. + +**3.1. Your server name must not have any advertisements.** +- For example, "Hosted by XXX". +- You are allowed to put these in the server description (inside the foldout). diff --git a/src/ua/community/space-wizards-role-hierarchy.md b/src/ua/community/space-wizards-role-hierarchy.md new file mode 100644 index 000000000..7c41b7d74 --- /dev/null +++ b/src/ua/community/space-wizards-role-hierarchy.md @@ -0,0 +1,7 @@ +# Space Wizards Role Hierarchy + + + +The XML for this diagram is available [here](../assets/misc/role_hierarchy.xml). You can edit it by going to [draw.io](https://app.diagrams.net/), going to `File -> Import from -> Device`, then selecting the file. You should export by going to `File -> Embed -> HTML`. You should also update the [XML](../assets/misc/role_hierarchy.xml) by going to `File -> Export as -> XML`. + +
    Wizard
    Project Manager
    SS14
    Maintainer
    Experienced Contributor
    Contributor
    Inactive Contributor
    Game Admin
    Trial Admin
    Head Game Admin
    Moderator
    RobustToolbox
    Maintainer
    Head Mapper
    Junior Maintainer
    Art Director
    \ No newline at end of file diff --git a/src/ua/general-development/codebase-info.md b/src/ua/general-development/codebase-info.md new file mode 100644 index 000000000..7a23bad56 --- /dev/null +++ b/src/ua/general-development/codebase-info.md @@ -0,0 +1,3 @@ +# Codebase Info + +This section contains info pertinent to the codebase and contribution, such as the code conventions and pull request guidelines. \ No newline at end of file diff --git a/src/ua/general-development/codebase-info/acronyms-and-nomenclature.md b/src/ua/general-development/codebase-info/acronyms-and-nomenclature.md new file mode 100644 index 000000000..50005a41a --- /dev/null +++ b/src/ua/general-development/codebase-info/acronyms-and-nomenclature.md @@ -0,0 +1,26 @@ +# Acronyms and Nomenclature + +| Shorthand | Meaning | +|:------------------------ |:-------------------------------------------------------------------------------------------------------------- | +| SS14 | Space Station 14. Remake of Space Station 13 (SS13). | +| BYOND | The game engine for SS13. | +| RobustToolbox, Engine | SS14's game engine. (Think BYOND). | +| Content, Content Pack | The "game" running on RobustToolbox. (Think SS13). | +| CVar | Convar/Console Variable. Configurable value that you can change in the config files or through the console. | +| .yml, YAML | YAML Ain't Markup Language. Used to define prototypes. | +| .toml, TOML | Tom's Obvious Minimal Language. Like YAML but for config. | +| ECS | Entity Component System | +| IoC | [Inversion of Control](../../robust-toolbox/ioc.md) | +| .dmi, DMI | BYOND/SS13's sprite file format. Converted to an RSI for our usage. | +| .rsi, RSI | Robust Station Images. SS14's image "file" format (actually a folder). | +| PVS | Potentially Visible Set. Stops the server from sending out-of-range entities to clients. | +| PJB, PJB3005 | Pieter-Jan Briers. Supreme ^Nerd^ overlord of SS14. | +| VSC, VSCode | Visual Studio Code. Not the same as VS. | +| VS | Visual Studio Community 2017/19. Not the same as VSC. The IDE for people who can't get a Rider license. | +| Rider | [A crossplatform IDE for C#.](https://www.jetbrains.com/rider/) (Can be obtained for free as a student). | +| Watchdog | SS14 Server Watchdog. Used for dedicated server logging, updating and general management. | +| Lidgren | Networking library. | +| Box2D | The basis for SS14's (heavily modified) physics system. | +| Avalonia | UI framework used for the launcher. | +| Postgres, SQLite | Databases. | +| EntityUid | Entity Unique Identifier | \ No newline at end of file diff --git a/src/ua/general-development/codebase-info/codebase-organization.md b/src/ua/general-development/codebase-info/codebase-organization.md new file mode 100644 index 000000000..31e020a30 --- /dev/null +++ b/src/ua/general-development/codebase-info/codebase-organization.md @@ -0,0 +1,50 @@ +# Codebase Organization + +## Projects + +SS14 and RobustToolbox are split into several different projects. The main ones you'll care about are the `Client`, `Shared`, and `Server` projects. Other projects are for smaller things like integration tests, benchmarks, or database-specific code. + +`Client`, `Shared`, and `Server` are each packaged into different 'assemblies', which is basically .NET talk for executables or shared libraries. + +The `Client` project in both Robust and SS14 contains client-specific code, like UI. This assembly is only sent to the client, the person actually playing the game. + +The `Server` project contains server-specific code that no specific client should be able to interact with, like atmospherics or botany. This assembly is only located on the game server. + +The `Shared` project contains shared code that can be used by the client or the server. This assembly is not executable, and it relies on the client or server to call functions in it or use data classes located within it. The purpose of shared is to allow for network prediction (where the client and server run the same code, to make things smoother) as well as to specify shared data classes, like network messages, so that the client and server can speak to eachother effectively. + +Shared code is only allowed to access other shared code, not client or server code. However, client and server code are always allowed to access shared code. + +## Game Code + +In SS13, all game code is randomly thrown around under `code/`, and instead of grouping by relevance to systems, is grouped by abstract things like whether the file is a list of constants or whether a file pertains to a master controller subsystem. In SS14, we first delineate by which game system we're working with (atmos/botany/buckling, etc) and then by the classes needed for it, which is much easier for anyone actually trying to work within a single system. + +`Content.Client`, `Content.Shared`, and `Content.Server` all follow this organization. RobustToolbox's equivalents do not currently, but will in the future. + +- All game code will be organized in folders directly under Content.Client/Shared/Server etc. +- Game code folders are split into Components, EntitySystems, Visualizers, UI, Prototypes, etc +- If there would only be one file in a folder, it doesn't need a folder (unless that file would go directly into the project's top directory, which is undesirable). +- Do not use 'misc' folders; misc folders are hell for organization and its completely arbitrary what goes inside them and what doesn't. You can encapsulate smaller game systems inside larger game systems if it unambiguously makes sense (Atmos -> Piping), but don't just slap all the smaller game systems into a misc folder. + +This structure should hopefully be very clear after working with it or seeing examples. + +A real example, under `Content.Server` at `da11cbd8e6bef3373ec1f570df7d7b9155a3890f` + +![](../../assets/images/codebase-server-example.png) + +- Atmos is a fairly large game system. It has many folders, and many files that do not need to go in these folders. +- Botany is a smaller game system. However, it only has one folder for Components since that's all that's really there. +- ItemCabinets are a very small game system. They just have a component and EntitySystem, and thus do not need folders for each. + +## Resources + +The resources folder is another area where we hope to improve over the organization structure of SS13 codebases. + +### Entity Prototypes + +New folders should usually only be created for a new parent type. If you find something that can be pulled out into a parent prototype, it should go in its own folder. + +Parent prototypes should be contained in `base.yml` in this folder, while other prototypes go in a different file. + +Not everywhere is organized like this; however, the `Structures` folder is. + +This was chosen to make the directory structure mirror the prototype inheritance tree, making it obvious where to place new prototypes as well as being fairly unambiguous when choosing to create new folders. diff --git a/src/ua/general-development/codebase-info/conventions.md b/src/ua/general-development/codebase-info/conventions.md new file mode 100644 index 000000000..fd2e14906 --- /dev/null +++ b/src/ua/general-development/codebase-info/conventions.md @@ -0,0 +1,580 @@ +# Conventions + +There are nearly infinite ways to program the same thing, but some ways will get your PR rejected. + +In this page you'll learn all about the coding conventions we have chosen for the codebase, which you'll need to follow if you want to get your PR merged. + +See [Codebase Organization](./codebase-organization.md) for guidelines on how files and folders are organized in the SS14 codebase. + +Read the [Pull Request guidelines](./pull-request-guidelines.md) to learn how to make your code more reviewable by maintainers. + +```admonish info +Keep in mind that some older areas of the codebase might not follow these conventions. These should be refactored in the future to follow them. All new code should try to follow these conventions as closely as possible. +``` + +## File Layout + +1. Start with [using directives](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive) at the top of the file. + +2. All classes should be explicitly namespaced. Use [file-scoped namespaces](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/file-scoped-namespaces), e.g. a single `namespace Content.Server.Atmos.EntitySystems;` before any class definitions instead of `namespace Content.Server.Atmos.EntitySystems { /* class here */ }`. + +3. Always put all fields and auto-properties before any methods in a class definition. + +## Comments + +- Comment code at a high level to explain *what* the code is doing, and more importantly, *why* code is doing what it is doing. + +- When documenting classes, structs, methods, properties/fields, and class members, use [XML docs](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/) + +### Why Not What + +Some folks blindly adhere to "comment the why, not the what" and think that "code should be self-documenting and comments a last resort". Below we present a few examples that we hope will change your mind. + +#### Example 1 + +```csharp + float fractionalPressureChange = Atmospherics.R * (outlet.Air.Temperature / outlet.Air.Volume + inlet.Air.Temperature / inlet.Air.Volume); +``` + +All of the variables are named in a self-documenting way (*R* gets a pass because that is the ideal gas constant, and physics conventions existed long before computers, so this is following convention). Obviously, the comment should *not* be: + +```csharp + // Take R and multiply it by the ratio of outlet temperature divided by outlet air volume and add it to ... + float fractionalPressureChange = Atmospherics.R * (outlet.Air.Temperature / outlet.Air.Volume + inlet.Air.Temperature / inlet.Air.Volume); +``` + +Because this only explains what the code is literally doing, which you could have gathered from any cusory reading of the code. **However, you still have absolutely no idea what this code is doing and why**, even though the code is self-documenting. + +You don't know where this magic formula came from, what it's trying to accomplish, or even if the formula is correct. Therefore, this needs to be documented: + + +```csharp + // We want moles transferred to be proportional to the pressure difference, i.e. + // dn/dt = G*P + + // To solve this we need to write dn in terms of P. Since PV=nRT, dP/dn=RT/V. + // This assumes that the temperature change from transferring dn moles is negligible. + // Since we have P=Pi-Po, then dP/dn = dPi/dn-dPo/dn = R(Ti/Vi - To/Vo): + float dPdn = Atmospherics.R * (outlet.Air.Temperature / outlet.Air.Volume + inlet.Air.Temperature / inlet.Air.Volume); +``` + +#### Example 2 + +```csharp + if (HasComp(uid)) + { + return; + } + + // more stuff +``` + +Obviously, this code skips "more stuff" if the entity represented by *uid* already has a MindContainerComponent. This code is as self-documenting as it gets, it literally just returns early if there is a MindContainer. What needs to be documented is *why* this code needs to skip *uid*s that already have a MindContainerComponent: + + +```csharp + // Don't let players who drink cognizine be eligible for a ghost takeover + if (HasComp(uid)) +``` + +## Methods + +### Line breaks of parameter/argument lists + +If you're defining a function and the parameter declarations are so long they don't fit on a single line, break them apart so you have **one parameter per line**. Some leeway is granted for closely tied parameter pairs like X/Y coordinates and pointer/length in C APIs. + +Bad: + +```cs +public void CopyTo(ISerializationManager serializationManager, SortedDictionary source, ref SortedDictionary target, + SerializationHookContext hookCtx, ISerializationContext? context = null) +``` + +Good: + +```cs +public void CopyTo( + ISerializationManager serializationManager, + SortedDictionary source, + ref SortedDictionary target, + SerializationHookContext hookCtx, + ISerializationContext? context = null) +``` + +## Strings and Identifiers + +Human-readable text should never be used as an identifier or vice versa. That means no putting human-readable text (result of localization functions) in a dictionary key, comparing with `==`, etc... + +This avoids spaghetti when these inevitably have to be decoupled for various reasons, and avoids inefficiency and bugs from comparing human-readable strings. + +### Invariant comparisons on human-readable strings + +If you're doing something like a filter/search dialog, use `CurrentCulture` comparisons over human-readable strings. Do not use invariant cultures. + +## Properties + +In a property setter, the value of the property should always literally become the `value` given. None of this: + +```cs +public string Name +{ + get => _name; + private set => _name = Loc.GetString(value); +} +``` + +## Constants and CVars +If you have a specific value such as an integer you should generally make it either: +* a constant (const) if it's never meant to be changed +* a CVar if it's meant to be configured + +This is so it is clear to others what it is. This is especially true if the same value is used in multiple places to make the code more maintainable. + +## Prototypes + +### Prototype data-fields +Don't cache prototypes, use prototypeManager to index them when they are needed. You can store them by their ID. When using data-fields that involve prototype ID strings, use custom type serializers. For example, a data-field for a list of prototype IDs should use something like: +```csharp= +[DataField("exampleTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] +public List ExampleTypes = new(); +``` + +### Enums vs Prototypes +The usage of enums for in-game types is *heavily discouraged*. +You should always use prototypes over enums. +Example: In-game tool "kinds" or "types" should use prototypes instead of enums. + +## Resources + +### Sounds +When specifying sound data fields, use `SoundSpecifier`. + + + +
    + C# code example (click to expand) + +```csharp= +[DataField("sound", required: true)] +public SoundSpecifier Sound { get; } = default!; +``` + +
    + +
    + YAML prototype example (click to expand) + +```yml= +# You can specify a specific sound file like this +- type: MyComponent + sound: + path: /Audio/path/to/my/sound.ogg + +# But this works, too! +- type: MyOtherComponent + sound: /Audio/path/to/my/sound.ogg + +# You can only specify a sound collection like this +- type: AnotherComponent + sound: + collection: MySoundCollection + +``` + +
    + +### Sprites and Textures +When specifying sprite or texture data fields, use `SpriteSpecifier`. + +
    + C# code example (click to expand) + +```csharp= +[DataField("icon")] +public SpriteSpecifier Icon { get; } = SpriteSpecifier.Invalid; +``` + +
    + +
    + YAML prototype example (click to expand) + +```yml= +# You can specify a specific texture file like this +- type: MyComponent + icon: /Textures/path/to/my/texture.png + +# You can specify an rsi sprite like this +- type: MyOtherComponent + icon: + sprite: /Textures/path/to/my/sprite.rsi + state: MySpriteState +``` + +
    + +
    + RSI meta.json (click to expand) + +- The order of fields should be `version -> license -> copyright -> size -> states`. +- JSON should not be minified, and should follow normal JSON quality guideliens (egyptian brackets, etc) + +Example: + +```json +{ + "version": 1, + "license": "CC0-1.0", + "copyright": "GitHub @PJB3005", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "hello", + "flags": {}, + "directions": 4, + "delays": [ + [1, 1, 1], + [2, 3, 4], + [3, 4, 5], + [4, 5, 6] + ] + } + ] +} +``` +
    + +### EntityUid in Logs +When using `EntityUid` in admin logs, use the `IEntityManager.ToPrettyString(EntityUid)` method. + +
    + Admin log with entities example (click to expand) + +```csharp= +// If you're in an entity system... +_adminLogs.Add(LogType.MyLog, LogImpact.Medium, $"{ToPrettyString(uid)} did something!"); + +// If you're not in an entity system... +_adminLogs.Add(LogType.MyLog, LogImpact.Medium, $"{entityManager.ToPrettyString(uid)} did something!"); +``` + +
    + +### Optional Entities +If you need to pass "optional" entities around, you should use a nullable `EntityUid` for this. +Never use `EntityUid.Invalid` to denote the abscence of `EntityUid`, always use `null` and nullability so we have compile-time checks. +e.g. `EntityUid? uid` + +## Components + +### Component data access modifiers +All data in components should be public. + +### Component property setters +You may not have setters with any logic whatsoever in properties. Instead, you should create a setter method in your entity system, and apply the `[Friend(...)]` attribute to the component so only that system can modify it. +Your component may use properties with setter logic for *ViewVariables integration* (until we have a better system for that) + +### Component access restrictions +The `[Access(...)]` attribute allows you to specify which types can read or modify data in your class, while prohibiting every other type from modifying it. + +Components should specify their access restrictions whenever possible, usually only allowing the entity systems that wrap them to modify their data. + +### Shared Component inheritance +If a shared component is inherited by server and client-side counterparts, it should be marked as *abstract*. + +## Entity Systems + +### Game logic +Game logic should *always* go in entity systems, not components. +Components should *only* hold data. + +### Proxy Methods +When possible, try using the `EntitySystem` [proxy methods](https://github.com/space-wizards/RobustToolbox/blob/master/Robust.Shared/GameObjects/EntitySystem.Proxy.cs) instead of using the `EntityManager` property. + +
    + Examples (click to expand) + +```csharp= +// Without proxy methods... +EntityManager.GetComponent(uid).EntityName; + +// With proxy methods +Name(uid); + +// Without proxy methods... +EntityManager.GetComponent(uid).Coordinates; + +// With proxy methods +Transform(uid).Coordinates; +``` + +
    + +### Public API Method Signature +All public Entity System API Methods that deal with entities and game logic should *always* follow a very specific structure. + +All relevant `EntityUid` should come first. +Next, any arguments you want should come afterwards. +And finally, all the components you need from the entity or entities should come last. +The component arguments shall be nullable, and default to `null`. + +The first thing you should do in your method's body should then be calling `Resolve` for the entity UID and components. + +
    + Example (click to expand) + +```csharp= +public void SetCount(EntityUid uid, int count, StackComponent? stack = null) +{ + // This call below will set "stack" to the correct instance if it's null. + // If all components were resolved to an instance or were non-null, it returns true. + if(!Resolve(uid, ref stack)) + return; // If the component wasn't found, this will log an error by default. + + // Logic here! +} +``` + +
    + +The `Resolve` helper performs a few useful checks for you. In `DEBUG`, it checks whether the component reference passed (if not null) is actually owned by the entity specified. + +This helper will also log an error by default if the entity is missing any of the components that you attempted to resolve. +This error logging can be disabled by passing `false` to the helper's `logMissing` argument. You may want to disable the error logging for resolving optional components, `TryX` pattern methods, etc. + +Please note that the `Resolve` helper also has overloads for resolving 2, 3 or even 4 components at once. +If you want to resolve components for multiple entities, or you want to resolve more than 4 components at once for a given entity, you'll need to perform multiple `Resolve` calls. + +### Extension Methods + +Extension methods (those with an explicit `this` for the first argument) should never be used on any classes directly related to simulation--that means `EntityUid`, components, or entity systems. Extension methods on `EntityUid` are used throughout the codebase, however this is bad practice and should be replaced with entity system public methods instead. + +### Dependencies On Other Systems +Inside an entity system, prefer a system dependency instead of resolving the system using the IoCManager. For example, instead of: + +```csharp= +var random = IoCManager.Resolve(); +random.Prob(0.1f); +``` + +Add an entity system dependency: + +```csharp= +[Dependency] private readonly IRobustRandom _random = default!; +_random.Prob(0.1f); +``` + +## Events + +### Method Events vs Entity System Methods +Method Events are events that you raise when you want to perform a certain action. Example: +```csharp= +// This would change the damage on the entity by 10. +RaiseLocalEvent(uid, new ChangeDamageEvent(10)); +``` +On the other hand, Entity System Methods are methods you call on systems to perform an action. +```csharp= +// This would change the damage on the entity by 10. +EntitySystem.Get().ChangeDamage(uid, 10); +``` + +Method Events are *prohibited*, always use Entity System Methods instead. +There's an exception to this, however. + +You may use Method Events as long as they're wrapped by an Entity System Method. +In the example above, this would mean that `DamageableSystem.ChangeDamage()` would internally raise the `ChangeDamageEvent`, which would then by handled by any subscriptors... + +### Event naming +- Always suffix your events with `Event`. +Example: `DamagedEvent`, `AnchorAttemptEvent`... + +- Always name your event handler like this: `OnXEvent` +Example: `OnDamagedEvent`, `OnAnchorAttemptEvent`... + +### Struct by-ref events +Events should always be structs, not classes, and should always be raised by ref. If possible it should also be readonly if applicable. +They should also have the [ByRefEvent] attribute. + +In practice this will look like the following: +```cs + var ev = new MyEvent(); + RaiseLocalEvent(ref ev); +``` + +### C\# Events vs EventBus Events +The EventBus should generally be used over C# events where possible. C# events can leak, especially when used with components which can be created or removed at any time. + +C# events should be used for out-of-simulation events, such as UI events. +Remember to *always* unsubscribe from them, however! + +### Async vs Events +For things such as DoAfter, always use events instead of async. + +Async for any game simulation code should be avoided at all costs, as it's generally virulent, cannot be serialized (in the case of DoAfter, for example), and usually causes icky code. +Events, on the other hand, tie in nicely with the rest of the game's architecture, and although they aren't as convenient to code, they are definitely way more lightweight. + +## UI + +### XAML and C#-defined UIs +You should always use XAML over UIs defined entirely in C# code. +Extending existing C#-defined UIs is fine, but they should be converted eventually. + +## Performance + +### Iterator Methods vs returning collections +Always use [iterator methods](https://docs.microsoft.com/en-us/dotnet/csharp/iterators) over creating a new collection and returning it in your method. + +Keep in mind, however, that iterator methods allocate a lot of memory. +If you need to reduce allocations as much as possible, use struct iterators. + +### Sealed Classes +Your class must be marked as either `abstract`, `static`, `sealed` or `[Virtual]`. This is to avoid accidentally making classes inheritable when the shouldn't be and can improve performance slightly when accessing or invoking virtual members. + +Use `sealed` if the class shouldn't be inherited, `[Virtual]` for the normal C# behavior (it mutes the compiler warning), `static` for classes that don't need to be instantiated, or `abstract` if it's meant for being inherited but not meant to be instantiated by itself. + +### Events over updates +Where possible you should always have your system run code in response to an event rather than updating every tick. Your code may only take up 0.5% of CPU time but when 100 systems do this it's unnecessary. + +### Variable capture +When using [lambdas](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions) or [local functions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions) be sure to **avoid variable captures**. + +If you're adding a method that takes in a [Func delegate](https://docs.microsoft.com/en-us/dotnet/api/system.func-2), be sure to have an overload that **allows the caller to pass in custom data** to it. + +
    + Example of what not to do (click to expand) + +```csharp= +void DoSomething(EntityUid otherEntity) +{ + // This is BAD. It will allocate on the heap a lot. + var predicate = (EntityUid uid) + => uid == otherEntity; + + // This method doesn't allow us to pass custom data, + // so we're forced to do a costly variable capture. + MethodWithPredicate(predicate); +} + +void MethodWithPredicate(Func predicate) +{ + // We do something with the predicate here... +} +``` + +
    + +
    + Example of what to do (click to expand) + +```csharp= +void DoSomething(EntityUid otherEntity) +{ + // This is good and much more performant than the example before. + var predicate = (EntityUid uid, EntityUid otherUid) + => uid == otherUid; + + // Pass our custom data to this method. + MethodWithPredicate(predicate, otherEntity); +} + +// This method allows you to pass custom data into the predicate. +void MethodWithPredicate(Func predicate, TState state) +{ + // We do something with the predicate here, making sure to pass "state" to it... +} +``` + +
    + +## Naming + +### Shared types +Shared types should only be prefixed with `Shared` if and only if there are server and/or client inherited types with the same name. + +Example: +- If `FooComponent` only exists in shared, it doesn't need a prefix. +- If `BarComponent` exists in shared, server and client, the shared type should be prefixed with shared: `SharedBarComponent`. + +## Physics + +### Anchoring + +Always use `TransformComponent` anchoring. +You may use `PhysicsComponent` static body anchoring but *only* if you know what you're doing and you can defend your choice over transform anchoring. + +# YAML Conventions + +- Every component `- type` should be together without any empty newlines separating them +- Separate prototypes with one empty newline. +- `name:` and `description:` fields should never have quotations unless punctuation in the name/description requires the use of them, then you will use ''. For example: +```yaml + name: 'Spessman's Smokes packet' + description: 'A label on the packaging reads, 'Wouldn't a slow death make a change?'' +``` +- Dont specify textures in abstract prototypes/parents. +- You should declare the first prototype block in this order: `type` > `abstract` > `parent` > `id` > `name` > `description` > `components.` +- New components should not have an indent when added to the `components:` section. + This + ```yaml= + components: + - type: Sprite + state: + ``` + Not this + ```yaml= + components: + - type: Sprite + state: + ``` +- When it makes sense, place more generalized/engine components near the top of the components list and more specific components near the bottom of the list. For example, + ```yaml= + components: + - type: Sprite # Engine-specific + - type: Physics + - type: Anchorable # Content, but generalized + - type: Emitter # A component for a specific type of item + ``` + +### YAML and data-field naming +`PascalCase` is used for IDs and component names. +Everything else, even prototype type names, uses `camelCase`. +`prefix.Something` should NEVER be used for IDs. + +## Entities + +Please ensure you structure entities with components as follows for easier YAML readability: + +``` + type: entity + parent: Base + id: + name: + abstract: + components: + +``` + +### Entity Prototype suffixes + +Use `suffix` in prototypes, this it's a spawn-menu-only suffix that allows you to distinguish what prototypes are, without modifying the actual prototype name. You can use it like this: +![](https://i.imgur.com/epkPR3Y.png) + +And results in this: +![](https://i.imgur.com/JigMCuu.png) + +# Localization +Every player-facing string ever needs to be localized. + +### Localization ID naming +- Localization IDs are always `kebab-case` and should never contain capital letters. +- Localization IDs should be specific as possible, to avoid clashing with other IDs. + This + ```ftl= + antag-traitor-user-was-traitor-message = ... + ``` + Not this + ```ftl= + traitor-message = ... diff --git a/src/ua/general-development/codebase-info/pull-request-guidelines.md b/src/ua/general-development/codebase-info/pull-request-guidelines.md new file mode 100644 index 000000000..c41b32ee3 --- /dev/null +++ b/src/ua/general-development/codebase-info/pull-request-guidelines.md @@ -0,0 +1,113 @@ +# Pull Request Guidelines + +Following these guidelines will make contributing much easier, and will help your PRs be merged much faster by making them easy to review. + +- If you're unfamiliar with the Git workflow, please read [our git guide](../setup/git-for-the-ss14-developer.md) and ask as many questions as you need in #howdoicode. +- Do not use the GitHub web editor to make pull requests. You are required to make sure your code compiles and you've tested your changes in-game before making the PR. **PRs created through the Github web editor are liable to be closed at a maintainer's discretion, as they have not been tested locally and cannot meet these requirements.** Repeated submission of PRs made through the web editor may result in a repository ban. +- Please **re-read your code preview on GitHub** before submitting a PR. If it's obvious to a reviewer it includes your last PR (i.e. PR2 includes PR1's changes), or that there are accidental changes, then it should be obvious to you. +- If your PR adds a new feature, you should provide a screenshot or video of it functioning properly ingame. This not only makes reviewing easier, but also makes writing the progress report easier. +- Avoid adding miscellaneous additional changes to a PR, e.g. changing the heat resistance of a pair of gloves alongside your PR adding a new gun. +- Avoid making lots of formatting changes in a file if you change very few lines in it. It makes the review significantly more difficult to parse what actually changed and can generate conflicts for other PRs. +- If you do make refactoring/formatting changes, make them separate commits from actual content/feature/functionality changes so that they are easier to review. +- Large refactors that touch a lot of other systems belong in a separate refactor-only PR with no content changes +- If you move a file to a different folder and/or namespace, put that in its own commit when possible to make it easier to tell what got changed in a file and what was just moved. +- Please have some familiarity with [C# conventions](https://docs.microsoft.com/en-us/dotnet/csharp/) (if working with C#) and [our own conventions](./conventions.md). Try to read how other parts of the codebase are formatted for a general idea. +- Try to split your PR into smaller ones where it makes sense to do so. This makes it significantly easier to read and can lead to faster reviews. It's also usually easier for you, and means you will receive earlier feedback and can avoid spending time making changes that have to be reworked. +- Large new features and comprehensive reworks to existing large features (ie antags or anything that could be considered a subdepartment unto itself), should first be [proposed and accepted in abstract](../feature-proposals.md) before you start working on actually implementing it. +- Avoid force-pushing to your branch. This makes all reviews show as 'outdated', even if they have not been addressed yet, which makes it much harder for us. +- When you're addressing reviews, click 'Resolve conversation' on GitHub once your revised code has been pushed. +- If you have questions about reviews that were submitted on your PR (or code questions in general, of course), feel free to ask for clarification on GitHub or Discord in #howdoicode. + +## Reviews + +We get around 700 PRs a month and only have a small number of active maintainers. All maintainers are volunteers and maintainer availability is very variable. Depending on the size and importance of your PR, it could take up to a few weeks before someone gets around to reviewing it. Responding to any changes may also take some time. Please be patient. + +Anyone is welcome to review PRs. Reviews from other contributors can be just as valuable as reviews from maintainers, and often mean that PRs can be merged faster and can help relieve the worload for maintainers. If you are waiting for a review it might be a good idea to find another contributor in a similar position so that you can mutually review each other's PRs. Reading other people's PRs and thinking critically about how you would have written the code can also be a useful learning tool. + +### Asking for reviews +Please do not simply post your PR in our discord channels without context simply to ask for reviews. However, if your PR hasn't been reviewed by anyone and it has been a month, feel free to ping those listed here on GitHub or Discord. These aren't all of our maintainers, but they're currently the most active when it comes to reviews. + +**For content reviews:** +- mirrorcult#9528, @mirrorcult on GitHub +- metalgearsloth#7697, @metalgearsloth +- ElectroSR#9529, @ElectroJr +- /tmp/moony#0029, @moonheart08 + +**For engine reviews:** +- metalgearsloth#7697, @metalgearsloth +- PJB#3005, @PJB3005 +- ElectroSR#9529, @ElectroJr +- mirrorcult#9528, @mirrorcult + +## Adding screenshots/videos to your PR +PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes or the PR will not be merged, in accordance with our PR guidelines. Small fixes/refactors are exempt. If you include media in your pull request, we may potentially use it in the SS14 progress reports, with clear credit given. + +Use screenshot software like Window's built in snipping tool, ShareX, Lightshot, or recording software like ShareX (gif), ScreenToGif, or OBS (cross platform). +If you're unsure whether your PR will require media, ask a maintainer. + +## Changelog +Changelog entries help make players aware of new features or changes to existing features. + +### Changelog Template +The Github PR template contains the following changelog that you can use to format your changelog entry so that it is automatically updated in-game: + +``` +:cl: +- add: Added fun! +- remove: Removed fun! +- tweak: Changed fun! +- fix: Fixed fun! +``` + +By default, changes are credited to your Github username. If you would like your name to appear differently in-game, add a string on the same line as the `:cl:` with the name that you would like to use. + +Each entry is either an `add`, `remove`, `tweak`, or `fix`. There can be multiple entires in each category. These set the change log icon and do not show up in the change log text. + +Maintainers may, at their discretion, add, modify, or remove a change log entry that you suggest. + +### Writing An Effective Changelog +The Changelog is for *players* to be aware of new features and changes that could affect how they play the game. It is *not* designed for maintainers, admins, or server operators (these should be in the PR description). + +When writing your changelog entries, please follow these guidelines: + +1. **Log entries should be complete, gramatically-correct sentences.** They should begin with a capital letter and end in a period. + + - Not so good: "fixed reflected projectiles dealing stamina damage" This sentence does not begin with a capital letter, does not end with a period. + + - Not so good: "Wide attacks no longer cost stamina, deal weapon damage." Dangling clause after the comma. + + - Not so good: "There is now more structures that can be made out of web" Missing a period at the end of the sentence, and since "more structures" is plural, the correct verb conjugation is "are". But this entire sentence could be revised using the active voice, e.g. "More structures can now be made out of web" + + - Not so good: "A craft for cloth consisting of silk." This is not a complete sentence. + +2. **Log only changes with significant in-game impact.** This may include new features or changes or tweaks to existing features that affect balance. Minor changes to object apperances and descriptions typically do *not* affect how you would play the game. Changelog entries for major sprite updates are appropriate. + + - Good: "The R&D server can be deconstructed. Be warned: this resets all unlocked technology, points, and the current discipline." Without the Changelog entry, players may not know that R&D servers can now be deconstructed. It also provides them enough warning about losing technology so that they don't accidentally get surprised. + + - Not so good: "Adjusted pickaxe inhand sprites and added sprites for wielded pickaxes." You would see the changes when you decided to wield a pickaxe. Knowing that the pickaxes look different wouldn't change your traitor strategy. + + - Not so good: "Changed the plating sprite to be a little less blue." Same reason as above. + + - Not so good: "Changed sprites for some medipens." + +3. **Use the present, active voice.** + + - Not so good: "HAMTR mech has been added." + - Good: "The roboticist can now build the HAMTR mech." Revising this sentence in the active voice resulted in a more engaging sentence and included more relevant detail. + + - Not so good: "Added candy bowls for waiting lines." Who is doing the adding? + - Good: "Candy bowls can now be found near waiting lines." The subject is now "candy bowls". Each sentence has a subject and a verb. + +4. **Be concise.** Players should be able to understand the jist of the changes by skimming the Changelog. If they need more information, they can consult the guidebook. Avoid spamming multiple related changes across several different lines. If several security weapons were rebalanced, just say that to make players aware. + + - Not so good: "Central has distributed a new subversion of the standard particle accelerators. Nothing exciting, but they have brought back the old wiring layout. Apparently some of the newer versions were having firmware issues and it was more reliable. Keep on eye on it while it''s running will you? We don''t want an intern disabling the safeties and frying their face off." Do you understand what changed? Even the author thinks the change is "nothing exciting." + +5. **Avoid technical jargon.** + + - Not so good: "Fixed microwaves defaulting to 5 seconds when the ui said instant." What is a *ui*? It could be improved by using the accepted abbreviation, "UI". Aside: Is instant really instant now, or does it just default to 5 seconds? + +6. **Set the appropriate tone.** + + - Not so good: "Can you believe it? Arachnid re-rework just dropped! Check the PR for more details" + + - Not so good: "Arachnids have new sprites for being creampied." *crampied* has another, unfortunate meaning that undermines the professional tone of a Changelog entry. \ No newline at end of file diff --git a/src/ua/general-development/feature-proposals.md b/src/ua/general-development/feature-proposals.md new file mode 100644 index 000000000..5e17ea20c --- /dev/null +++ b/src/ua/general-development/feature-proposals.md @@ -0,0 +1,46 @@ +# Feature Proposals + +If you are considering adding or reworking some major component of the game it's recommended that you submit a proposal before you actually start coding whatever it is you want to do. Pinning down exactly how your feature should work to mesh well with the rest of the game will save you a lot of headaches rewriting your PR for the ~~1st2nd4th~~ 7th time before it gets merged. + +## How do I make a proposal? + +Just follow [our guide](../meta/guide-to-editing-docs.md) on editing any part of this site and PR your proposal as a page under the "Design Proposals" section found in the sidebar between "Space Station 14" and "Server Hosting". + +Proposals that get approved by a maintainer will be merged to this site, which is basically the green light for you or someone else to go ahead and implement it. + +``` admonish tip "Unfinished Proposals" +If you don't think that your proposal is ready for maintainer scrutiny, but still want feedback on it you can PR it as a draft. Drafts are less likely to attract people looking to get down to brass tacks, but still let people comment and give advice. +``` + +## No, as in what is a design doc? + +A design document is a high level summary of whatever you're proposing to add to the game. They basically serve to get nebulous ideas down on paper so it's easier to see how exactly those ideas will mesh with the rest of the project. They generally have a few parts. + +1. Why the feature is being proposed, which can be as simple as 'I think it would be cool' to as specific as 'I noticed ABC problems with cargos gameplay loop and want to add XYZ to remedy those problems'. +2. A high level summary of what the feature actually is, how the feature engages with players, and how the feature interacts with other major parts of the game. +3. A more detailed summary of the proposed mechanics for the feature, how players are intended to interact with those mechanics, and how those mechanics involve or are involved with other parts of the game. You don't need to go into every specific case, it's enough to say that there should be chemicals filling specific roles and not detail the exact chemicals you + +These don't have to be discrete sections and shouldn't be. When you're detailing the mechanics it's probably a good idea to detail how the players will interact with those mechanics and how the way the players interact with those mechanics benefits the game. If you want to see examples of successful design documents all of the accepted, but unimplemented, design docs can be found in the "Design Proposals" section below. Design documents that actually got implemented eventually transmute into the feature docs in the "Space Station 14" section. + +``` admonish warning "Don't" +- Include pseudocode level descriptions of how your feature works. That's for after the proposal has been accepted and you're actually implementing the thing. +- Specify numerical amounts for every item, field, or mechanic. That's for eventual balance bikeshedding; for example it's enough to say that a disease will have one, several, or many symptoms. +- Forget to include how players should interact with your features. SS14 is a multiplayer game and how the players engage with your mechanics is more important than the specific mechanics they engage with. +- Write an entire, actual FSD or SRS. It's not and will never be required as it constitutes egregious overkill for a project of our size and structure. +``` + +## Does my idea really need a design doc? + +It depends on the scale, and pervasiveness of whatever you're thinking of proposing. If it's something like adding a single gun or a couple simple plants it probably won't. On the other hand if you are adding an entire subdepartment ala anomalies/xenoarchaeology, or something on the order of reworking the entirity of botany or chemistry it certainly will. + +A good rule of thumb if the new feature or rework you have in mind would require adding or reworking a page of the guidebook or one of the feature docs on this site then it probably needs a design doc. Same if the proposal could be accurately described as 'reworking the entirity of X'. + +## Will my design doc get accepted? + +No idea! What design proposals do or do not get in is determined by maintainer approval like normal PRs. If you can get at least one maint to decide that it sounds like a good idea then there's a good chance that it will get accepted. Pretty much any idea is going to need at least some critiquing before it gets merged so don't get discouraged! + +``` admonish tip "Design Principles" +If you want to improve your chances, it's recommended that you read the [SS14 Fundamental Design Principles](./feature-proposals/ss14-fundamental-design-principles.md) document to get a high-level overview before you start writing, as it'll provide context for why things are the way they are. + +PR'd design documents should also follow the [Decorum Guidelines](./feature-proposals/expected-feature-proposal-decorum.md). +``` diff --git a/src/ua/general-development/feature-proposals/expected-feature-proposal-decorum.md b/src/ua/general-development/feature-proposals/expected-feature-proposal-decorum.md new file mode 100644 index 000000000..40aae7059 --- /dev/null +++ b/src/ua/general-development/feature-proposals/expected-feature-proposal-decorum.md @@ -0,0 +1,15 @@ +# Expected Feature Proposal Decorum + +{{#template ../../templates/wip.md}} + +- No half-baked stuff or scratchbooks or anything like that. Only fully-formed documents. +- If you’re the main author of something, put your name in the title in brackets. Otherwise, note all of the authors and designers in italics under the main header. The primary designers take priority over the author of a document. +- Not everything here is gospel, or going to be implemented into SS14. If a document is authored by a maintainer or signed off by one, it can be treated as a source of truth. + +Any documents proposed: + +- Should conform to the basic [SS14 Game Design Principles](./ss14-fundamental-design-principles.md) as well as this document obviously. +- Should contain sufficient justification for their existence, +- Can be closed or drafted at maintainer discretion, +- Are a reflection of SS14 to others that may be interested in how the game is designed. Take note of that. +p \ No newline at end of file diff --git a/src/ua/general-development/feature-proposals/ss14-fundamental-design-principles.md b/src/ua/general-development/feature-proposals/ss14-fundamental-design-principles.md new file mode 100644 index 000000000..93f629cc0 --- /dev/null +++ b/src/ua/general-development/feature-proposals/ss14-fundamental-design-principles.md @@ -0,0 +1,23 @@ +# SS14 Fundamental Design Principles + +{{#template ../../templates/wip.md}} + +The purpose of this document is to outline the basics of how Space Station 14 upstream design is approached, taking into consideration its status as a greenfield project and taking care to build a foundation for the future fork ecosystem. + +## Social Gameplay + +Mechanics should seek to be pro-social and encourage interacting with other players. These interactions need not be strictly cooperative or competitive in nature. Humans are chaotic by nature, and provide depth and replayability to games that cannot fully be achieved through programmed mechanics alone. Thus, we should utilize that power to drive gameplay. + +Mechanics which incentivize players to do them entirely alone, or mechanics which would be just as fun and fulfilling if done on a private test server with only one player, **will not be added to SS14**. + +These mechanics detract from the overall game by segmenting players into their own separate regions of play. Mechanics which are "simpler" but reward social gameplay will always be preferred to mechanics which are "deep" but are single-player. + +Parts of a whole gameplay loop or mechanic may be achievable by a single player. It's obviously not feasible to have everything forcibly involve multiple people, but care should be taken to try and incentivize social gameplay above singleplayer gameplay, even if something *can* be completed solo. + +An addendum to this principle is that, if a mechanic involves perceived NPCs or AI mobs, you should always try to find ways to put real humans in their place. For example, offering ghost roles to hostile mobs, or crafting an economy system that is player-motivated rather than externally-motivated. + +## Intuitive Simulation + +## Discoverable Interactions + +## Systems-based Design diff --git a/src/ua/general-development/setup.md b/src/ua/general-development/setup.md new file mode 100644 index 000000000..ac50b9229 --- /dev/null +++ b/src/ua/general-development/setup.md @@ -0,0 +1,3 @@ +# Setup + +This section includes basic setup guides for things like the development environment, and hosting a server. \ No newline at end of file diff --git a/src/ua/general-development/setup/git-for-the-ss14-developer.md b/src/ua/general-development/setup/git-for-the-ss14-developer.md new file mode 100644 index 000000000..724e9da32 --- /dev/null +++ b/src/ua/general-development/setup/git-for-the-ss14-developer.md @@ -0,0 +1,774 @@ +# Git for the SS14 Developer + +If you've ever followed a hackily written guide to Git or opened up one of the many incredibly bloated modern git GUIs like GitKraken, you probably recognize that Git can be *really confusing*. The purpose of this guide is to give you just the information you need to develop properly for SS14 and give you the resources to learn more if necessary. + +Here are some more resources for learning about Git: +- [The Git-SCM online book](https://git-scm.com/book/en/v2) +- [Atlassian's git guides](https://www.atlassian.com/git/tutorials/setting-up-a-repository). Good guides for some more advanced stuff +- [Oh shit, Git?!](https://ohshitgit.com/), a list of solutions to common git problems. This one will come in handy. +- [Learn Git Branching](https://learngitbranching.js.org/). This one is interactive, and very in-depth, but you **will have learned Git by the end of it**. Recommended for intermediate Git users. + +## 1. Setting up Git itself + +```admonish danger "DO NOT USE GITKRAKEN" +For the love of god do not install GitKraken or GitHub Desktop. I have felt nothing but endless CBT trying to help people using either of them. I know GitKraken looks all professional and GH Desktop is nice and simple but please do not use either of them unless you know what you are doing. +``` + +If you were following our **"Setting up a Development Environment"** guide, you probably already have Git installed. If not, go to [their website](https://git-scm.org) and install it now. This will install the Git backend, as well as Git Bash (if you select that option)--one of the many ways you can actually use Git. + +If you're on Linux, you'll probably just be using Git through your terminal or whichever IDE you've chosen, and chances are you have it installed already. + +I highly recommend at least trying Git Bash (as will a lot of our developers), but there are friendlier alternatives many use that I'll be showing steps for here as well: + +- [TortoiseGit](https://tortoisegit.org/) -- old but gold Git GUI that shows info in the file explorer menu and makes basic stuff a breeze +- [SmartGit](https://www.syntevo.com/smartgit/) -- fully featured Git GUI that's very customizable and simple to use + +I won't have steps for these (I'm recommending these after I initially wrote this guide) but after trying some more there are other very, very good options: + +- [Fork](https://git-fork.com/) -- fast and extremely ergonomic GUI, my personal favorite. "Non-free", but it's WinRAR-level non-free, so it's basically free. Has support for partial staging of +- [Sublime Merge](https://www.sublimemerge.com/) -- very similar to Fork, looks and feels great and I've gotten a lot of recommendations for it, though I haven't used it much. Also has support for partial staging. + +Most IDEs have some form of Git integration aswell. [Jetbrains Rider](https://www.jetbrains.com/rider/)'s Git integration is really good (and I personally recommend Rider for everything SS14-development related). I don't recommend Visual Studio's Git integration, because it's.. not very good. + +While you're here, install `Python 3.7+` as well if you don't have it already. You can do that [here](https://www.python.org/) for Windows and Mac, and if you're on Linux you almost certainly have Python installed already. If you don't, figure it out yourself, dork! + +
    + +Now that you have Git installed, I recommend you read up a bit on the basics of it first and get acquainted with whatever git client you're working with, whether its just command-line (Git Bash) or anything else. + +We're going to run through the process of setting up a Git environment for Space Station 14 so that you can **contribute code through pull requests, create your own codebase**, or just **check out the history of the project.** + +### 1.1 Why are we even using Git? + +Git is **version-control software**--basically, it's an easy way to track changes to the code, and manage those changes without headaches. It's an invaluable tool for software development, because it easily lets you make new changes, view different changes, see who made changes, etc. without having to coordinate and tabulate everything yourself. + +GitHub is an online service that hosts Git **repositories** (codebases) for easy collaboration. It's perfect for a codebase like SS14, with lots of contributors and lots of history. It also means that we're *open-source*--anyone can go to our GitHub and download the code! + +## 2. Setting up your repositories + +Like I said before, a repository is just a codebase. Repositories contain some **branches**, and those branches contain different **commits**. You might have heard of both of these--I'll talk more about them in depth later. + +A **remote** repository is just a repository's that's on GitHub. A **local** repository is one that's actually on your computer. + +### 2.1 Creating your remote repository + +First, let's make our own remote repository fork of Space Station 14. You'll need a GitHub account for this, of course. 'Forking' it like this just means you're copying all of the repository's history and changes into your own remote repository so that you can do stuff freely to the code. + +Your remote repository doesn't automatically update with changes from the original SS14 repo--you'll have to do that yourself, which I'll talk about later. + +Navigate to the [Space Station 14 repository](https://github.com/space-wizards/space-station-14) and click here: + +![](https://i.imgur.com/lAHNHdD.png) + +From there, it'll ask you where to fork it and what to name it--just to your regular account, and name it whatever you please! I'd stick with `space-station-14` if you just want to help out with development, though. + +### 2.2 Creating your local repository + +Now, we'll need to download our remote repository onto our computer (**cloning**) so we can add ~~20 pairs of clown shoes to every locker~~ some changes to it. You *can* technically change your remote repository (GitHub has some nice tools), but having it on your computer means you use IDEs like Visual Studio or Rider to build the game and run tests, as well as handle Git stuff easily. + +For every step, there will be screenshots and instructions for Git Bash, SmartGit, and TortoiseGit on Windows. + +Navigate to somewhere on your computer where you want to put the local repository, and: + +
    TortoiseGit +

    + +Right click to see TortoiseGit's context menu stuff: + +![](https://i.imgur.com/QGmrQmH.png) + +

    +
    + +
    SmartGit +

    + +Open up SmartGit and navigate to the desired location, then: + +![](https://i.imgur.com/C3JBYR6.png) + +

    +
    + +
    Git Bash +

    + +Right click: + +![](https://i.imgur.com/kIYnm16.png) + +

    +
    + +
    + +Then, we'll enter the command for cloning **our** remote repository--not the `space-wizards/space-station-14` repository. + +
    TortoiseGit +

    + +![](https://i.imgur.com/3HzCnjm.png) +![](https://i.imgur.com/a7vhKcC.png) + + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/YyJm5fx.png) + + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/Xn4AQLf.png) + +Every Git command will look something like this--`git` and then a keyword like `add`, `commit`, `pull`, etc. + +

    +
    + +
    + +After this completes, you have a local repository that you can now modify! There's still some more setup to go through, though. + +### 2.3 Submodule woes + +**Pay attention to this!** If you don't do this, you'll get a lot of weird errors about stuff not being available when you actually try to build the game. + +Space Station 14 has a *lot* of submodules--most notably our engine, RobustToolbox. Submodules are just repositories inside a repository, and they need to be updated manually by you. Or do they? + +We have an automatic submodule updater so you don’t have to worry about running `git submodule update --init --recursive` (the command for manually updating submodules) all the time. + +Run `RUN_THIS.py` inside the repo you downloaded with Python. Preferably from a terminal too (`python RUN_THIS.py` or `python3 RUN_THIS.py`). This should take a few seconds so if it instantly stops you probably aren’t using Python 3.7+ or something. + +If you are on Windows and get redirected to the Microsoft Store or encounter a message in your terminal claiming that Python is not installed when you attempt to run the above command, you will need to disable the Microsoft shortcut that might be causing this issue. You can do this by searching for `Manage App Execution Aliases` in the Windows search and then turning off the two Python references. + +If you do want to modify the engine directly however, or you want to update the submodule manually (the auto updating can be a pain occasionally), make a file called DISABLE_SUBMODULE_AUTOUPDATE inside the BuildChecker/ directory. + +If you ever need to manually update RobustToolbox for whatever reason you can use `cd RobustToolbox; git checkout v0.4.87`(replace `v0.4.87` with the latest RobustToolbox release) then you can use `cd..\` to get back into your SS14 repo. This is also an example of using `cd` to navigate files from the comfort of your command line. + +## 3. Setting up remotes + +When you cloned your remote repository, a **remote** was automatically added to your local repository. **Remotes** are just named URLs to remote repositories that Git keeps track of so you can do stuff like download (pull) new changes to the code or upload (push) code to your forked repository. + +In this case, the remote automatically added is called`origin` and it points to `https://github.com/[username-here]/space-station-14` (or whatever you named the remote repository). + +One issue: we don't have a reference to the original `space-wizards/space-station-14` remote repository anywhere! How are we supposed to update our local repository without it? So let's make sure we've navigated inside our local repo's folder, and we'll add a new remote: + +
    TortoiseGit +

    + +![](https://i.imgur.com/yANaYWI.png) +![](https://i.imgur.com/cjbhMEN.png) + + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/LXCpgVo.png) +![](https://i.imgur.com/ZHIHPJC.png) + + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/00ETpii.png) + +

    +
    + +
    + +All this does is add a new remote named `upstream` that points to the original `space-wizards/space-station-14` repository. Now we can receive updates from the main repository whenever we want! (see below on how to do that). + +The convention is to call the remote pointing to the original repository `upstream` but you can technically call it whatever you like. I'll be referring to it as 'the upstream', though, and it's terminology Git guides use as well. + +## 4. Branching & Commits + +Branches and commits are two of the most important concepts in Git, and most of the work you do will revolve around them. + +### 4.1 Whats a commit? + +Like I mentioned before, **commits** are just packaged up changes to the code. As the developer, you choose which changes go into a commit and when to commit those changes. **Committing** refers to creating a commit, and it essentially makes a save point that you can go back to at any time. + +Commits have an author, timestamp, a message, and some code changes attached to them. They also have a really long 'commit hash', a unique identifier used to refer to different commits. + +Commits are how history is built up--you can actually view the history of every single commit made to the SS14 repository from the beginning, which is pretty cool: + +![](https://i.imgur.com/HQDdw6h.png) + +(done with `git log --reverse`) + +### 4.2 What's a branch? + +**Branches** are very, very important. They're basically just a list of changes to the code (commits). The default branch is 'master', and all of our servers use that branch to compile the code. + +You're pretty much always 'on a branch' when you're working with your code, and you can switch which branch you're working on easily. + +Generally, branches are named for whatever you're going to be working on in them, but it doesn't *really* matter what they're named. + +You can make as many branches as you like. When you create a branch, it 'branches out' (no shit, really?) from the current branch you're on and becomes its own independent thing you can add commits to. + +![](https://i.imgur.com/ByMugxu.png=500x300) + +In this diagram, each little node is a different commit, and each color is a different branch. + +#### Branch merging + +Branches are important because they can be **merged** together. This is how features are integrated into the main `master` branch. A **merge** just means 'take the special commits from this branch, and apply them to another branch'. You can merge any two branches together. + +Sometimes this doesn't go well, because both branches modify the same part in a file in contradictory ways, in which case you'll get a **merge conflict**--more on that in the addendums. + +GitHub pull requests are really a 'merge request'--you're saying that you want to merge the commits on your branch into another branch, usually their `master`. More on that later. + +Pull requests show all this info very well: + +![](https://i.imgur.com/YAOWX5R.png) +![](https://i.imgur.com/nWWy3J4.png) + +In this pull request, Swept started out by creating a new branch. Since he now had a fresh branch free of interference to work with, he started working on the feature and created commits to 'save his progress' whenever he felt it was necessary. These commits were added to the branch sequentially, and you can see the evolution of the branch as more code was written. We'll talk more about pull requests later. + +#### But whyyy? + +Okay, technically, sure, you can just do all of your work on the `master` branch and pull request from there. But, creating different branches makes it easy to understand where you are, how many changes you've made, and it makes it possible to work on multiple features at once. + +Also we'll close your PR if it's from your `master` branch (it can very easily cause issues) so don't do it. + +### 4.3 Making and working with branches + +Making branches is pretty easy. Let's make a new branch called `funny-feature`: + +
    TortoiseGit +

    + +![](https://i.imgur.com/OGkblCk.png) +![](https://i.imgur.com/ZPfzFcm.png) + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/pK1oyfz.png) +![](https://i.imgur.com/5MZ6Ocv.png) + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/kOc9rfe.png) + +You may notice that the bit in parentheses (master) changed to (funny-feature)! Incredible! + +The `-b` in `git checkout` here means 'checkout this branch, and create it if it doesn't exist.' + +

    +
    + +
    + +Now, you can work freely with this branch as you please without fear of messing up your all-important master branch. + +Switching between branches is pretty easy: it's called **checking out** a branch. When you do this, your files and folders locally will be changed to match the branch, so Git will yell at you if you have local changes and you try to check out. + +Checking out a branch: + +
    TortoiseGit +

    + +![](https://i.imgur.com/UThKrCK.png) + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/fzC1pVm.png) + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/DqWEdY5.png) + +

    +
    + +
    + +Then, make whatever local changes you want! It doesn't really matter. Make a new file, delete everything, change one line in a file, etc. It won't affect your `master` branch, because this is`funny-feature` land now! + +### 4.4 Staging and committing changes to your branch + +One more important thing: Before you can `commit` your changes, you have to `add` your changes to the **staging area**. All this means is that you're specifying which files you want to commit. This is helpful, because you *almost never* want to commit submodule changes, so you avoid that by not adding them to the staging area. + +As mentioned before, commits always come with a message, which is just a short, imperative description of what's being done in that commit. Or you can be a chad and name every commit "changes stuff", up to you. + +If you want to see what you've currently changed, and what's in the staging area, it's pretty easy: + +
    TortoiseGit +

    + +![](https://i.imgur.com/xmZKKWJ.png) + +TortoiseGit also shows changed files/folders (a red icon in the bottom right) in the Windows Explorer which is really nice and why I have it installed in the first place. + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/ROsurs1.png) + +This is assuming you installed SmartGit with the option that the main window shows diffs and status. If you didn't, I don't really know where it is. + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/UeMjAHj.png) + +

    +
    + +
    + +Now that you've verified that all of these changes look good, we'll add them to the staging area and commit them (some Git GUIs do this in one step) + +
    TortoiseGit +

    + +![](https://i.imgur.com/ltIASro.png) +![](https://i.imgur.com/BIa9r6c.png) + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/RYUL7u3.png) +![](https://i.imgur.com/Du7HqRV.png) + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/mpKk5L1.png) + +

    +
    + +
    + +Woo, we've committed our changes to a branch! Now that they're committed, they're in the history of the branch forever (sort of). We can do a lot of things now: merge our `funny-feature` into our local `master` branch (if we wanted, for some reason), upload (push) our `funny-feature` branch to our remote repository, or nuke the branch entirely (among other things). We'll opt for pushing the branch and making a pull request now. + +## 5. Pushing and making a PR + +A **pull request** is a GitHub-specific thing. It just means that you want a codebase to merge your changes on one of your branches into one of their branches--usually to their `master` branch. Before we can do this, our remote GitHub repository (origin) needs to know about the beautiful branches and commits we've created locally, so we upload or **push** those changes to the remote. + +### 5.1 Pushing commits + +It's pretty easy to push our changes now that we've committed them. Be aware that, when using these commands, Git is probably going to ask for your GitHub credentials so that it can verify that you're allowed to push to that remote. + +When pushing changes, we specify the *remote* repository that we're pushing to and the *local* branch that we're pushing. Simple enough. + +Pushing our branch to our remote repository (origin): + +
    TortoiseGit +

    + +![](https://i.imgur.com/bWS5Kdk.png) +![](https://i.imgur.com/Irv1e5k.png) + +Selecting 'push all branches' does what it says on the tin. Can be useful. + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/s82VnNn.png) +![](https://i.imgur.com/VP8PuCq.png) + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/7FJqzkL.png) + +

    +
    + +### 5.2 Making a pull request + +Now, the fun part. We'll go to GitHub now and make a pull request for our funny feature. + +![](https://i.imgur.com/YNmEMtG.png) + +Add a description, a nice title, some screenshots, and hopefully it gets merged. + +## 6. Updating our repository + +Maybe it's been a while, a week or two, since your last pull request, and you'd like to make another. Before you do anything, you need to download (**pull**) the code changes from the main SS14 repository into your local repository. If you don't, you'll have out-of-date code and your local changes may not be accurate to how the game will actually run--you might even get **merge conflicts** when you try to PR. + +There are two ways to update your repository. Both methods assume you have the `upstream` remote set up properly--if not, go back to earlier in the guide. + +The first method, **fetch+merge**, gives you more control but can be confusing. The second method, **pulling**, is simple and easy but doesn't give you much control. However, pulling is usually all you need. + +### 6.1 Fetch + merge method + +**Fetching** refers to downloading the new branches and commits from a remote repository--but not doing anything with them just yet (nothing locally will be changed). After we fetch changes from our `upstream` remote (the main SS14 repository), we'll merge them into our local `master` branch. + +When you fetch a remote, it downloads those branches to your local repository and prepends them with the remotes name and a slash. So, when you fetch `upstream`, it'll make a branch called `upstream/master`. + + +First, let's fetch from our `upstream` remote. It'll take a little bit to complete. + +
    TortoiseGit +

    + +![](https://i.imgur.com/3cWun8b.png) +![](https://i.imgur.com/XGgXRY0.png) + +Make sure you select `upstream` and not origin! + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/CNFFJJ8.png) + +I think smartgit fetches from all remotes when you click this????? + +If it doesn't and it just fetches from origin, go to the bottom left and do this: + +![](https://i.imgur.com/8rF0tz5.png) + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/aJvW9PX.png) + +Here nothing happened because I just fetched, but it'll take a while. + +

    +
    + +
    + +Now, we'll merge those changes we just downloaded into our `master` branch. You don't have to merge into master here; you can merge into another branch, too. If you just wanted to 'fast-forward' update one of your branches to make sure your PR is up to date, you can merge into that branch instead. + +Check out the branch you want to merge to. Then, + +
    TortoiseGit +

    + +![](https://i.imgur.com/8lUaEFt.png) +![](https://i.imgur.com/7BvBPYY.png) + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/n8cc2DN.png) +![](https://i.imgur.com/aRSawAo.png) + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/H2L8pOp.png) + +You can also `git merge upstream/master [branch-to-merge-to] + +

    +
    + +### 6.2 Pull method + +**Pulling** refers to **fetching** (downloading) the new branches and commits from a remote repository, and then merging them into a branch. Pulling is often easier because Git has a nice system for automatically figuring out which remote you want to fetch from (but it doesn't always work cleanly). + +Pulling is usually simpler and a lot easier to do. + +We'll **pull** from our `upstream` remote (the main SS14 repo) and tell it to merge into our local `master` branch. + +First, checkout your `master` branch. We covered this earlier. Then, + +
    TortoiseGit +

    + +![](https://i.imgur.com/XMUt6cv.png) +![](https://i.imgur.com/NHVlZ4W.png) + +

    +
    + +
    SmartGit +

    + +![](https://i.imgur.com/ANqpcph.png) +![](https://i.imgur.com/kvv058A.png) +![](https://i.imgur.com/k0scDB8.png) + +

    +
    + +
    Git Bash +

    + +![](https://i.imgur.com/OfHut9Y.png) + +

    +
    + +
    + +If either method went well, you've successfully updated your master branch (or whichever branch you chose to update)! Do this regularly, and always before you start work on a new branch. + +# Addendums + +## 1. Things to keep in mind + +You've more or less learned the workflow for developing features for SS14 Git-wise, but here's some things I'd really like to hammer into your mind: +- When creating a new feature, *always always always* create a new branch off of `master` before committing anything. If you accidentally commit your physics changes to your bike horn branch, you're not in for a fun time, but it is fixable (see Oh Shit, Git?! above) +- **Never, ever commit RobustToolbox or any submodules like Lidgren.Network** unless you know what you're doing. In the top-level local repository, these submodules are considered 'files', so it's easy to accidentally stage and commit them. Do not do this. See below for how to fix your fuckups if it happens. +- If you need further help with Git, feel free to ask in the SS14 Discord in #howdoicode. + +## 2. A quick example workflow + +To get everything in your head and to summarize it all, here's an example workflow for making several pull requests using Git Bash commands. + +```python +git checkout master # Before we create a new branch, we should be on master. +git fetch upstream # We'll fetch any new changes from the SS14 repo.. +git merge upstream/master # ..and merge them into our master branch. + +git checkout -b my-new-feature # Make a new branch for the feature +...local changes later... +git add -A # Add all of our local changes to the staging area +git commit -m "Fix spaghetti explosions" # Commit them +git push origin my-new-feature # and push them to our remote + +# Now, I want to work on a different pull request. + +git checkout master + +# It hasn't been too long, and nothing important was merged, +# so I won't fetch and merge changes again--just a new branch. + +git checkout -b another-feature +...local changes later... +git add -A +git commit -m "Deletes nuclear operatives" + +# I committed, but then I realized my commit was entirely wrong +# and i'll take it up later. + +git revert HEAD +git checkout master + +...a week later... + +# A lot of new stuff was merged, so let's update our branch. + +git fetch upstream +git merge upstream/master master +git checkout another-feature +git merge master + +# Now we'll make changes and push again, this time correctly. + +...local changes later... +git add -A +git commit -m "Adds Highlander gamemode" +git push origin another-feature + +# Made both PRs, both were merged, so we're done here + +git checkout master +git branch -d my-new-feature # Delete both old branches +git branch -d another-feature + +``` + +# Glossary: The Inner Machinations of Git + +Just for reference, here's a little glossary of Git concepts and terms explained in a little more detail, all in one place. + +- **'Branches'** are self-contained versions of the codebase that you can add commits to. The default branch is **master**, but you can make as many as you like. +- **'Repositories'** are essentially just folders where you can use Git to make changes and keep track of changes made. Local repositories are repositories you have on your computer, and remote repositories are repositories that live on websites like [GitHub](https://github.com/space-wizards/space-station-14). Repositories are made up of a lot of branches. +- **'Remotes'** are names for and links to remote repositories that your local repository can use. +- **'Submodules'** are repositories that are located inside another repository. +- **'Forks'** are repositories that are based on another repository. If you're going to make a pull request to the SS14 repo, you need to fork it first. +- **'The working tree'** is just every file and folder and what not that's in the repository. +- **'Staging'** means adding (with `git add`) changes from your working tree into the 'staging area', where some actions can be performed on it +- **'Commits'** are snapshots of the repository's working tree at a given time. Basically a save point. A 'commit' is just a list of files that have been changed from the last commit, and the changes that are 'committed' are the changes that you've 'staged'. +- **'Checking out'** is the act of switching to another branch so you can mess with it or look at its changes locally. +- **'Merging'** is the act of integrating the changes from one branch into another branch. +- **'Merge conflicts'** occur when integrating the changes from one branch into another can't be done automatically because they both change the same area in a file, or their changes are mutually exclusive in some other way. +- **'Fetching'** means getting the branches and commits of a remote repository, but not actually.. doing anything with them yet. You'll just have them updated for if you want to checkout or merge them later. +- **'Pulling'** is the act of integrating changes from a remote repository's branch into your local branch. +- **'Pull requests'** are a GitHub-specific action that allow you to request that your local branch and all of its changes is merged into another repository's branch. +- **'Pushing'** is the act of integrating your local changes into a remote repository. + +There are way more commands and concepts than this, but this is all you *really* need to know for basic development work. + + +# Appendix A: Helpful tips and tricks + +There's some stuff I didn't cover, but you'll almost inevitably have to do at some point. I'll cover these all **exclusively as git commands in Git Bash** quickly, but they're not too hard to figure out in the other programs (same keywords, just look for those). I recommend using their specific guides because I don't know TortoiseGit / SmartGit / GitKraken / Github Desktop well enough to help you with more advanced stuff. + +One note since it comes up a lot here: **`HEAD` is a fancy name for the commit that you're currently on**. Nothing more than that. Branches are also technically fancy names for commits, but you don't need to know that yet. + +A lot of these can be found probably more eloquently in Oh Shit, Git?! (see resources above) + + +## Resolving merge conflicts + +*WIP i'll write a better guide for this later because it's important* + +A nasty little maintainer has told you to 'resolve conflicts' or your PR 'wont be merged'. What an asshole! Thankfully, it's not too hard. + +First, you're going to want to update your local `master branch`. See above for how to do that. + +When you run `git merge master [local branch]`, it'll either do it cleanly (woohoo) or tell you you have to resolve conflicts (wahhhh). + +All you need to do to resolve conflicts manually is go into the files that are conflicting, remove all the `>>>>HEAD` and `===== <<<