diff --git a/src/SUMMARY.md b/src/SUMMARY.md index c4e5666..c36a1f3 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -29,8 +29,15 @@ General Development - [Codebase Info](en/general-development/codebase-info.md) - [Help, I'm So Lost](en/general-development/codebase-info/im-so-lost.md) - [Conventions](en/general-development/codebase-info/conventions.md) + - [General programming](en/general-development/codebase-info/conventions/general.md) + - [Project programming](en/general-development/codebase-info/conventions/project.md) + - [ECS Conventions](en/general-development/codebase-info/conventions/ecs.md) + - [Architecture](en/general-development/codebase-info/conventions/architecture.md) + - [Networking Conventions](en/general-development/codebase-info/conventions/networking.md) + - [Resource Conventions](en/general-development/codebase-info/conventions/resources.md) + - [Module Conventions](en/general-development/codebase-info/conventions/modules.md) - [Pull Request Guidelines](en/general-development/codebase-info/pull-request-guidelines.md) - - [Goobmodules and You](en/general-development/codebase-info/goobmodules-and-you.md) + - [Codebase Organization](en/general-development/codebase-info/codebase-organization.md) - [Feature Proposals](en/general-development/feature-proposals.md) - [Feature Proposal Template](en/templates/proposal.md) - [Expected Team Decorum & Usage](en/general-development/feature-proposals/expected-feature-proposal-decorum.md) diff --git a/src/en/assets/images/general-development/codebase-info/conventions/entityprototypesuffixes1.png b/src/en/assets/images/general-development/codebase-info/conventions/entityprototypesuffixes1.png new file mode 100644 index 0000000..ed04add Binary files /dev/null and b/src/en/assets/images/general-development/codebase-info/conventions/entityprototypesuffixes1.png differ diff --git a/src/en/assets/images/general-development/codebase-info/conventions/entityprototypesuffixes2.png b/src/en/assets/images/general-development/codebase-info/conventions/entityprototypesuffixes2.png new file mode 100644 index 0000000..cf8b26a Binary files /dev/null and b/src/en/assets/images/general-development/codebase-info/conventions/entityprototypesuffixes2.png differ diff --git a/src/en/general-development/codebase-info.md b/src/en/general-development/codebase-info.md index 7a23bad..dcadd30 100644 --- a/src/en/general-development/codebase-info.md +++ b/src/en/general-development/codebase-info.md @@ -1,3 +1,3 @@ -# Codebase Info - +# 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/en/general-development/codebase-info/acronyms-and-nomenclature.md b/src/en/general-development/codebase-info/acronyms-and-nomenclature.md index e31c6c4..e4f3882 100644 --- a/src/en/general-development/codebase-info/acronyms-and-nomenclature.md +++ b/src/en/general-development/codebase-info/acronyms-and-nomenclature.md @@ -1,26 +1,26 @@ -# Acronyms and Nomenclature - -| Shorthand | Meaning | -|:------------------------ |:-------------------------------------------------------------------------------------------------------------- | -| GS14 | Goob Station. The coolest Space Station 14 server. | -| 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](https://docs.spacestation14.com/en/robust-toolbox/ioc.html) | -| .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. | -| 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. | +# Acronyms and Nomenclature + +| Shorthand | Meaning | +|:------------------------ |:-------------------------------------------------------------------------------------------------------------- | +| GS14 | Goob Station. The coolest Space Station 14 server. | +| 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](https://docs.spacestation14.com/en/robust-toolbox/ioc.html) | +| .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. | +| 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/en/general-development/codebase-info/codebase-organization.md b/src/en/general-development/codebase-info/codebase-organization.md new file mode 100644 index 0000000..e372cf0 --- /dev/null +++ b/src/en/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 each other 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/en/general-development/codebase-info/conventions.md b/src/en/general-development/codebase-info/conventions.md index 6018e6a..5979daf 100644 --- a/src/en/general-development/codebase-info/conventions.md +++ b/src/en/general-development/codebase-info/conventions.md @@ -1,591 +1,25 @@ -# 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. - -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. -``` - -## Goob Station - -1. Every new prototype, localisation should be placed within a Goobstation folder (prototypes, assets), and system / code file in a custom goobstation module, for example `Content.Goobstation.Server/Audio/CoolAudioSystem.cs`. It should be just as organised and have just as many subfolders as things outside the Goobstation folder. -2. Code related to a feature should be within its folder. This includes game rules, comps and other miscellaneous things that are using another system. Instead of having GameTicking/Rules/ChangelingRuleSystem.cs - do Changeling/GameTicking/Rules/ChangelingRuleSystem.cs. -3. Every modification to Core module code should have a comment indicating it's a goob station edit, following this general format `// Goobstation - Cool new update`. -4. Modifications to the core module should be a single line call to an [extension method](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods) you have in a custom module. -5. Refactors to an existing Core system to accomodate for modularity must be marked as such via comment, and are an exception to 3 and 4. -6. Namespaces should be declared with the same structure as folders. - -## 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 cursory 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 ProtoId. For example, a data-field for a list of prototype IDs should use something like: -```csharp= -[DataField] -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(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] -public SpriteSpecifier Icon { get; } = SpriteSpecifier.Invalid; -``` - -
- -
- YAML prototype example (click to expand) - -```yml= -# You can specify a specific texture file like this, /Textures/ is optional -- type: MyComponent - icon: /Textures/path/to/my/texture.png - -# /Textures/ is optional and will be automatically inferred, however make sure that you don't start the path with a slash if you don't specify it -- type: MyComponent - icon: 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 guidelines (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 absence 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 `Entity` and `EntityUid` should come first. -The `T?` in `Entity` stands for the component type you need from the entity. -The question mark `?` must be present at the end to mark the component type as nullable. -Next, any arguments you want should come afterwards. - -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(Entity stack, int count) -{ - // This call below will set "Comp" 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(stack, ref stack.Comp)) - 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?'' -``` -- Don't 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 = ... +# Conventions + +There are nearly infinite ways to program the same thing, but some ways will get your PR rejected. + +In this category 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. +``` + +# Navigation + +This is the recommended order to read these conventions for the first time. + +1. [General programming](./conventions/general.md) +2. [Project programming](./conventions/project.md) +3. [ECS Conventions](./conventions/ecs.md) +4. [Architecture](./conventions/architecture.md) +5. [Networking Conventions](./conventions/networking.md) +6. [Resource Conventions](./conventions/resources.md) +7. [Module Conventions](./conventions/modules.md) diff --git a/src/en/general-development/codebase-info/conventions/architecture.md b/src/en/general-development/codebase-info/conventions/architecture.md new file mode 100644 index 0000000..66f74ad --- /dev/null +++ b/src/en/general-development/codebase-info/conventions/architecture.md @@ -0,0 +1,70 @@ +# Architecture Conventions + +## In-simulation or out-of-simulation + +```admonish warning +This convention is *very* poorly enforced by our current codebase. Keep that in mind if you see something that seemingly violates it. +``` + +Broadly, all code in the game should be separated based on whether it is *inside* the "simulation" or *outside* it. The "simulation" is a encompassing term that basically means "the contents of the actual game". + +For example, the following things are "inside" the simulation: +- Basically everything concerning entities: interactions, physics, atmos, etc. +- IC chat +- Round state (lobby, in-game, post-game) + +The following examples are "outside" the simulation: +- OOC chat +- Adminhelp +- Admin votes +- Basically anything talking to an external service, such as the database or a Discord webhook + +We always need locations in the code where these two sides of the codebase exchange data. (For example, a player connecting is initially handled out of simulation, but the simulation needs to be notified of new players to spawn them in somehow.) Exactly how this should be done depends on a case-by-case basis, and it can take effort to do properly, but it is vitally important for code architecture. + +A thought experiment to think about this is "should this logic stop working if the game were to be paused by an admin." If such a pause button were to exist, we would like to completely stop the game logic (no time would progress, nobody could move, etc), but we'd still like people to be able to connect to the server, talk in OOC chat, ask an admin *why* the game is still paused, and so on. + +```admonish info +The game server currently already automatically pauses like this when no players are online, to save resources. This isn't purely theoretical! But perhaps hard to observe at the moment. +``` + +Time in the simulation may accelerate or slow down relative to "real time", depending on server settings or performance issues. On the client, the simulation is constantly committing time travel as part of network prediction. The simulation doesn't actually *exist* on the client until connected to a server! + +Here are some of the differences between how in-simulation and out-of-simulation code should be written: + +| Thing you want to do | in-simulation | out-of-simulation | +|-------------------------------------|-------------------------|--------------------------------------------------------------------------------------------------| +| "Default place" for singleton code. | Make an `EntitySystem` | Use a manager: make a new class, register it with IoC, and call it from `EntryPoint` or similar. | +| Check elapsed time | `IGameTiming.CurTime` | `IGameTiming.RealTime`, `(R)Stopwatch`, `DateTime`, etc. | +| Send custom network messages | Networked entity events | Custom `NetMessage` | + +### 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); +``` + +### 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. + +### 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. diff --git a/src/en/general-development/codebase-info/conventions/ecs.md b/src/en/general-development/codebase-info/conventions/ecs.md new file mode 100644 index 0000000..81b88cc --- /dev/null +++ b/src/en/general-development/codebase-info/conventions/ecs.md @@ -0,0 +1,117 @@ +# Entity-Component-System Conventions + +## 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 ProtoId. For example, a data-field for a list of prototype IDs should use something like: +```csharp +[DataField] +public List> ExampleTypes = new(); +``` + +### 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 absence 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; +``` + +
+ +## 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... + +```admonish info +Ensure events are unsubscribed from when systems are shutdown. Proxy methods like `Subs.CVar()`or `SubscribeLocalEvent` already take care of it, note that you do not need to unsubscribed inside managers, as their lifetime ensures that when they shutdown, the rest of the client / server is also shutting down, making unsubscribing not necessary. +``` + +### 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); +``` diff --git a/src/en/general-development/codebase-info/conventions/general.md b/src/en/general-development/codebase-info/conventions/general.md new file mode 100644 index 0000000..b26a57a --- /dev/null +++ b/src/en/general-development/codebase-info/conventions/general.md @@ -0,0 +1,161 @@ +# General Programming Conventions + +These conventions are not really specific to Space Station 14, and you should be following these no matter what project you are working on. Any experienced programmer should know these by heart. + +## Don't copy paste code + +If you're every looking at another piece of code and think "I want to do the same thing as this": **DO NOT** copy paste it. Make a new function or some other kind of abstraction that allows you to re-use as much code as possible. + +Copy-pasting code is a gigantic maintenance hazard as, in the future, if somebody needs to update the code you copied, they now have to do it in *two* places (and be aware that those *two places* even exist). + +Of course, there are places where you may think you're "copy pasting" unavoidable code. For example, the basic structure for making an `EntitySystem` that does a thing always has a class definition, some dependencies, an `override void Initialize()`, and so on. This kind of "boilerplate" is fine to copy as there's really no way to avoid it. + +## Don't use magic strings/numbers + +These are kind of a subset of "don't copy paste code". A "magic" value is any case in which you have a value in your code, say a string or a number, and it needs to be *that* specific value because it has to be the same as some other value somewhere else. + +The name of the game here is "make sure that if two values have to match, it's practically impossible for them not to be." Be that via compiler error, unit test failure, a guaranteed crash on startup, whatever. + +In the simplest case, such magic values should simply be stored in a `const` or `static readonly` that gets referenced from multiple locations, so the C# compiler enforces they will always be the same. If you need to reference a prototype ID from C#, you should define the prototype ID in a `static readonly ProtoId`, since our validation tooling ensures the IDs in those fields are always valid. + +## 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/). DataFields and Public methods should always be documented. + - Example: + ```csharp + /// + /// Resets the InteractCounter on the . + /// + /// + /// This is a public method other systems can call to interact with FooComponent! + /// Remember that public methods should always use docstring. + /// + [PublicAPI] + public void ResetInteractCounter(Entity ent) + ``` + +### 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 +var 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 ... +var 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 cursory 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): +var 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)) + return; +``` + +## Strings and Identifiers + +Human-readable text should never be used as an identifier or vice versa. In one direction, that means no putting human-readable text (result of localization functions) in a dictionary key, comparing with `==`, etc... In the other direction, that means things like "never show `Enum.ToString()` to a user directly." + +This avoids spaghetti when these inevitably have to be decoupled for various reasons, and avoids inefficiency and bugs from comparing human-readable strings. + +Example: + +```csharp +private void UpdateDisplay(Gender gender) +{ + // This can't be localized! And the capitalization is kinda weird! + // Don't do this! + GenderLabel.Text = gender.ToString(); + + // This is good! + GenderLabel.Text = Loc.GetString($"gender-{gender}"); +} +``` + +### 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: + +```csharp +public string Name +{ + get => _name; + private set => _name = Loc.GetString(value); +} +``` + +## Properly order members in a type + +When laying out the contents of a type, you should **always** put fields above all other instance members. When reading a piece of code, the best way to get familiar with it is to look at the data it operates on. If fields and other members are mixed randomly, it can be much harder to understand the code. + +For this rule, auto-properties (e.g. `string FooBar { get; set; }`) are considered the same as fields, since they have an internal field. Non-auto properties (e.g. `string FooBar => _field.Trim();`) do not, so should not be mixed. + +Bad: + +```csharp +class FooBar +{ + private int _field; + + public void Update() { + _field *= 2; + Counter += 1; + } + + public int Counter { get; set; } +} +``` + +Good: + +```csharp +class FooBar +{ + private int _field; + public int Counter { get; set; } + + public void Update() { + _field *= 2; + Counter += 1; + } + +} +``` diff --git a/src/en/general-development/codebase-info/goobmodules-and-you.md b/src/en/general-development/codebase-info/conventions/modules.md similarity index 94% rename from src/en/general-development/codebase-info/goobmodules-and-you.md rename to src/en/general-development/codebase-info/conventions/modules.md index 756435f..5b05ca6 100644 --- a/src/en/general-development/codebase-info/goobmodules-and-you.md +++ b/src/en/general-development/codebase-info/conventions/modules.md @@ -1,99 +1,103 @@ -# Goobmodules and You - -## What? - -Goobstation provides a way to isolate your code into separate, custom modules. These custom modules retain full functionality like the core modules, while solving the problem of having to recompile the entire game when making changes specific to your downstream. - -## Why? - -As stated above, there are two primary reasons: - -1. **Streamlined Testing and Deployment**: By separating downstream-specific code from the rest of the game, you can compile and test your custom features independently. - -2. **Enhanced Modularity**: Instead of managing files and folders within the already bloated Content.Client/Shared/Server projects, you can organize your work into four distinct, fork-specific projects. - -This approach is specifically designed to accommodate [Robust Modularity](https://github.com/space-wizards/docs/pull/152) should it be implemented in the future. - -## Terminology - -- **Core Module**: Modules belonging to the base space-station-14 repository. These include `Content.Client`, `Content.Server`, `Content.Shared`, and their dependencies like `Content.Shared.Database`. - -- **Custom Module**: Modules that are part of the Client or Server process but don't belong to Core Modules. In Goobstation's case, these are prefixed with `Content.Goobstation.`. - -- **Engine Module**: Modules that are part of RobustToolbox (the game engine). These are prefixed with `Robust.`. - -- **.Common**: A module type intended to be shared between Custom and Core modules. These hold interfaces, components, and other types that both module types need to share. - -- **ModLoader**: The Module Loader system responsible for loading both core and custom modules. - -- **Infix**: The module identifier, which is the middle part of a module name. For example, "Goobstation" in `Content.Goobstation.Common`. - -## How - -The client loader, unless configured otherwise, loads every Assembly from the `Assemblies` folder with a specific prefix. -This prefix can be defined in the `manifest.yml` file and defaults to `Content.`. - -By creating new projects with the `Content.` prefix, you can have the game automatically load them at startup alongside the core modules. - -The dependency structure works as follows: - -``` -Engine Modules - ↑ ↑ ↑ - | | | - | | | -Goobmodules → Core Modules - ↑ | - | | (if same type) - | ↓ - └── .Common Module - -``` - -This means: -- Goobmodules (custom modules) can directly reference core modules. -- Core modules cannot directly reference Goobmodules if they are of the same type (Core.Shared-Custom.Shared), as this would create a circular dependency. -- Client or Server Core modules can reference Custom shared, but this is heavily discouraged. -- If the types are the same, core modules must communicate with Goobmodules through an intermediary `.Common` module. -- The Common module must not depend on either core or custom modules and should be able to build standalone. -- Any part of this system can depend on Engine Modules. - -## Implementation - -### 1. Check Configuration -Review your `manifest.yml` file to check if any prefixes have been set there. - -### 2. Create Your Projects -Create four projects with appropriate naming: -- Prepend them with the correct prefix (default is `Content.`) -- Give them a meaningful name -- Suffix them with the module type (Client, Shared, Server, Common) - -For example: `Content.Goobstation.Client`, `Content.Goobstation.Server`, etc. - -### 3. Set Up Dependencies -Follow the dependency chain described above. You can use Core Modules as dependencies for your new modules, but not vice versa. - -### 4. Create base files -Each module must have an EntryPoint class which inherits from the appropriate type: -- `GameClient` for Client modules -- `GameShared` for Shared or Common modules -- `GameServer` for Server modules - -Each of these base classes provides override methods for each RunLevel (PreInit, Init, PostInit). Refer to the engine documentation for more information about these. - -Additionally, to have autogenerated and networking files working you need to have [global usings](https://github.com/space-wizards/space-station-14/blob/master/Content.Shared/GlobalUsings.cs) and relevant [imports](https://github.com/space-wizards/space-station-14/blob/99484ad7442e8a2b2641437e155986ac2b5e6cb3/Content.Shared/Content.Shared.csproj#L25-L28) -Every module needs to import Robust.Properties. - -### 5. Configure IoC -You'll need to set up Inversion of Control. Refer to the Space Wizards [IoC documentation](https://docs.spacestation14.com/en/robust-toolbox/ioc.html) for detailed information. - -### 6. Configure Build Paths -When building your project: -- Ensure your `Client` custom module is built in the same folder as the Core client module -- Similarly, ensure your `Server` custom module is built in the same folder as the Core server module -- `Shared` and `Common` modules should be built as dependencies of the above, so you don't need to set specific build paths for them - -## Verification - +# Modules Conventions + +```admonish warning "Attention: Placeholder!" +This section is a placeholder, pending an updated guide to be written +``` + +## What? + +Goobstation provides a way to isolate your code into separate, custom modules. These custom modules retain full functionality like the core modules, while solving the problem of having to recompile the entire game when making changes specific to your downstream. + +## Why? + +As stated above, there are two primary reasons: + +1. **Streamlined Testing and Deployment**: By separating downstream-specific code from the rest of the game, you can compile and test your custom features independently. + +2. **Enhanced Modularity**: Instead of managing files and folders within the already bloated Content.Client/Shared/Server projects, you can organize your work into four distinct, fork-specific projects. + +This approach is specifically designed to accommodate [Robust Modularity](https://github.com/space-wizards/docs/pull/152) should it be implemented in the future. + +## Terminology + +- **Core Module**: Modules belonging to the base space-station-14 repository. These include `Content.Client`, `Content.Server`, `Content.Shared`, and their dependencies like `Content.Shared.Database`. + +- **Custom Module**: Modules that are part of the Client or Server process but don't belong to Core Modules. In Goobstation's case, these are prefixed with `Content.Goobstation.`. + +- **Engine Module**: Modules that are part of RobustToolbox (the game engine). These are prefixed with `Robust.`. + +- **.Common**: A module type intended to be shared between Custom and Core modules. These hold interfaces, components, and other types that both module types need to share. + +- **ModLoader**: The Module Loader system responsible for loading both core and custom modules. + +- **Infix**: The module identifier, which is the middle part of a module name. For example, "Goobstation" in `Content.Goobstation.Common`. + +## How + +The client loader, unless configured otherwise, loads every Assembly from the `Assemblies` folder with a specific prefix. +This prefix can be defined in the `manifest.yml` file and defaults to `Content.`. + +By creating new projects with the `Content.` prefix, you can have the game automatically load them at startup alongside the core modules. + +The dependency structure works as follows: + +``` +Engine Modules + ↑ ↑ ↑ + | | | + | | | +Goobmodules → Core Modules + ↑ | + | | (if same type) + | ↓ + └── .Common Module + +``` + +This means: +- Goobmodules (custom modules) can directly reference core modules. +- Core modules cannot directly reference Goobmodules if they are of the same type (Core.Shared-Custom.Shared), as this would create a circular dependency. +- Client or Server Core modules can reference Custom shared, but this is heavily discouraged. +- If the types are the same, core modules must communicate with Goobmodules through an intermediary `.Common` module. +- The Common module must not depend on either core or custom modules and should be able to build standalone. +- Any part of this system can depend on Engine Modules. + +## Implementation + +### 1. Check Configuration +Review your `manifest.yml` file to check if any prefixes have been set there. + +### 2. Create Your Projects +Create four projects with appropriate naming: +- Prepend them with the correct prefix (default is `Content.`) +- Give them a meaningful name +- Suffix them with the module type (Client, Shared, Server, Common) + +For example: `Content.Goobstation.Client`, `Content.Goobstation.Server`, etc. + +### 3. Set Up Dependencies +Follow the dependency chain described above. You can use Core Modules as dependencies for your new modules, but not vice versa. + +### 4. Create base files +Each module must have an EntryPoint class which inherits from the appropriate type: +- `GameClient` for Client modules +- `GameShared` for Shared or Common modules +- `GameServer` for Server modules + +Each of these base classes provides override methods for each RunLevel (PreInit, Init, PostInit). Refer to the engine documentation for more information about these. + +Additionally, to have autogenerated and networking files working you need to have [global usings](https://github.com/space-wizards/space-station-14/blob/master/Content.Shared/GlobalUsings.cs) and relevant [imports](https://github.com/space-wizards/space-station-14/blob/99484ad7442e8a2b2641437e155986ac2b5e6cb3/Content.Shared/Content.Shared.csproj#L25-L28) +Every module needs to import Robust.Properties. + +### 5. Configure IoC +You'll need to set up Inversion of Control. Refer to the Space Wizards [IoC documentation](https://docs.spacestation14.com/en/robust-toolbox/ioc.html) for detailed information. + +### 6. Configure Build Paths +When building your project: +- Ensure your `Client` custom module is built in the same folder as the Core client module +- Similarly, ensure your `Server` custom module is built in the same folder as the Core server module +- `Shared` and `Common` modules should be built as dependencies of the above, so you don't need to set specific build paths for them + +## Verification + If you've done everything correctly, Content.Client or Content.Server should load your custom module on startup, with everything registering and initializing properly. \ No newline at end of file diff --git a/src/en/general-development/codebase-info/conventions/networking.md b/src/en/general-development/codebase-info/conventions/networking.md new file mode 100644 index 0000000..4767c0c --- /dev/null +++ b/src/en/general-development/codebase-info/conventions/networking.md @@ -0,0 +1,100 @@ +# Networking Conventions + +## Field Deltas + +Field deltas allow you to send only specific fields of a component over the network instead of the entire state. This is done by adding `fieldDeltas: true` to your `AutoGenerateComponentState` attribute: + +```csharp +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] +public sealed partial class MyComponent : Component +{ + [DataField, AutoNetworkedField] + public bool IsActive; + + [DataField, AutoNetworkedField] + public int Value; +} +``` + +### When to use field deltas + +Field deltas are great when: +- Your component has fields that change at different rates +- Only a subset of fields typically changes at once +- You have a bunch of networked fields and don't want to send all of them every time + +A good rule of thumb: If you have 3+ fields and they often change independently, consider field deltas. For components with just 1-2 fields, it's usually simpler to skip them. + +### Marking fields as dirty + +When you change a field and want to network just that field, use `DirtyField` instead of `Dirty`: + +```csharp +// Instead of this: +comp.IsActive = true; +Dirty(uid, comp); // Would send ALL networked fields + +// Do this: +comp.IsActive = true; +DirtyField(uid, comp, nameof(MyComponent.IsActive)); // Only sends IsActive +``` + +For a component with many fields where usually only one or two change at a time, field deltas can reduce network traffic by 80-90%. The more fields you have, the more you'll benefit from field deltas. + +Even for components with just 3-4 fields, if they change independently (e.g., one field updates frequently, others rarely), field deltas can still be worth it. + +Field deltas add a little overhead for tracking field changes, but this is usually outweighed by the bandwidth savings. The generator automatically handles most of the implementation complexity. + +## TimeSpans + +### Using TimeSpans + +You should always use `TimeSpan` over `float` for defining static periods of time, such as intervals. Update loops should compare against `CurTime` instead of accumulating `frametime`. + +### Handling paused entities + +When working with `TimeSpan` fields that are modified during runtime (like timers or countdowns), you need to handle entity pausing properly. SS14 provides two important mechanisms for this. + +### AutoGenerateComponentPause and AutoPausedField + +The `[AutoGenerateComponentPause]` and `[AutoPausedField]` attributes work together to automatically adjust `TimeSpan` fields when an entity is unpaused: + +- `[AutoGenerateComponentPause]` is applied to a component class and automatically generates code to handle unpausing. +- `[AutoPausedField]` is applied to individual `TimeSpan` fields within that component that should be adjusted when the entity is unpaused. + +These attributes should **always** be used for `DataField` `TimeSpan` properties that are modified by other systems during runtime, such as timers or cooldowns. + +
+ Example usage (click to expand) + +```csharp +[RegisterComponent, AutoGenerateComponentPause] +public sealed partial class CooldownComponent : Component +{ + [DataField, AutoPausedField] + public TimeSpan CooldownEnd; + + [DataField, AutoPausedField] + public TimeSpan? OptionalTimer; +} +``` +
+ +### TimeOffsetSerializer + +The `TimeOffsetSerializer` is used for serializing `TimeSpan` values that are offset by the current game time. + +- It automatically offsets a `TimeSpan` by the game's current time during serialization/deserialization +- If the entity is paused, it uses the time at which the entity was paused as the reference point +- It prevents unintentional saving of time offsets to maps during mapping (prototypes always serialize as zero) + +Similar to `AutoPausedField`, the `TimeOffsetSerializer` should always be used for runtime-modified `TimeSpan` fields that represent absolute times rather than durations. + +
+ Example usage (click to expand) + +```csharp +[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] +public TimeSpan NextActivationTime; +``` +
diff --git a/src/en/general-development/codebase-info/conventions/project.md b/src/en/general-development/codebase-info/conventions/project.md new file mode 100644 index 0000000..c952811 --- /dev/null +++ b/src/en/general-development/codebase-info/conventions/project.md @@ -0,0 +1,163 @@ +# Project Conventions + +These conventions are specific to Space Station 14. They may talk about code or systems that aren't relevant to other projects, or those other projects may simply have a different opinion about code style. + +## 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. + +## 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: + +```csharp +public void CopyTo(ISerializationManager serializationManager, SortedDictionary source, ref SortedDictionary target, + SerializationHookContext hookCtx, ISerializationContext? context = null) +``` + +Good: + +```csharp +public void CopyTo( + ISerializationManager serializationManager, + SortedDictionary source, + ref SortedDictionary target, + SerializationHookContext hookCtx, + ISerializationContext? context = null) +``` + +## 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. + +### 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 `Entity` and `EntityUid` should come first. +The `T?` in `Entity` stands for the component type you need from the entity. +The question mark `?` must be present at the end to mark the component type as nullable. +Next, any arguments you want should come afterwards. + +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(Entity stack, int count) +{ + // This call below will set "Comp" 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(stack, ref stack.Comp)) + 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. + +## 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 they 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`. diff --git a/src/en/general-development/codebase-info/conventions/resources.md b/src/en/general-development/codebase-info/conventions/resources.md new file mode 100644 index 0000000..dd59bbc --- /dev/null +++ b/src/en/general-development/codebase-info/conventions/resources.md @@ -0,0 +1,201 @@ +# Resource files conventions (Prototypes and other) + +## 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?'' +``` +- Don't specify textures in abstract prototypes/parents. +- You should declare the first prototype block in this order: `type` > `abstract` > `parent` > `id` > `categories` > `name` > `suffix` > `description` > `components.` +- Use inline lists for categories and regular lists for everything else: + ```yaml + - type: entity + parent: [ PartHuman, BaseHead ] # Inline list + id: Headhuman + components: + - type: Tag + tags: # Regular list + - Head + ``` +- 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: + ``` +- The same rule applies for any other list or dictionary, for example: + ```yaml + - type: Tag + tags: + - HighRiskItem # Correct indentation + + - type: Tag + tags: + - HighRiskItem # Wrong indentation + ``` +- 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 + abstract: true # remove this line if not abstract + parent: + id: + name: + 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: +![entityprototypesuffixes1.png](../../../assets/images/general-development/codebase-info/conventions/entityprototypesuffixes1.png) + + +And results in this: +![entityprototypesuffixes2.png](../../../assets/images/general-development/codebase-info/conventions/entityprototypesuffixes2.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 = ... + +## Sounds +When specifying sound data fields, use `SoundSpecifier`. +You should avoid defining sound paths directly and instead use `SoundCollectionSpecifier` whenever possible. + +
+ C# code example (click to expand) + +```csharp +[DataField] +public SoundSpecifier Sound = new SoundCollectionSpecifier("MySoundCollection"); +``` + +
+ +
+ YAML prototype example (click to expand) + +```yaml +# You can define a sound collection like this +- type: soundCollection + id: MySoundCollection + files: + - /Audio/Effects/Cargo/ping.ogg + +# And use it like this +- type: MyComponent + sound: + collection: MySoundCollection +``` + +
+ +## Sprites and Textures +When specifying sprite or texture data fields, use `SpriteSpecifier`. + +
+ C# code example (click to expand) + +```csharp +[DataField] +public SpriteSpecifier Icon = SpriteSpecifier.Invalid; +``` + +
+ +
+ YAML prototype example (click to expand) + +```yaml +# You can specify a specific texture file like this, /Textures/ is optional +- type: MyComponent + icon: /Textures/path/to/my/texture.png + +# /Textures/ is optional and will be automatically inferred, however make sure that you don't start the path with a slash if you don't specify it +- type: MyComponent + icon: 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 guidelines (egyptian brackets, etc). All new JSON files should be indented at 4 spaces. Existing files should be changed to 4 space indent if you are modifying them (fix as you go). You should never be using tab for indent. + +Example: + +```json +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/547852588166c8e091b441e4e67169e156bb09c1", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} + +``` +
diff --git a/src/en/general-development/codebase-info/im-so-lost.md b/src/en/general-development/codebase-info/im-so-lost.md index 17f5af0..9fa48dd 100644 --- a/src/en/general-development/codebase-info/im-so-lost.md +++ b/src/en/general-development/codebase-info/im-so-lost.md @@ -1,24 +1,24 @@ -# I'm Lost. How the hell do I do anything?! How does anyone learn this?! - -Are you wondering how in the world anyone has learned to code for this game? Have you spent ten hours staring at your screen wondering why you even bothered trying? You're not alone. - -### The Problem -Space Station 14 is a very complicated game- as of time of writing (the 18th of January 2025) the entire Goob Station codebase contains (according to [cloc](https://github.com/AlDanial/cloc)) -**12,787,465** lines of readable text files across 18,579 files: -11,025,924 non-comment, non-blank lines of YAML -830,842 non-comment, non-blank lines of C# -452,851 non-comment, non-blank lines of JSON -21,015 non-comment, non-blank lines of Localizations (.ftl files) -10,691 non-comment, non-blank lines of XAML -9,447 non-comment, non-blank lines of XML -And tens of thousands of lines of other filetypes. - -Adding to this is a daunting task because there is absolutely **no way** you are going to read, understand, and remember every single line of code in the codebase. - -But guess what? You don't have to understand the entire codebase. It might feel like it sometimes, but really, it's not that hard coding for this game. There being so much also means there are a **ton** of functions, components, and systems already coded for you to use! The problem is, *you don't know how to find the ones you need and glue them together*. - -### The Solution -The solution to this problem is simple, just look at things similar enough to what you're doing. Trying to add a shader overlay? **Copy paste another shader overlay and work from there**. Trying to add support for custom borg names? **Look at a PR adding some character creation Database field, or just look at how existing character creation stuff works!** Searching [wizden PRs](https://github.com/space-wizards/space-station-14/pulls) or [goob PRs](https://github.com/Goob-Station/Goob-Station/pulls) or other codebase PRs are probably the best way to find something similar to what you're doing (or maybe someone's already done it, lol). Having good knowledge on the game is super helpful with this as you can just go "oh well this has x functionality I can copy that" and just take from all around. - -### TLDR -Coding from this game is copy pasting just as much as coding in general is :P. Copy and use existing stuff in the game people have already coded for you. I literally copy my own previous code these days. +# I'm Lost. How the hell do I do anything?! How does anyone learn this?! + +Are you wondering how in the world anyone has learned to code for this game? Have you spent ten hours staring at your screen wondering why you even bothered trying? You're not alone. + +### The Problem +Space Station 14 is a very complicated game- as of time of writing (the 18th of January 2025) the entire Goob Station codebase contains (according to [cloc](https://github.com/AlDanial/cloc)) +**12,787,465** lines of readable text files across 18,579 files: +11,025,924 non-comment, non-blank lines of YAML +830,842 non-comment, non-blank lines of C# +452,851 non-comment, non-blank lines of JSON +21,015 non-comment, non-blank lines of Localizations (.ftl files) +10,691 non-comment, non-blank lines of XAML +9,447 non-comment, non-blank lines of XML +And tens of thousands of lines of other filetypes. + +Adding to this is a daunting task because there is absolutely **no way** you are going to read, understand, and remember every single line of code in the codebase. + +But guess what? You don't have to understand the entire codebase. It might feel like it sometimes, but really, it's not that hard coding for this game. There being so much also means there are a **ton** of functions, components, and systems already coded for you to use! The problem is, *you don't know how to find the ones you need and glue them together*. + +### The Solution +The solution to this problem is simple, just look at things similar enough to what you're doing. Trying to add a shader overlay? **Copy paste another shader overlay and work from there**. Trying to add support for custom borg names? **Look at a PR adding some character creation Database field, or just look at how existing character creation stuff works!** Searching [wizden PRs](https://github.com/space-wizards/space-station-14/pulls) or [goob PRs](https://github.com/Goob-Station/Goob-Station/pulls) or other codebase PRs are probably the best way to find something similar to what you're doing (or maybe someone's already done it, lol). Having good knowledge on the game is super helpful with this as you can just go "oh well this has x functionality I can copy that" and just take from all around. + +### TLDR +Coding from this game is copy pasting just as much as coding in general is :P. Copy and use existing stuff in the game people have already coded for you. I literally copy my own previous code these days. diff --git a/src/en/general-development/codebase-info/pull-request-guidelines.md b/src/en/general-development/codebase-info/pull-request-guidelines.md index 6d5f2bc..9573405 100644 --- a/src/en/general-development/codebase-info/pull-request-guidelines.md +++ b/src/en/general-development/codebase-info/pull-request-guidelines.md @@ -1,172 +1,172 @@ -```admonish warning -This page is under construction and is just a copy-paste of wizden's doc right now. Just read theirs and get requested changes for missing all the goob-specific stuff :P. -``` - -# Pull Request Guidelines - -Thank you for contributing to Space Station 14. When submitting pull requests (PRs), please follow these guidelines to make your pull requests easier to review and merge. Pull requests that do not follow these guidelines may be closed at a maintainer's discretion. - -## Before You Begin - -- If you're unfamiliar with the Git workflow, please [howdoicode](../setup/howdoicode.md) and ask as many questions as you need in our [Development Discord](https://discord.gg/zXk2cyhzPN). - -- 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. - -- 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. - -## Content - -- **Make separate PRs for feature changes, bug fixes, and cleanup/refactors.** This makes changes easier to review, reduces conflicts, and also easier to revert if something goes wrong. - - - Feature changes and bug fixes should be in their own PR. - - Cleanups and "refactoring", including variable renaming and indentation changes (for example, due to file-scoped namespacing) must be in their own PR. - - **Refactors must be in a separate PR.** This includes changes that impact a significant number of public APIs (fields, methods, etc.) that require changes across multiple systems. These must be made in a separate PR from any content changes or bug fixes. - - 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. - - Mapping changes should be given a separate PR for each map changed. This includes even minor changes. - -- **Do not make multiple unrelated changes in one PR.** For example, do not make miscellaneous additional changes to a PR, e.g. changing the heat resistance of a pair of gloves alongside your PR adding a new gun. - - - 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. - -## Testing - -- **Test all of your changes in-game.** All bug fixes and features must be tested in-game. You should also test other features that may be indirectly impacted by your changes. - - - For the above reason, **do not use the GitHub web editor to make PRs.** Web edits are liable to be closed at a maintainer's discretion. Repeated submission of PRs made through the web editor may result in a repository ban. - -- **Provide screenshots or videos** that demonstrate testing done. This also makes it easier to write progress reports. - -## Before Submitting - -- **Review your diff** using the code preview tab on GitHub. - - - Check for changes that you did not intend to commit. - - Check for accidental whitespace additions or line end changes. - -## After Submitting - -You are free to make changes to your PR after submitting, for example, if you make improvements or fix bugs that you discover after submitting. - -- **Do not force push to your branch** after receiving a review unless a maintainer requests it. Doing so makes all reviews show as 'outdated', even if they have not been addressed yet. - -# Reviews - -Reviews are an important part of the pull request process. Reviews help us obtain feedback from the community and maintain a high quality of code in the codebase. Since maintainers are volunteers, we ask for your patience. The review process for large changes can take up to several weeks. - -## Getting Reviews - -- 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 workload 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. - -- Maintainers periodically review open PRs. - -- If it is taking several days to get an initial review, it is appropriate to ask for a review in #pr-review-request. - -## Addressing Reviews - -- 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 from the reviewer or in #howdoicode. - -# 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 entries 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. - -#### Admin changelog - -Putting `ADMIN:` in the changelog will place all changelogs below it into the admin changelog category instead of the main changelog category. - -Note: The category is case-insensitive, but if it is not alphabetical ending with a colon, it will fail parsing the category and will fall back to placing the changelogs in the previous category or the main changelog category. - -``` -:cl: -ADMIN: -- add: Added fun! -- remove: Removed fun! -- tweak: Changed fun! -- fix: Fixed fun! -``` -or -``` -:cl: -- add: Added fun! -- remove: Removed fun! -- tweak: Changed fun! -- fix: Fixed fun! -ADMIN: -- add: Added fun! -- remove: Removed fun! -- tweak: Changed fun! -- fix: Fixed fun! -``` - -## 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, grammatically-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 appearances 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: "Updated Security on Meta Station." Mapping changes often fill the changelog and shouldn't be included. - -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 and avoid wordy, "IC" changes.** Players should be able to understand the gist of the changes by skimming the Changelog. "IC" changes are harder for players to read and understand what the change was. Make changes concise and to the point. 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." - - - Not so good: "Due to budget cuts, detective's revolver has been replaced with something more appropriate." What is more appropriate? - - Good: "The detective's revolver now contains cap bullets instead of lethals." - - - Not so good: "The Syndicate has changed around their stock prices and gotten rid of some old dusty headsets" It is unclear what changed and what this has to do with "stock prices" and "dusty old headsets". - - Good: "The syndicate headset has been removed from the uplink." Clearly explains what was changed. - - - Not so good: "Due to Nanotrashen's budget cuts, Space pens are no longer supplied on the station." - - Good: "Space pens are no longer available." - -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." *creampied* has another, unfortunate meaning that undermines the professional tone of a Changelog entry. +```admonish warning +This page is under construction and is just a copy-paste of wizden's doc right now. Just read theirs and get requested changes for missing all the goob-specific stuff :P. +``` + +# Pull Request Guidelines + +Thank you for contributing to Space Station 14. When submitting pull requests (PRs), please follow these guidelines to make your pull requests easier to review and merge. Pull requests that do not follow these guidelines may be closed at a maintainer's discretion. + +## Before You Begin + +- If you're unfamiliar with the Git workflow, please [howdoicode](../setup/howdoicode.md) and ask as many questions as you need in our [Development Discord](https://discord.gg/zXk2cyhzPN). + +- 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. + +- 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. + +## Content + +- **Make separate PRs for feature changes, bug fixes, and cleanup/refactors.** This makes changes easier to review, reduces conflicts, and also easier to revert if something goes wrong. + + - Feature changes and bug fixes should be in their own PR. + - Cleanups and "refactoring", including variable renaming and indentation changes (for example, due to file-scoped namespacing) must be in their own PR. + - **Refactors must be in a separate PR.** This includes changes that impact a significant number of public APIs (fields, methods, etc.) that require changes across multiple systems. These must be made in a separate PR from any content changes or bug fixes. + - 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. + - Mapping changes should be given a separate PR for each map changed. This includes even minor changes. + +- **Do not make multiple unrelated changes in one PR.** For example, do not make miscellaneous additional changes to a PR, e.g. changing the heat resistance of a pair of gloves alongside your PR adding a new gun. + + - 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. + +## Testing + +- **Test all of your changes in-game.** All bug fixes and features must be tested in-game. You should also test other features that may be indirectly impacted by your changes. + + - For the above reason, **do not use the GitHub web editor to make PRs.** Web edits are liable to be closed at a maintainer's discretion. Repeated submission of PRs made through the web editor may result in a repository ban. + +- **Provide screenshots or videos** that demonstrate testing done. This also makes it easier to write progress reports. + +## Before Submitting + +- **Review your diff** using the code preview tab on GitHub. + + - Check for changes that you did not intend to commit. + - Check for accidental whitespace additions or line end changes. + +## After Submitting + +You are free to make changes to your PR after submitting, for example, if you make improvements or fix bugs that you discover after submitting. + +- **Do not force push to your branch** after receiving a review unless a maintainer requests it. Doing so makes all reviews show as 'outdated', even if they have not been addressed yet. + +# Reviews + +Reviews are an important part of the pull request process. Reviews help us obtain feedback from the community and maintain a high quality of code in the codebase. Since maintainers are volunteers, we ask for your patience. The review process for large changes can take up to several weeks. + +## Getting Reviews + +- 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 workload 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. + +- Maintainers periodically review open PRs. + +- If it is taking several days to get an initial review, it is appropriate to ask for a review in #pr-review-request. + +## Addressing Reviews + +- 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 from the reviewer or in #howdoicode. + +# 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 entries 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. + +#### Admin changelog + +Putting `ADMIN:` in the changelog will place all changelogs below it into the admin changelog category instead of the main changelog category. + +Note: The category is case-insensitive, but if it is not alphabetical ending with a colon, it will fail parsing the category and will fall back to placing the changelogs in the previous category or the main changelog category. + +``` +:cl: +ADMIN: +- add: Added fun! +- remove: Removed fun! +- tweak: Changed fun! +- fix: Fixed fun! +``` +or +``` +:cl: +- add: Added fun! +- remove: Removed fun! +- tweak: Changed fun! +- fix: Fixed fun! +ADMIN: +- add: Added fun! +- remove: Removed fun! +- tweak: Changed fun! +- fix: Fixed fun! +``` + +## 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, grammatically-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 appearances 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: "Updated Security on Meta Station." Mapping changes often fill the changelog and shouldn't be included. + +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 and avoid wordy, "IC" changes.** Players should be able to understand the gist of the changes by skimming the Changelog. "IC" changes are harder for players to read and understand what the change was. Make changes concise and to the point. 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." + + - Not so good: "Due to budget cuts, detective's revolver has been replaced with something more appropriate." What is more appropriate? + - Good: "The detective's revolver now contains cap bullets instead of lethals." + + - Not so good: "The Syndicate has changed around their stock prices and gotten rid of some old dusty headsets" It is unclear what changed and what this has to do with "stock prices" and "dusty old headsets". + - Good: "The syndicate headset has been removed from the uplink." Clearly explains what was changed. + + - Not so good: "Due to Nanotrashen's budget cuts, Space pens are no longer supplied on the station." + - Good: "Space pens are no longer available." + +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." *creampied* has another, unfortunate meaning that undermines the professional tone of a Changelog entry. diff --git a/src/en/general-development/contributing-translations.md b/src/en/general-development/contributing-translations.md index 00b6485..8911e62 100644 --- a/src/en/general-development/contributing-translations.md +++ b/src/en/general-development/contributing-translations.md @@ -1,7 +1,7 @@ -# Contributing Translations - -```admonish info -The main game **cannot** be translated for now. While the game itself does support localization as well, we currently do not want to accept translations as they cannot actually be used by players on official servers. -``` - +# Contributing Translations + +```admonish info +The main game **cannot** be translated for now. While the game itself does support localization as well, we currently do not want to accept translations as they cannot actually be used by players on official servers. +``` + we don't accept translations at this time. \ No newline at end of file diff --git a/src/en/general-development/feature-proposals.md b/src/en/general-development/feature-proposals.md index 04a9cf8..30b501b 100644 --- a/src/en/general-development/feature-proposals.md +++ b/src/en/general-development/feature-proposals.md @@ -1,52 +1,52 @@ -# 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? - -1. Make a copy of the design proposal template located at `src/en/templates/proposal.md`. - -3. Read through [SS14's Core Design Documentation](../space-station-14/core-design.md) (for gameplay-related proposals). - -4. Write your proposal (see [guide to editing docs](../meta/guide-to-editing-docs.md)). - -5. When you are ready for your proposal to be reviewed, make a pull request. - -6. Your proposal is approved when a maintainer merges it. This is a green light for you or someone else to go ahead and implement it. A maintainer will then link your proposal to the side bar. *Note to maintainers: edit `src/SUMMARY.md`* - -``` 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. GS14 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 entirety 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 entirety 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 [GS14 Core Design Documentation](/src/en/space-station-14/design.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). -``` +# 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? + +1. Make a copy of the design proposal template located at `src/en/templates/proposal.md`. + +3. Read through [SS14's Core Design Documentation](../space-station-14/core-design.md) (for gameplay-related proposals). + +4. Write your proposal (see [guide to editing docs](../meta/guide-to-editing-docs.md)). + +5. When you are ready for your proposal to be reviewed, make a pull request. + +6. Your proposal is approved when a maintainer merges it. This is a green light for you or someone else to go ahead and implement it. A maintainer will then link your proposal to the side bar. *Note to maintainers: edit `src/SUMMARY.md`* + +``` 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. GS14 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 entirety 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 entirety 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 [GS14 Core Design Documentation](/src/en/space-station-14/design.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/en/general-development/feature-proposals/expected-feature-proposal-decorum.md b/src/en/general-development/feature-proposals/expected-feature-proposal-decorum.md index 098bdd0..d155afd 100644 --- a/src/en/general-development/feature-proposals/expected-feature-proposal-decorum.md +++ b/src/en/general-development/feature-proposals/expected-feature-proposal-decorum.md @@ -1,14 +1,14 @@ -# 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 GS14. - -Any documents proposed: - -- Should conform to the basic [Core Game Design](../../space-station-14/core-design.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 GS14 to others that may be interested in how the game is designed. Take note of that. +# 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 GS14. + +Any documents proposed: + +- Should conform to the basic [Core Game Design](../../space-station-14/core-design.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 GS14 to others that may be interested in how the game is designed. Take note of that. diff --git a/src/en/general-development/setup.md b/src/en/general-development/setup.md index ac50b92..65faeda 100644 --- a/src/en/general-development/setup.md +++ b/src/en/general-development/setup.md @@ -1,3 +1,3 @@ -# Setup - +# 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/en/general-development/setup/howdoicode.md b/src/en/general-development/setup/howdoicode.md index 2fdef8c..7e0fd1e 100644 --- a/src/en/general-development/setup/howdoicode.md +++ b/src/en/general-development/setup/howdoicode.md @@ -1,14 +1,14 @@ -# How do I code? (I don't know how to program.) -Below are some useful links for learning programming. If you get stuck, don't be afraid to ask us questions in our [development discord](https://discord.gg/zXk2cyhzPN). If you can't find what you're looking for in one of these links, try googling "how to do X in csharp". You'll usually find several examples. - -If you have some experience (read: understand things like "classes" and "control flow") and would rather learn by doing, try looking at the issues on [the Github](https://github.com/Goob-Station/Goob-Station/issues) on the Github repo. They are usually pretty simple and someone can always help you out on the Discord if you're lost. - -You may have come here simply wanting to add items. It's possible to do that without learning how to program, though you'll be limited in what kind of functionality you can give them. You will still need to learn git to add items however! Really, if you are doing *anything* in Goob Station other than maybe editing docs or wikis, you should absolutely learn git. With that out of the way, here is the quick list of links you need to learn how to add items: [learn git](https://docs.spacestation14.com/en/general-development/setup/howdoicode.html#git), [set up a development environment](https://docs.goobstation.com/en/general-development/setup/setting-up-a-development-environment.html), and then follow the classic [Bike Horn tutorial](https://docs.spacestation14.com/en/ss14-by-example/adding-a-simple-bikehorn.html). - -### Learning Resources - -**[CS50 | Harvard](https://cs50.harvard.edu/)** -Teaches computer programming and how computers work. Covers a broad swathe of topics (different languages like C and Python, algorithms, memory, etc). To oversimplify: learning things like "for loops" and "if statements" teaches you how to talk the talk. Breaking down why algorithms work, the differences between programming languages, actually writing programs to solve puzzles, etc. is the "walk the walk" part. You can get started without this and pick it up naturally over time, but it's a slower process. I highly recommend - -**[Wizden Docs | Wizard's Den](https://docs.spacestation14.com/en/general-development/setup/howdoicode.html)** -Provides a great list of other resources you can use to learn C#, git, programming in general, and general SS14 development (including YAML, setting up a development environment, et cetera). Do note however there are a few minor things Goob devs will disagree on, or that you need to know in addition to wizden's teachings (obviously, why else does this wiki exist?!) and if you want to develop for Goob (why else are you here) you should note them. So I recommend you don't just open up Wizden's docs and *completely* abandon Goob's forever if you want to contribute to Goob, or a fork of Goob. +# How do I code? (I don't know how to program.) +Below are some useful links for learning programming. If you get stuck, don't be afraid to ask us questions in our [development discord](https://discord.gg/zXk2cyhzPN). If you can't find what you're looking for in one of these links, try googling "how to do X in csharp". You'll usually find several examples. + +If you have some experience (read: understand things like "classes" and "control flow") and would rather learn by doing, try looking at the issues on [the Github](https://github.com/Goob-Station/Goob-Station/issues) on the Github repo. They are usually pretty simple and someone can always help you out on the Discord if you're lost. + +You may have come here simply wanting to add items. It's possible to do that without learning how to program, though you'll be limited in what kind of functionality you can give them. You will still need to learn git to add items however! Really, if you are doing *anything* in Goob Station other than maybe editing docs or wikis, you should absolutely learn git. With that out of the way, here is the quick list of links you need to learn how to add items: [learn git](https://docs.spacestation14.com/en/general-development/setup/howdoicode.html#git), [set up a development environment](https://docs.goobstation.com/en/general-development/setup/setting-up-a-development-environment.html), and then follow the classic [Bike Horn tutorial](https://docs.spacestation14.com/en/ss14-by-example/adding-a-simple-bikehorn.html). + +### Learning Resources + +**[CS50 | Harvard](https://cs50.harvard.edu/)** +Teaches computer programming and how computers work. Covers a broad swathe of topics (different languages like C and Python, algorithms, memory, etc). To oversimplify: learning things like "for loops" and "if statements" teaches you how to talk the talk. Breaking down why algorithms work, the differences between programming languages, actually writing programs to solve puzzles, etc. is the "walk the walk" part. You can get started without this and pick it up naturally over time, but it's a slower process. I highly recommend + +**[Wizden Docs | Wizard's Den](https://docs.spacestation14.com/en/general-development/setup/howdoicode.html)** +Provides a great list of other resources you can use to learn C#, git, programming in general, and general SS14 development (including YAML, setting up a development environment, et cetera). Do note however there are a few minor things Goob devs will disagree on, or that you need to know in addition to wizden's teachings (obviously, why else does this wiki exist?!) and if you want to develop for Goob (why else are you here) you should note them. So I recommend you don't just open up Wizden's docs and *completely* abandon Goob's forever if you want to contribute to Goob, or a fork of Goob. diff --git a/src/en/general-development/setup/server-hosting-tutorial.md b/src/en/general-development/setup/server-hosting-tutorial.md index 9d29ba9..1b6ab4b 100644 --- a/src/en/general-development/setup/server-hosting-tutorial.md +++ b/src/en/general-development/setup/server-hosting-tutorial.md @@ -1,3 +1,3 @@ -# Server Hosting Tutorial - -Please simply reference [Wizden's Docs](https://docs.spacestation14.com/en/general-development/setup/server-hosting-tutorial.html) on hosting a server and just download the build from [https://cdn.goobstation.com/fork/GoobLRP](https://cdn.goobstation.com/fork/GoobLRP) for LRP or [https://cdn.goobstation.com/fork/GoobMRP](https://cdn.goobstation.com/fork/GoobMRP) for MRP instead of downloading a wizden build. +# Server Hosting Tutorial + +Please simply reference [Wizden's Docs](https://docs.spacestation14.com/en/general-development/setup/server-hosting-tutorial.html) on hosting a server and just download the build from [https://cdn.goobstation.com/fork/GoobLRP](https://cdn.goobstation.com/fork/GoobLRP) for LRP or [https://cdn.goobstation.com/fork/GoobMRP](https://cdn.goobstation.com/fork/GoobMRP) for MRP instead of downloading a wizden build. diff --git a/src/en/general-development/setup/setting-up-a-development-environment.md b/src/en/general-development/setup/setting-up-a-development-environment.md index 8d50310..199b6e8 100644 --- a/src/en/general-development/setup/setting-up-a-development-environment.md +++ b/src/en/general-development/setup/setting-up-a-development-environment.md @@ -1,224 +1,224 @@ -```admonish warning -This page is under construction and a lot of the information here is out of date or incorrect. Just read wizden's development environment and replace the repo you're using with Goob's. Literally the only reason I haven't deleted this page is because people literally just want to copy past commands without using 1 brain cell. -``` - -# Setting up a Development Environment - -First you're gonna need some software: - -* [Git](https://git-scm.com/) or one of the [many](https://www.sourcetreeapp.com/) [third-party](http://www.syntevo.com/smartgit/) [UIs](https://tortoisegit.org/) that make it easier to use. Make sure to let it install to your PATH like [this](../../assets/images/setup/git-path.png). -* [Python 3.7 or higher](https://www.python.org/). Make sure to install it into your [PATH on Windows](../../assets/images/setup/python-path.png). Also make sure the 'py launcher' option is enabled when installing on Windows. You should get python from [python.org](https://www.python.org/). Versions installed from the windows store sometimes cause build issues. -* [.NET 9.0 SDK](https://dotnet.microsoft.com/download/dotnet/9.0). Visual Studio also installs this if you're on Windows. - * ARM (M1) Mac users: You need to make sure to install x64 .NET, **not** ARM .NET. The engine does not currently run natively on Mac ARM so using x64 via Rosetta 2 emulation is recommended. -* Preferably an IDE to make development not painful (all free options unless otherwise noted): - * For **Windows**, [Visual Studio 2022 **Community**](https://www.visualstudio.com/). For a minimal install (Jesus it's large) you're gonna want the .NET desktop development workload, the C# compiler, C# support, NuGet package manager, MSBuild and .NET 9 SDK or something along those lines. - * For **macOS**, [Visual Studio for Mac](https://docs.microsoft.com/en-us/visualstudio/mac/). - * For **all platforms**, (NOT FREE) [Rider](https://www.jetbrains.com/rider/) is an IDE preferred by some mostly wizden developers. College/University students can get a free education license, even if they're not a computer science major. - * For **all platforms**, [Visual Studio Code](https://code.visualstudio.com/) with the C# extension. Usually an inferior IDE experience than full blown IDEs like regular Visual Studio, but some experienced programmers enjoy the minimalism. - * **Exclusive to VSCode/VSCodium**: you can install our community made [Robust YAML](https://marketplace.visualstudio.com/items?itemName=slava0135.robust-yaml) extension for better Robust Toolbox YAML experience on top of [YAML Language Support](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) extension. - * For **all platforms**, [VSCodium](https://vscodium.com/) with the C# extension. Open source and without the bloat and tracking of VSCode. - -## 1. Cloning - -**Even if you already know Git, scroll down to read the section about submodule setup. Seriously.** - -If you're **familiar with Git**, just fork and clone the repository, set up remotes, and then follow the submodule guide below. - -If you're **unfamiliar with Git**, or just don't know how to proceed, read the [howdoicode](../setup/howdoicode.md) guide, which goes in depth on how to contribute to the game and how to set up your initial repository. It also touches on submodule setup, but that's included here as well because of its importance. - -## 2. Submodule Setup - -We have an automatic submodule updater so you don't have to worry about running `git submodule update --init --recursive` all the time. - -Run `RUN_THIS.py` inside the repo you downloaded with Python. Preferably from a terminal too. This should take a few seconds so if it instantly stops then check if you are running Python 3.7+ otherwise keep reading. - -**If running `RUN_THIS.py` immediately opens and closes a window: do not worry.** This does not mean that it failed. The script closes automatically upon completion, so if you want to verify that it worked properly, check the submodule `/RobustToolbox/` and verify that all the files are there. If not try checking out the troubleshooting at the bottom of this page. - -Note: If you have any issues when getting started with missing files it's recommended you run `git submodule update --init --recursive` by hand once in case something went wrong with python. - -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), make a file called `DISABLE_SUBMODULE_AUTOUPDATE` inside the `BuildChecker/` directory. - -And with that, your repo is now properly setup! - -## 3. Setup an IDE - -### Visual Studio - -1. Download Visual Studio Community (if you don't own a paid version) from [here](https://visualstudio.microsoft.com/vs/community/) -2. Run the installer and choose `.net desktop development`, then install -3. If the installer asks you for a development environment select `Visual C#`. -4. Open Visual Studio -5. Select `Open a project or solution`, then navigate to your cloned repository from above and open `SpaceStation14.sln` - -### JetBrains Rider -1. Install Rider, we suggest using [Jetbrains Toolbox](https://www.jetbrains.com/toolbox-app/) so it can also automaticly update in the future. -2. Go through the setup. -3. Press "Open" and select `SpaceStation14.sln` -4. If you plan to do engine development you must add Robust Toolbox to the Directory Mappings so that Riders VCS can detect changes to Robust. - Open Riders settings and go to the Version Control section > Directory Mappings and press the plus (+) button. For Directory point it to the `RobustToolbox` folder in the project and Git as the VCS - -### VSCodium -1. Download [VSCodium Here](https://vscodium.com/) or more directly [on Github Here](https://github.com/VSCodium/vscodium/releases) (On the latest release, click the assets dropdown then scroll to the ZIP or .exe for your OS). -2. Run the installer or extract the zip file to a location of your choice and run the .exe once extracted. -3. Once installed, navigate to the Extensions tab (part way down on the top left corner bar, looks like 4 tiles) and search for "C#". An extension by "Muhammad-Sammy" with over 70K downloads and a green / white logo is the one, install that. Extension ID `muhammad-sammy.csharp`. -4. Select File > Open Folder, then navigate to your cloned repository from above and open this full folder. -5. When asked to open a solution, select `SpaceStation14.sln`. Alternatively, set `dotnet.defaultSolution` setting to `SpaceStation14.sln` in your workspace settings. -6. Now you can run and debug your game. Select the icon above "Extensions" from earlier for "Run and Debug" and from the dropdown next to the green play button you can select "Server/Client". This will run both the client and server, opening the game for you to debug. Relevant information will pop up in the debug along the bottom. Select the processes in the call stack on the left to change what you are debugging. - -## 4. Starting SS14 - -Now you can get on to compiling the client and server! Use your flavor of IDE to open the solution file `SpaceStation14.sln` and press the build button. - -To compile without an IDE, run `dotnet build` in the Goob Station repo directory. Then, call the following commands to run the client and server. -* `dotnet run --project Content.Server` -* `dotnet run --project Content.Client` - -Both these commands use a debug configuration by default. To enable release optimizations, add `--configuration Release` to the dotnet invocation. - -Note: If you're having problems with dotnet not finding libssl (e.g. when using libressl), try setting the `CLR_OPENSSL_VERSION_OVERRIDE` environment variable to the appropriate version. For instance, set it to `48` if your `/usr/lib` contains `libssl.so.48`. -If that doesn't work you can also try running `ln -s /usr/lib/libssl.so /usr/local/lib/libssl.so.1.0.0` instead. - -## 5. Configuring Build Options - -The GS14 client and server are independent projects, but both can launch with a single button somewhere in your IDE. This needs to be set up, however. Note: **It is recommended that you run `Content.Client` and `Content.Server` when developing from your IDE.** *Not* `Robust.Client` or `Robust.Server`. The reason is that running `Content.*` will make your IDE aware of dependencies correctly and ensure everything is rebuilt nicely. If you run `Robust.Client` directly you have to make sure the solution is fully built every time which is annoying and easy to forget. - -### Visual Studio 2022 - -In Visual Studio 2022, you can configure the build button to run both the server and client by right clicking the solution, then selecting `Configure StartUp Projects...`. Once the menu pops up, then select `Multiple startup projects:` and set the action for `Content.Client` and `Content.Server` to `Start`. Once you apply the changes, hitting the big `Start` button with a green arrow next to it should launch both client and server at the same time. - -Note: If you're having problems with the program not getting built right, you may need to set always build before run. Go to Options `Projects and Solutions/Build and Run` and change `On Run, when projects are out of date` to `Always build`. - -In VS you can also use the keys F7 to build the project and F5 to run it. - -### Visual Studio Code - -The C# extension provides a `"coreclr"` launch type which can be used to run the `Content.Server` and `Content.Client` executables in their respective `bin/` directories. A [compound launch configuration](https://code.visualstudio.com/Docs/editor/debugging#_compound-launch-configurations) can be used to run the server and client at the same time. - -### Command Line - -Build with `dotnet build` and run the client and server on different command lines with: - -* `dotnet run --project Content.Server` -* `dotnet run --project Content.Client` - -There's also definitely some way to run two commands at the same time, but you should probably google it. - -### JetBrains Rider - -In Rider you can create a "compound configuration" to run or debug both client and server at the same time. Quite convenient! - -![](../../assets/images/setup-rider-configurations.png) - -## 6. Configuring IDE directories - -C# IDEs like Visual Studio and Rider do not automatically show the `Resources` folder in the project. This folder contains all non-C# files such as sprites, audio, and most importantly, YAML prototypes. These instructions will explain how to get this folder to show up in your IDE, so you can easily work with it. - -### Visual Studio 2022 - -In Visual Studio, you can switch the **Solution Explorer** from "solution" view (only showing the C# projects) to "folder" view (showing all the files in the project). Press the button to switch views as follows, then select the folder view: - -![](../../assets/images/setup/vs-solution-explorer-switch-view-1.png) -![](../../assets/images/setup/vs-solution-explorer-switch-view-2.png) - -After this, the Solution Explorer should look something like this, and you should be able to easily access the `Resources` folder: - -![](../../assets/images/setup/vs-solution-explorer-switch-view-3.png) - -### JetBrains Rider - -In Rider, you can "attach" the resources directory to the solution. Do this by right clicking the solution in the explorer, then doing "Add" -> "Existing Folder...". Select the "Resources" directory in the file picker. - -![asdfs](../../assets/images/setup/rider-attach-folder-1.png) -![](../../assets/images/setup/rider-attach-folder-2.png) - -After this, your solution view should look something like this, and you should be able to easily access the `Resources` folder: - -![](../../assets/images/setup/rider-attach-folder-3.png) - -### Visual Studio Code - -Visual Studio Code shows all files by default, so no extra setup is needed here. - -# Reproducible Development Environment with Nix/NixOS - -An easier way to set up your development environment for Linux users is to leverage Nix. Nix is a package manager and a functional domain specific language that allows one to declare anything from development environments to entire systems. In order to prevent the dreaded "it works on my machine" conundrum, we can declare a development environment in Nix that spawns an isolated reproducible shell. - -## Setting up Nix/NixOS with flakes - -You can [install Nix](https://nixos.org/download) either through installing the NixOS distribution itself or by using the script that is compatible with all Linux distributions that use systemd (Ubuntu, Fedora, Mint etc). For the sake of simplicity and convenience, it is recommended that you install Nix in a distribution that you are comfortable with instead of making the jump to a different operating system entirely. It is also possible to use Nix with MacOS through `nix-darwin` though this has not been tested as of yet and thus not covered in this article. - -Once Nix is installed, you should enable experimental features such as flakes. If you are on a non-NixOS distribution, you can just add the following to your `~/.config/nix/nix.conf`. - -* `experimental-features = nix-command flakes` - -If you're using NixOS, you only need to add these options to your `configuration.nix` file. - -* `nix.settings.experimental-features = [ "nix-command" "flakes" ];` - -For more information about how to enable Nix flakes, see [here](https://nixos.wiki/wiki/Flakes). - -## Using Nix flakes for a Robust Development Environment - -NB it is technically required that you already have Git installed but in the case with most Linux distributions it comes preinstalled. In the highly unlikely case that you do not: - -* Use your distribution's package manager - -* Declare it in your `configuration.nix` file if you're using NixOS. It's recommended that you check the [appropriate section in the NixOS manual](https://nixos.org/manual/nixos/stable/#sec-configuration-file) but in short you should add `pkgs.git` into the `environment.systemPackages` attribute. - -Using your terminal you can simply navigate to the root directory of your GS14 repo and run: - -* `nix develop` - -Nix will automatically handle all dependencies as declared by `shell.nix` and called by the `flake.nix` file. You will have a new ephemeral shell (known as a `devShell`) that has everything that you need installed to build GS14 from source. - -This remains the reason as to why flakes are highly recommended despite being considered an experimental feature. We can make sure that everyone has the same versions of dependencies by specifying the nixpkgs collection version in the input attribute of the flake and locking the versions in a `flake.lock` file. In this way, all contributors that use Nix/NixOS get to have the exact same development environment. No pun intended, but that is pretty robust! - -## (Optional) Run JetBrains Rider through Nix - -You can then use either use an editor or IDE of your choosing. However within the shell that you already spawned you can just specify that you require JetBrains Rider. Run this command in your devShell. - -* `NIXPKGS_ALLOW_UNFREE=1 nix shell nixpkgs#jetbrains.rider --impure` - -From your new shell you can start a "detached" JetBrains Rider process by running something like: - -* `nohup rider >/dev/null 2>&1 &` - -And voila! You have robustly set up your development environment in a way that doesn't result in pesky buildup of "state". You can practically work on GS14 from any Linux distribution (granted that they use systemd) without irreversibly changing your system. - -# Troubleshooting - -Make sure [the first three items](#setting-up-a-development-environment) on top are downloaded. - -## `RUN_THIS.py` not running -Check that python is installed from the website and not the Microsoft Store. If it's installed from the Microsoft Store, uninstall it then download and install from the python website. - -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. This issue may be caused by a stupid Microsoft shortcut. Which you can disable by searching for `Manage App Execution Aliases` and disabling the two python references - -### py not found -If python was installed from the website and the `python` command works, but you still get the error 'py is not installed', then check if `C:\WINDOWS\py.exe` works. If so, then add `C:\WINDOWS` to your path. - -## System.DllNotFoundException: Unable to load DLL 'freetype6' or one of its dependencies: The specified module could not be found. - -```PS C:\Users\Larme\Downloads\space-station-14> dotnet run --project Content.Client -Unhandled exception. Robust.Shared.IoC.Exceptions.ImplementationConstructorException: Robust.Client.Graphics.FontManager threw an exception inside its constructor. - ---> System.DllNotFoundException: Unable to load DLL 'freetype6' or one of its dependencies: The specified module could not be found. (0x8007007E) - at SharpFont.FT.FT_Init_FreeType(IntPtr& alibrary) - at SharpFont.Library..ctor() - at Robust.Client.Graphics.FontManager..ctor(IClyde clyde) in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Client\Graphics\FontManager.cs:line 33 - --- End of inner exception stack trace --- - at Robust.Shared.IoC.DependencyCollection.BuildGraph() in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Shared\IoC\DependencyCollection.cs:line 348 - at Robust.Shared.IoC.IoCManager.BuildGraph() in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Shared\IoC\IoCManager.cs:line 271 - at Robust.Client.GameController.InitIoC(DisplayMode mode) in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Client\GameController\GameController.IoC.cs:line 16 - at Robust.Client.GameController.ParsedMain(CommandLineArgs args, Boolean contentStart, IMainArgs loaderArgs, GameControllerOptions options) in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Client\GameController\GameController.Standalone.cs:line 49 -``` - -Uninstall .NET Core SDK x86. Install .NET Core SDK x64. - - -## The client and server aren't available in Visual Studio to configure in Multiple startup projects - -This may be because you opened the project as a folder rather than a solution. Make sure you open it as a solution and click the space station 14 .sln file. - -## The system cannot find the specified file RUN_THIS.py - -`The system cannot find the specified file` error usually means that OneDrive is conflicting with the git repository. Clone the git repo outside of OneDrive or disable syncing for the cloned folder. +```admonish warning +This page is under construction and a lot of the information here is out of date or incorrect. Just read wizden's development environment and replace the repo you're using with Goob's. Literally the only reason I haven't deleted this page is because people literally just want to copy past commands without using 1 brain cell. +``` + +# Setting up a Development Environment + +First you're gonna need some software: + +* [Git](https://git-scm.com/) or one of the [many](https://www.sourcetreeapp.com/) [third-party](http://www.syntevo.com/smartgit/) [UIs](https://tortoisegit.org/) that make it easier to use. Make sure to let it install to your PATH like [this](../../assets/images/setup/git-path.png). +* [Python 3.7 or higher](https://www.python.org/). Make sure to install it into your [PATH on Windows](../../assets/images/setup/python-path.png). Also make sure the 'py launcher' option is enabled when installing on Windows. You should get python from [python.org](https://www.python.org/). Versions installed from the windows store sometimes cause build issues. +* [.NET 9.0 SDK](https://dotnet.microsoft.com/download/dotnet/9.0). Visual Studio also installs this if you're on Windows. + * ARM (M1) Mac users: You need to make sure to install x64 .NET, **not** ARM .NET. The engine does not currently run natively on Mac ARM so using x64 via Rosetta 2 emulation is recommended. +* Preferably an IDE to make development not painful (all free options unless otherwise noted): + * For **Windows**, [Visual Studio 2022 **Community**](https://www.visualstudio.com/). For a minimal install (Jesus it's large) you're gonna want the .NET desktop development workload, the C# compiler, C# support, NuGet package manager, MSBuild and .NET 9 SDK or something along those lines. + * For **macOS**, [Visual Studio for Mac](https://docs.microsoft.com/en-us/visualstudio/mac/). + * For **all platforms**, (NOT FREE) [Rider](https://www.jetbrains.com/rider/) is an IDE preferred by some mostly wizden developers. College/University students can get a free education license, even if they're not a computer science major. + * For **all platforms**, [Visual Studio Code](https://code.visualstudio.com/) with the C# extension. Usually an inferior IDE experience than full blown IDEs like regular Visual Studio, but some experienced programmers enjoy the minimalism. + * **Exclusive to VSCode/VSCodium**: you can install our community made [Robust YAML](https://marketplace.visualstudio.com/items?itemName=slava0135.robust-yaml) extension for better Robust Toolbox YAML experience on top of [YAML Language Support](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) extension. + * For **all platforms**, [VSCodium](https://vscodium.com/) with the C# extension. Open source and without the bloat and tracking of VSCode. + +## 1. Cloning + +**Even if you already know Git, scroll down to read the section about submodule setup. Seriously.** + +If you're **familiar with Git**, just fork and clone the repository, set up remotes, and then follow the submodule guide below. + +If you're **unfamiliar with Git**, or just don't know how to proceed, read the [howdoicode](../setup/howdoicode.md) guide, which goes in depth on how to contribute to the game and how to set up your initial repository. It also touches on submodule setup, but that's included here as well because of its importance. + +## 2. Submodule Setup + +We have an automatic submodule updater so you don't have to worry about running `git submodule update --init --recursive` all the time. + +Run `RUN_THIS.py` inside the repo you downloaded with Python. Preferably from a terminal too. This should take a few seconds so if it instantly stops then check if you are running Python 3.7+ otherwise keep reading. + +**If running `RUN_THIS.py` immediately opens and closes a window: do not worry.** This does not mean that it failed. The script closes automatically upon completion, so if you want to verify that it worked properly, check the submodule `/RobustToolbox/` and verify that all the files are there. If not try checking out the troubleshooting at the bottom of this page. + +Note: If you have any issues when getting started with missing files it's recommended you run `git submodule update --init --recursive` by hand once in case something went wrong with python. + +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), make a file called `DISABLE_SUBMODULE_AUTOUPDATE` inside the `BuildChecker/` directory. + +And with that, your repo is now properly setup! + +## 3. Setup an IDE + +### Visual Studio + +1. Download Visual Studio Community (if you don't own a paid version) from [here](https://visualstudio.microsoft.com/vs/community/) +2. Run the installer and choose `.net desktop development`, then install +3. If the installer asks you for a development environment select `Visual C#`. +4. Open Visual Studio +5. Select `Open a project or solution`, then navigate to your cloned repository from above and open `SpaceStation14.sln` + +### JetBrains Rider +1. Install Rider, we suggest using [Jetbrains Toolbox](https://www.jetbrains.com/toolbox-app/) so it can also automaticly update in the future. +2. Go through the setup. +3. Press "Open" and select `SpaceStation14.sln` +4. If you plan to do engine development you must add Robust Toolbox to the Directory Mappings so that Riders VCS can detect changes to Robust. + Open Riders settings and go to the Version Control section > Directory Mappings and press the plus (+) button. For Directory point it to the `RobustToolbox` folder in the project and Git as the VCS + +### VSCodium +1. Download [VSCodium Here](https://vscodium.com/) or more directly [on Github Here](https://github.com/VSCodium/vscodium/releases) (On the latest release, click the assets dropdown then scroll to the ZIP or .exe for your OS). +2. Run the installer or extract the zip file to a location of your choice and run the .exe once extracted. +3. Once installed, navigate to the Extensions tab (part way down on the top left corner bar, looks like 4 tiles) and search for "C#". An extension by "Muhammad-Sammy" with over 70K downloads and a green / white logo is the one, install that. Extension ID `muhammad-sammy.csharp`. +4. Select File > Open Folder, then navigate to your cloned repository from above and open this full folder. +5. When asked to open a solution, select `SpaceStation14.sln`. Alternatively, set `dotnet.defaultSolution` setting to `SpaceStation14.sln` in your workspace settings. +6. Now you can run and debug your game. Select the icon above "Extensions" from earlier for "Run and Debug" and from the dropdown next to the green play button you can select "Server/Client". This will run both the client and server, opening the game for you to debug. Relevant information will pop up in the debug along the bottom. Select the processes in the call stack on the left to change what you are debugging. + +## 4. Starting SS14 + +Now you can get on to compiling the client and server! Use your flavor of IDE to open the solution file `SpaceStation14.sln` and press the build button. + +To compile without an IDE, run `dotnet build` in the Goob Station repo directory. Then, call the following commands to run the client and server. +* `dotnet run --project Content.Server` +* `dotnet run --project Content.Client` + +Both these commands use a debug configuration by default. To enable release optimizations, add `--configuration Release` to the dotnet invocation. + +Note: If you're having problems with dotnet not finding libssl (e.g. when using libressl), try setting the `CLR_OPENSSL_VERSION_OVERRIDE` environment variable to the appropriate version. For instance, set it to `48` if your `/usr/lib` contains `libssl.so.48`. +If that doesn't work you can also try running `ln -s /usr/lib/libssl.so /usr/local/lib/libssl.so.1.0.0` instead. + +## 5. Configuring Build Options + +The GS14 client and server are independent projects, but both can launch with a single button somewhere in your IDE. This needs to be set up, however. Note: **It is recommended that you run `Content.Client` and `Content.Server` when developing from your IDE.** *Not* `Robust.Client` or `Robust.Server`. The reason is that running `Content.*` will make your IDE aware of dependencies correctly and ensure everything is rebuilt nicely. If you run `Robust.Client` directly you have to make sure the solution is fully built every time which is annoying and easy to forget. + +### Visual Studio 2022 + +In Visual Studio 2022, you can configure the build button to run both the server and client by right clicking the solution, then selecting `Configure StartUp Projects...`. Once the menu pops up, then select `Multiple startup projects:` and set the action for `Content.Client` and `Content.Server` to `Start`. Once you apply the changes, hitting the big `Start` button with a green arrow next to it should launch both client and server at the same time. + +Note: If you're having problems with the program not getting built right, you may need to set always build before run. Go to Options `Projects and Solutions/Build and Run` and change `On Run, when projects are out of date` to `Always build`. + +In VS you can also use the keys F7 to build the project and F5 to run it. + +### Visual Studio Code + +The C# extension provides a `"coreclr"` launch type which can be used to run the `Content.Server` and `Content.Client` executables in their respective `bin/` directories. A [compound launch configuration](https://code.visualstudio.com/Docs/editor/debugging#_compound-launch-configurations) can be used to run the server and client at the same time. + +### Command Line + +Build with `dotnet build` and run the client and server on different command lines with: + +* `dotnet run --project Content.Server` +* `dotnet run --project Content.Client` + +There's also definitely some way to run two commands at the same time, but you should probably google it. + +### JetBrains Rider + +In Rider you can create a "compound configuration" to run or debug both client and server at the same time. Quite convenient! + +![](../../assets/images/setup-rider-configurations.png) + +## 6. Configuring IDE directories + +C# IDEs like Visual Studio and Rider do not automatically show the `Resources` folder in the project. This folder contains all non-C# files such as sprites, audio, and most importantly, YAML prototypes. These instructions will explain how to get this folder to show up in your IDE, so you can easily work with it. + +### Visual Studio 2022 + +In Visual Studio, you can switch the **Solution Explorer** from "solution" view (only showing the C# projects) to "folder" view (showing all the files in the project). Press the button to switch views as follows, then select the folder view: + +![](../../assets/images/setup/vs-solution-explorer-switch-view-1.png) +![](../../assets/images/setup/vs-solution-explorer-switch-view-2.png) + +After this, the Solution Explorer should look something like this, and you should be able to easily access the `Resources` folder: + +![](../../assets/images/setup/vs-solution-explorer-switch-view-3.png) + +### JetBrains Rider + +In Rider, you can "attach" the resources directory to the solution. Do this by right clicking the solution in the explorer, then doing "Add" -> "Existing Folder...". Select the "Resources" directory in the file picker. + +![asdfs](../../assets/images/setup/rider-attach-folder-1.png) +![](../../assets/images/setup/rider-attach-folder-2.png) + +After this, your solution view should look something like this, and you should be able to easily access the `Resources` folder: + +![](../../assets/images/setup/rider-attach-folder-3.png) + +### Visual Studio Code + +Visual Studio Code shows all files by default, so no extra setup is needed here. + +# Reproducible Development Environment with Nix/NixOS + +An easier way to set up your development environment for Linux users is to leverage Nix. Nix is a package manager and a functional domain specific language that allows one to declare anything from development environments to entire systems. In order to prevent the dreaded "it works on my machine" conundrum, we can declare a development environment in Nix that spawns an isolated reproducible shell. + +## Setting up Nix/NixOS with flakes + +You can [install Nix](https://nixos.org/download) either through installing the NixOS distribution itself or by using the script that is compatible with all Linux distributions that use systemd (Ubuntu, Fedora, Mint etc). For the sake of simplicity and convenience, it is recommended that you install Nix in a distribution that you are comfortable with instead of making the jump to a different operating system entirely. It is also possible to use Nix with MacOS through `nix-darwin` though this has not been tested as of yet and thus not covered in this article. + +Once Nix is installed, you should enable experimental features such as flakes. If you are on a non-NixOS distribution, you can just add the following to your `~/.config/nix/nix.conf`. + +* `experimental-features = nix-command flakes` + +If you're using NixOS, you only need to add these options to your `configuration.nix` file. + +* `nix.settings.experimental-features = [ "nix-command" "flakes" ];` + +For more information about how to enable Nix flakes, see [here](https://nixos.wiki/wiki/Flakes). + +## Using Nix flakes for a Robust Development Environment + +NB it is technically required that you already have Git installed but in the case with most Linux distributions it comes preinstalled. In the highly unlikely case that you do not: + +* Use your distribution's package manager + +* Declare it in your `configuration.nix` file if you're using NixOS. It's recommended that you check the [appropriate section in the NixOS manual](https://nixos.org/manual/nixos/stable/#sec-configuration-file) but in short you should add `pkgs.git` into the `environment.systemPackages` attribute. + +Using your terminal you can simply navigate to the root directory of your GS14 repo and run: + +* `nix develop` + +Nix will automatically handle all dependencies as declared by `shell.nix` and called by the `flake.nix` file. You will have a new ephemeral shell (known as a `devShell`) that has everything that you need installed to build GS14 from source. + +This remains the reason as to why flakes are highly recommended despite being considered an experimental feature. We can make sure that everyone has the same versions of dependencies by specifying the nixpkgs collection version in the input attribute of the flake and locking the versions in a `flake.lock` file. In this way, all contributors that use Nix/NixOS get to have the exact same development environment. No pun intended, but that is pretty robust! + +## (Optional) Run JetBrains Rider through Nix + +You can then use either use an editor or IDE of your choosing. However within the shell that you already spawned you can just specify that you require JetBrains Rider. Run this command in your devShell. + +* `NIXPKGS_ALLOW_UNFREE=1 nix shell nixpkgs#jetbrains.rider --impure` + +From your new shell you can start a "detached" JetBrains Rider process by running something like: + +* `nohup rider >/dev/null 2>&1 &` + +And voila! You have robustly set up your development environment in a way that doesn't result in pesky buildup of "state". You can practically work on GS14 from any Linux distribution (granted that they use systemd) without irreversibly changing your system. + +# Troubleshooting + +Make sure [the first three items](#setting-up-a-development-environment) on top are downloaded. + +## `RUN_THIS.py` not running +Check that python is installed from the website and not the Microsoft Store. If it's installed from the Microsoft Store, uninstall it then download and install from the python website. + +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. This issue may be caused by a stupid Microsoft shortcut. Which you can disable by searching for `Manage App Execution Aliases` and disabling the two python references + +### py not found +If python was installed from the website and the `python` command works, but you still get the error 'py is not installed', then check if `C:\WINDOWS\py.exe` works. If so, then add `C:\WINDOWS` to your path. + +## System.DllNotFoundException: Unable to load DLL 'freetype6' or one of its dependencies: The specified module could not be found. + +```PS C:\Users\Larme\Downloads\space-station-14> dotnet run --project Content.Client +Unhandled exception. Robust.Shared.IoC.Exceptions.ImplementationConstructorException: Robust.Client.Graphics.FontManager threw an exception inside its constructor. + ---> System.DllNotFoundException: Unable to load DLL 'freetype6' or one of its dependencies: The specified module could not be found. (0x8007007E) + at SharpFont.FT.FT_Init_FreeType(IntPtr& alibrary) + at SharpFont.Library..ctor() + at Robust.Client.Graphics.FontManager..ctor(IClyde clyde) in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Client\Graphics\FontManager.cs:line 33 + --- End of inner exception stack trace --- + at Robust.Shared.IoC.DependencyCollection.BuildGraph() in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Shared\IoC\DependencyCollection.cs:line 348 + at Robust.Shared.IoC.IoCManager.BuildGraph() in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Shared\IoC\IoCManager.cs:line 271 + at Robust.Client.GameController.InitIoC(DisplayMode mode) in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Client\GameController\GameController.IoC.cs:line 16 + at Robust.Client.GameController.ParsedMain(CommandLineArgs args, Boolean contentStart, IMainArgs loaderArgs, GameControllerOptions options) in C:\Users\Larme\Downloads\space-station-14\RobustToolbox\Robust.Client\GameController\GameController.Standalone.cs:line 49 +``` + +Uninstall .NET Core SDK x86. Install .NET Core SDK x64. + + +## The client and server aren't available in Visual Studio to configure in Multiple startup projects + +This may be because you opened the project as a folder rather than a solution. Make sure you open it as a solution and click the space station 14 .sln file. + +## The system cannot find the specified file RUN_THIS.py + +`The system cannot find the specified file` error usually means that OneDrive is conflicting with the git repository. Clone the git repo outside of OneDrive or disable syncing for the cloned folder.