Add getting started documentation

Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>
This commit is contained in:
Ritchie Frodomar 2024-10-20 00:09:36 -04:00
parent 638621185b
commit f58170f700
10 changed files with 1065 additions and 11 deletions

View file

@ -0,0 +1,135 @@
# Building Socially Distant from source
Although Socially Distant is fairly straight-forward to build, it is slightly more complex than most .NET projects and therefore requires some prior one-time setup before you can successfully compile the game. Furthermore, this setup depends on what platform you're on. This article explains exactly how to build Socially Distant from source.
## Pre-requisites
You will need:
- A 64-bit PC running Windows or Linux
- Steam
- .NET SDK 8
- For Linux users, Wine or Docker. You will need a Wine environment to compile shaders with.
- Basic familiarity with the .NET CLI
- Git
If you plan on contributing to the game, an acidic light community account is also required. You can create an account on the [community GitLab server](https://gitlab.acidiclight.dev/) if you do not have any. Signing in with Discord, GitHub, and Patreon is also possible.
## Clone the game
Clone the open-source version of the game to get started.
```bash
git clone https://gitlab.acidiclight.dev/sociallydistant/sociallydistant.git
```
If you plan to contribute to the game, you will need a fork on our GitLab server. Use the GitLab UI to create the fork, then add it as a remote to your cloned version of the repository.
```bash
git remote add fork https://gitlab.acidiclight.dev/YOUR_USERNAME/sociallydistant.git
```
For more info on contributing to the game, see [Contribution guidelines](/docs/contributing.md).
## Add the NuGet feed for Socially Distant
Socially Distant is made using **Ritchie's Toolbox,** a de facto custom game engine built on top of a fork of MonoGame. In order for .NET to acquire the required build tools and libraries for the game, you will need to add a new NuGet feed to your system's NuGet configuration. This only needs to be done once per user account per computer.
```bash
dotnet nuget add source -n gitlab-acidiclight https://gitlab.acidiclight.dev/api/v4/projects/13/packages/nuget/index.json
```
If, at any time, you want to remove this NuGet feed, run:
```bash
dotnet nuget remove source gitlab-acidiclight
```
## IMPORTANT: Shader compilation
Shader compilation is a **mandatory** part of game compilation, and rather unfortunately, is flaky due to MonoGame limitations. If you want it to smoothly, and don't feel like troubleshooting, then follow this section to the letter.
### If you're on Windows
Most likely, you will be fine. However, it is highly advised that you install Visual Studio with the ".NET Desktop Development" and "Desktop Development with C++" workloads. Even if you don't use Visual Studio as an IDE, this will ensure your system has the necessary native Windows libraries needed for asset compilation (including shader compilation) to succeed.
### If you're on Linux
A Windows-compatible build environment is required for shader compilation to succeed. This is because MonoGame shaders are written in HLSL and compiled using MojoShader, which uses the proprietary Direct3D shader compiler under the hood.
#### Using Wine directly
If you plan on actually developing the game, the recommended way to set up shader compilation is by using Wine directly.
You will need a 64-bit Wine installation, i.e `wine64`. Some distributions do not properly package Wine, resulting in a 32-bit environment being set up even when you specifically asked for a 64-bit one. If you run into this, your distro has done you a dis-service and the game will not build.
The esaiest way to check if your distribution is sane is to set up a 64-bit Wine prefix and check the `%PROCESSOR_ARCHITECTURE%` environment variable within the Windows environment.
```bash
export WINEARCH=win64
export WINEPREFIX="$HOME/.winetest"
wine64 wineboot
wine64 cmd
```
```bash
C:\> echo %PROCESSOR_ARCHITECTURE%
```
If it outputs `x86_64`, then great. Otherwise, you're on your own.
When you've installed Wine64 and verified that it actually works, navigate to the root of your repository and run the MGFXC Wine setup script.
```bash
chmod +x mgfxc-wine-setuo.sh
./mgfxc-wine-setup.sh
```
You will need to log out of and log back into your computer for the changes to take effect.
You can verify that the setup finished successfully, after re-logging, by checking the `MGFXC_WINE_PATH` environment variable has been set.
```bash
echo $MGFXC_WINE_PATH
# should look like
/home/ritchie/.winemonogame
```
If the environment variable isn't set, you may need to configure your login shell to set it to the value of `$HOME/.winemonogame`.
#### Docker
If you have Docker on your system, and want to build the game that way, a pre-configured Docker image with the necessary build tools for Socially Distant is available. However, this is built primarily around being used for CI/CD rather than as a development environment. Nonetheless, you can get a container and run a shell on it with this command.
```bash
docker run --rm -it cr.acidiclight.dev/docker-images/monogame-linux-build/dotnet-mgcb:latest /bin/bash
```
Note that you will need to add the custom NuGet feed within the container itself.
## Compile the game
If you've gotten this far, then everything from here on out is a breeze.
### Quickly run the game
Run the game by running the `SociallyDistant` project with `dotnet run`. This compiles the game and launches it.
```bash
cd src/SociallyDistant
dotnet run
```
### Build the game for release/installation
If you want to create a release version of the game, use `dotnet publish` to generate a self-contained build of the game for your platform. We do not create framework-dependent builds of the game for release, and we do not support the use of them, since self-contained builds are guaranteed to have the correct version of the .NET runtime bundled with them.
```bash
cd src/SociallyDistant
# Build for Linux
dotnet publish --self-contained -r linux-x64 -c release -o /path/to/build/output
# Build for Windows (Note: Must be done *on* Windows.)
dotnet publish --self-contained -r win-x64 -c release -o "C:\Path\To\Build\Output"
```
### Building the Socially Distant Core
The third officially-supported way to build Socially Distant is to build its core framework (`SociallyDistant.Framework`). This builds only the parts of the game that are necessary to write mods with. In the future, the development workflow for mod developers will be streamlined.
Note: Do not do a self-contained build for `SociallyDistant.Framework`.
```bash
cd src/SociallyDistant.Framework
dotnet publish -c debug -o /path/to/modding/build/output
```
Then, reference the resulting assemblies in your mod's C# project.

381
docs/code-style.md Normal file
View file

@ -0,0 +1,381 @@
# Socially Distant Code Style
Code written for Socially Distant, above all else, must be human. This means that all code in the game should be easily understood by any human being with a reasonable grasp of programming concepts. It should not feel confusing to read for someone with reasonable programming skill. On this page, you'll learn how we write code to reflect that.
## Functioning code is good code.
To put it bluntly, it doesn't matter one bit how well-written or "clean" your code is if it doesn't fucking work. Someone will eventually need to read, understand, and debug your code, so it should always be written with that in mind.
This boils down to these core principles:
- **Premature optimization is the root of all evil**: Wasting time optimizing your code for a computer before its functionality has actually been battle-tested will most likely result in you having optimized a bug. That bug is still a bug, and will need to eventually be found and fixed. Do not worry about optimizing your code at the cost of readability unless it both functions as-is and its current implementation demonstrably harms the game's performance.
- **Complexity causes problems:** Writing complex APIs and abstractions makes troubleshooting a bug more complex. Always focus on getting your code to _work_ before building a complex system around it. If complexity is needed, it will grow organically as your feature becomes more well-integrated with the rest of the codebase.
- **Code fixes problems, code doesn't fix code.** If you feel yourself trying to work around part of the game's code, there is a problem with someone's code. You should work to fix that problem directly, not work around it. Sometimes, the problem isn't actually in your own code.
## Naming things
The names of classes, variables and methods should, above all else, be understandable by humans.
Out of all things on this page, naming rules in Socially Distant are the most strict. This is because your code will be read by a screen reader, and must be understood when spoken aloud.
### Ensuring names can be spoken aloud easily
You **must** ensure that your names can be spoken aloud by a text-to-speech voice. Here's how:
1. **Avoid abbreviating things.** Prefer `numberOfPeople` over `numPeople`.
2. **Don't use acronyms.** Prefer `fileSystem` over `fs`.
3. **Use proper capitalization:** Local variables and private variables use `camelCase`, everything else uses `PascalCase`. Improper capitalization causes screen readers to guess, which makes things harder to read.
4. **Include unit names when necessary**: Prefer `angleInRadians` over `angle`, or `timeoutInSeconds` over `timeout`.
### Appropriate single-letter names, and their assumed meaning
You should never use single-letter names if you can avoid it, as they violate the above rules for screen reader compatibility. Names in this list are acceptable, as long as they're used in the correct context.
| **Name** | **Meaning** | **Valid context** |
|:-----------------:|:----------------------------------------------------------|:-----------------------------------------|
| `i` | Loop counter, or "index" | Use when iterating through a collection. |
| `j` | Nested loop counter | Same as `i`, but for nested loops. |
| `x`, `y`, `z`, `w` | Components of a 2D, 3D, or 4D vector, or of a quaternion. | Math |
| `e` | Euler's constant | Math |
| `e` | Event arguments | Event listeners and callbacks |
| `T` | Any class, structure, or other type | Type parameters and generics |
Please note that `r`, `g`, `b` and `a` (color components) are intentionally absent from that list. You should use `red`, `green`, `blue`, and `alpha` as names instead.
## Nullability
Socially Distant uses nullable reference types. Please write your code with nullability in mind. Do not opt out of nullability with `#nullable disable` under any circumstances, fix your code instead.
## Type structure
When defining any type, such as a class, please use these conventions:
### Access level
Access levels should always be specified explicitly. (`internal class` vs. just `class`).
All API defined in Socially Distant should use the least-permissive access modifier necessary for the code to function. If it doesn't need to be `public`, it should not be `public`.
### Always mark as `sealed` by default
Most types in Socially Distant do not need to be inherited, and this should be communicated. When defining a class, you should always mark it as `sealed` until someone actually needs to inherit it in another type. This has [performance implications](https://youtu.be/d76WWAD99Yo) and there is no reason to leave an object open for inheritance if it need not be inherited.
### Member ordering
When defining the members of a type, please follow this layout to ensure better readability.
- Fields
- Properties
- Events
- Methods
- Nested types
Then, order by access level:
- Public
- Protected / Internal
- Private
Then, for fields, order by mutability:
- Compile-time constant (`const`)
- Read-only (`readonly)
- Writable
## Attributes
Attributes are types that you can use to apply special *attributes* to certain parts of the code.
When applying an attribute to a type or member, always keep attributes on their own lines directly above the member.
This is good:
```csharp
[Attribute]
public sealed class Class
{
[Attribute]
private float field;
[Attribute]
public void Method()
{
this.field += 1;
}
}
```
This is bad:
```csharp
[Attribute] public sealed class Class
{
[Attribute] private float field;
[Attribute] public void Method()
{
this.field += 1;
}
}
```
When adding multiple attributes to something, each attribute should be on a separate line:
```csharp
[Attribute1]
[Attribute2]
[Attribute3]
private float field;
```
Attributes on method parameters aren't affected by this rule - because they're rare.
## Type parameters and Generics
You will run into situations where generics are extremely useful, if not required, as part of your API.
Remember to use descriptive names when writing generic types. Use type constraints and good naming to ensure the correct types are specified by users of your API.
## Interfaces vs. Abstract Types
Socially Distant's API uses interfaces all over the place. When designing a system, you should take advantage of this.
Socially Distant uses interfaces to define what a given type must be able to do in order for that type to be valid as part of another API. We do not care *how* a given thing is done, so long as the implementation conforms to an interface's requirements.
A good example of this is `IComputer` for representing an in-game computer. It describes what a given type must be able to do in order to be considered a computer. It does not make any concrete assertions on how the computer accomplishes its goal of being a computer.
We do **not** use abstract types unless there is mandatory concrete behaviour associated with the abstract type. A good example of this is `Widget`, since all widgets must be able to perform a layout update and must be able to be drawn to the screen, and both these behaviours need to generally behave the same across all widgets.
In other words, if you define an abstract class that only has `abstract` members with no `virtual` or concrete ones, then it should instead be defined as an interface.
This is an appropriate use case of abstract types:
```csharp
public abstract class Animation
{
private readonly float durationInSeconds;
private float progressInSeconds;
protected Animation(TimeSpan duration)
{
this.durationInSeconds = (float) duration.TotalSeconds;
}
public void Update()
{
float progress = MathHelper.Clamp(progressInSeconds / durationInSeconds, 0, 1);
OnUpdate(progress);
progressInSeconds += Time.DeltaTime;
}
protected abstract void OnUpdate(float progressPercentage);
}
```
This should be an interface:
```csharp
public abstract class Animation
{
public abstract TimeSpan Duration { get; }
public abstract void Update();
}
// Should instead be
public interface IAnimation
{
public TimeSpan Duration { get; }
public void Update();
}
```
Sometimes, it's a good idea to have both an interface and an abstract type implementing that interface. The abstract type should implement the interface, so that your API can accept other implementations of said interface.
```csharp
public interface IAnimation
{
public TimeSpan Duration { get; }
public float Progress { get; }
public bool IsActive { get; }
public void Update();
public void Cancel();
}
public abstract class Animation : IAnimatio
{
private readonly TimeSpan duration;
private double progressInSeconds;
private bool isComplete;
private bool isCanceled;
public TimeSpan Duration => duration;
public float Progress => MathHelper.Clamp((float) (progressInSeconds / duration.TotalSeconds), 0, 1);
public bool IsActive => !isCompleted && ~isCanceled;
public Animation(TimeSpan duration)
{
this.duration = duration;
}
public void Update()
{
OnUpdate(Progress);
if (progressInSeconds >= duration.TotalSeconds)
{
isCompleted = true;
return;
}
progressInSeconds += Time.DeltaTime;
}
protected abstract void OnUpdate(float progress);
}
```
By designing APIs in this way, when an abstract type is genuinely needed, that type can also be generic.
## Whitespace, braces, nesting,, and line breaks
Socially Distant is primarily written in C#, however portions of the game are written in `sdsh` (the Socially Distant Shell language). These formatting rules depend on what language you're writing in. [Learn more about `sdsh`](sdsh/the-basics.md)
### Indentation rules
Always use spaces instead of tabs, in both languages.
In C# code, use four spaces for each level of indentation. For `sdsh` scripts, use two spaces.
### Brace style
In C# code, curly braces always belong on their own lines, i.e.
```csharp
public sealed class Class
{
public void Method()
{
}
}
```
instead of
```csharp
public sealed class Class {
public void Method() {
}
}
```
For `sdsh` scripts, do the opposite, i.e:
```sh
function sayHello() {
say "Hello, $1!"
}
sayHello Ritchie
```
This is because `sdsh` is an interpreted language, so your scripts will take up less space in RAM and less time to execute.
### Nesting
In C#, avoid heavy amounts of nesting. If a method nests code more than three levels deep, you should try to fix that.
In `sdsh`, deeper nesting **directly correlates** to the script taking more time to execute. Furthermore, nested functions are defined in global scope as the script is executed, so excessive nesting will cause bugs.
### Line breaks
In C#, always separate members with a blank line, except for fields with the same access modifiers.
```csharp
public sealed class Class
{
public const float Constant = 42;
public readonly float PublicField;
public readonly float PublicField;
public readonly float PublicField;
public readonly float PublicField;
private readonly float Field;
private readonly float Field;
private readonly float Field;
private readonly float Field;
public float Property => Field;
public bool IsDone => false;
public IEnumerable<string> Things
{
get
{
yield return "One thing";
yield return "Two thing";
yield return "Red thing";
yield return "Blue thing";
}
}
public string Name
{
get => "Ritchie";
set => SetName(value);
}
public event Action? RitchieWasAngered;
public Class()
{
Field = Constant;
}
private void SetName(string newName)
{
RitchieWasAngered?.Invoke();
Log.Error($"My name is {Name}, not {newName}.");
}
private struct Struct
{
public int Number;
public string String;
public bool Boolean;
public void DoScaryThing()
{
throw new NotSupportedException("I cannot be scared.");
}
}
}
```
When writing `sdsh` scripts, separate groups of similar statements with a blank line.
```sh
function Function() {
command
command
command
if condition;
then
command Success
else
command Fail
fi
}
VARIABLE=value
VARIABLE=value
export ENV=value
export ENV=value
export ENV=value
Function
say "An octagon has 8 fantastic sides"
say "An octagon also has 8 amazing angles"
playSong --no-loop /Career/BGM/Octagonfire
once SongFinishedEvent exec "logout --force"
```

227
docs/contributing.md Normal file
View file

@ -0,0 +1,227 @@
# Contribution Guidelines
It's awesome that you're considering contributing to Socially Distant's development with your time. However, to keep development moving smoothly for everybody, there are a few things you need to keep in mind. This page demonstrates how to properly contribute to the game, as well as what's expected from all contributions.
## Sign into GitLab with your acidic light community account
Socially Distant's source code is available on [our GitLab server](https://gitlab.acidiclight.dev/sociallydistant/sociallydistant). You'll need an account on it in order to submit contributions.
You can either create an acidic light community account through GitLab itself, or sign in using Discord, GitHub, or Patreon.
## Set up SSH or HTTPS access for Git
In order to push changes to GitLab, you'll need to be able to sign in through Git with your acidic light community account. The easiest way to do this is with [Git Credential Manager](https://github.com/git-ecosystem/git-credential-manager), but you will still need either an SSH key or a Personal Access Token.
### Get a Personal Access Token (for HTTPS)
Once signed into GitLab, click your profile picture then go to **Preferences**.
On the left sidebar, click **Access Tokens**.
On the main area, under the **Personal Access Tokens8* section, click the "Add new token" button on the far right.
In the form that appears, give your new token a name and expiry date. Under **Token scopes**, check "api".
Click **Create personal access token,** then copy the token. When Git Credential Manager asks for a token, this is what you give it. Do not share it with anyone else, not even a plushie.
### Add an SSH key (for SSH)
If you prefer to authenticate using an SSH key, you must add your public key to your account.
Once signed into GitLab, click your profile picture then go to **Preferences**.
On the left, select **SSH Keys**.
In the form that appears, paste your public key into the **Key** field. Give the key a name and expiration date, and make sure its usage type is **Authentication and Signing.**
Click **Add key**, and then you're all set. Make sure that you clone repositories over SSH instead of HTTPS, like so:
```bash
git clone git@gitlab.acidiclight.dev:/sociallydistant/sociallydistant.git
```
## Fork the game
Make sure you fork the game on GitLab before trying to push changes. Once you have, add it as an origin to your clone of the game.
```bash
# HTTPS
git remote add fork https://gitlab.acidiclight.dev/YOU/sociallydistant.git
# SSH
git remote add fork git@gitlab.acidiclight.dev:/YOU/sociallydistant.git
```
Then, check out a branch to work in.
```bash
git checkout -b my-cool-feature
```
Push the branch to your fork:
```bash
git push -u fork my-cool-feature
```
If the push was successful, you will get prompted to create a merge request. Feel free to do this now, as a way of showing what you intend to change, if you'd like.
## The legal stuff
You should keep in mind that Socially Distant is a commercial game, even though it is also an open-source project. Therefore, it is extremely important we ensure that we are legally allowed to incorporate your work in the game, while also protecting your own rights to the content being contributed. For this reason, Socially Distant contributions are protected with the [Developer Certificate of Origin](https://developercertificate.org/).
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
Socially Distant's own code is licensed under the MIT License.
When submitting a merge request to Socially Distant, an automated build pipeline will run to check the work you've submitted. In order for the pipeline to succeed, you must acknowledge your understanding of the Developer Certificate of Origin, and must certify that your work is submitted under a license compatible with that of Socially Distant's own code. To do this, follow these instructions:
1. Set your Git configuration
Your Git user name and email must match the display name and email of your acidic light community account, otherwise the DCO check won't pass.
```bash
git config --global user.name "Your Name"
git config --global user.email "youremail@example.com"
```
2. Sign-off your commits
Use the `-s` flag to sign-off when making a commit.
```bash
git commit -s
```
This will generate a `Signed-off-by: Your Name <your email>` text based on your Git config, and this is what the DCO check will look for.
## Communication etiquette
When contributing to Socially Distant, please be respectful of the community etiquette. Intentional mis-use of the community's GitLab server will result in a ban, and any associated repositories will be removed for storage reasons.
### Follow community rules
The GitLab is governed under the same rules as the Discord server.
1. Respect all members at all times. Harassment and bullying will not be tolerated.
2. Do not spam messages, images, emoji or pings.
3. NSFW (explicit or suggestive) content is not permitted or tolerated under any circumstances.
4. Posting invite links to other Discord servers is not allowed unless otherwise cleared by a moderator or administrator.
5. Offensive or controversial content (racism, homophobia, targeted hate, etc.) are not allowed.
6. Discuss controversial topics in a civil manner. There are people with differing views on these subjects and all views should be respected when objectively reasonable. Moderators are permitted to forcibly de-escalate or stop debates if necessary.
7. Do not share harmful links or malware. Do not share binary executable files.
8. Sharing/leaking of Patreon-exclusive content is not allowed.
### Keep things on-topic.
Discussions on the GitLab should be related to the game's development, and nothing else. Please do not use the GitLab to ask for help playing the game, or for general programming help. Do not use the GitLab as a platform for advertisement of other communities or projects, unless directly related to Socially Distant.
### Know how to human.
Contributions may be subject to a manual code review in certain cases. Any and all feedback given by a maintainer about your work is a criticism of the work itself, not you as a person. You are expected to treat others the same.
### Development follows Ritchie's lead.
If you feel there is something you can bring to the table to help Socially Distant's development, then you should. However, please do not feel discouraged if and when a contribution doesn't get accepted. The only reason a contribution doesn't get accepted would be that it breaks the game or that it doesn't align with the game's direction. Sometimes, interesting-sounding mechanics might not actually fit with the rest of the game and that's okay.
[Ritchie Frodomar](https://gitlab.acidiclight.dev/ritchie) is the BDFL (benevolent dictator for life) of Socially Distant. This means the game's development will always directly follow Ritchie's plans.
If ever there is a time where an issue cannot be resolved unanimously or a project decision needs to be made, Ritchie's decision is the final decision on behalf of the project. Unless explicitly stated otherwise, Ritchie's decisions are absolutely final. Endless circular debating isn't productive and just annoys people.
Any decisions made related to Socially Distant's development are made in the best interest of the game itself, in the hopes that people will enjoy playing it when it is finished.
## Write good commit messages
When a new version of the game is released, or when a devlog is published, we always include a list of all changes made to the game since the last update. This change list is always generated based on Git commit logs. Therefore, you should make sure you write well-formatted commit messages.
All commits should be small and incremental, to ease the burden of code reviews. With that in mind, commit messages should generally answer these questions:
1. **What are you trying to do?** Write a short but descriptive one-sentence title explaining what you are actually trying to do with these changes. You may be adding a new feature, fixing a bug, documenting something, adding an asset to the game, or anything in between. Just remember to keep it short and sweet, this is the part of the commit that actually shows in changelogs.
2. **What are you changing?** Your commit body should be a more technical description of what your changes are actually doing.
3. **Is this change related to an issue?** If so, link it. People watching that issue will get notified.
Commit messages should also be written in the imperative mood.
### Examples of good commit messages
```
Add support for confirmation dialogs in System Settings
Add an API for showing a confirmation dialog when the user
changes a given setting in System Settings. The API allows
the setting to provide a warning message to show when the
user changes the setting.
If the user dismisses the dialog, the setting change is
reverted. Otherwise, the change is committed. The setting
field can also specify a callback to perform when the
change is confirmed, like restarting the game.
Fixes issue #47
Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>
```
```
Document various layout widgets
Add API documentation for the Stack Panel,
Flex Panel, Wrap Panel, and Scroll View.
Signed-off-by: Human Person <iamnotarobot@human.ai>
```
```
Fix memory leak when closing lots of terminals
Fixes an issue where closing a Terminal via the close
button on the tab doesn't kill the underlying shell process,
leading to a memory leak as a result of the shell never
exiting.
Signed-off-by: Hari Mann <hari-mann@notareal.email>
```
## Write human code
Human code is code that can be easily understood by a human being with objectively reasonable programming skill. Your code should be easily understood by yourself and by the team, and by a person unfamiliar with the codebase, That is always the priority. We do not subscribe to any specific design pattern or programming paradigm, as this is a large codebase and the correct way to approach a problem depends on the problem.
Here are some general tips to ensure your code is human.
1. **Use good naming**: Pick names that aren't confusing, do not use abbreviations, and do not use magic numbers.
2. **Write comments when the code isn't obvious:** If it isn't immediately obvious what your code is doing or why it needs to do what it's doing, write a comment above it to describe that. It's okay to forget, but you may still need to describe the code later in a review.
3. **Don't write comments when the code is obvious:** If your code is well-written, its intentions will be obvious. Lots of comments clutters the screen, so don't over-explain your code.
4. **Functioning code is good code:** It may be tempting to write abstractions or find ways to avoid repeating yourself - but this is a waste of time. Prioritize making your code actually work before trying to make it look pretty. If an abstraction is needed, it'll grow organically.
5. **Functioning code is fast:** Do not get hung up on performance. If your code doesn't actually show up as a red flag in a performance profiler, you don't need to optimize it. Well-written code is easy to optimize later when that's actually needed.
For even more info about code style, [read the style guide.](docs/code-style.md)
## Please document your API!
Please remember to document any new API you add to the game, and feel free to fill in missing API documentation if you see it. It's important for others to be able to learn their way around the code.
Socially Distant uses the [docfx project](https://github.com/dotnet/docfx) to generate this online manual. This includes the documentation for the game's API. Please use [C#'s XML documentation strings](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/) when documenting public API.
Ideally, all code should be as self-documenting as possible. However, any public APIs need to be explicitly documented so that other developers know *why* and *how* to use the API.
## Lastly, have fun.
Never feel pressured to contribute to the game, and if you need help, ask!

View file

@ -1 +0,0 @@
# Getting Started

View file

@ -1 +0,0 @@
# Introduction

45
docs/project-structure.md Normal file
View file

@ -0,0 +1,45 @@
# Project structure
Here's everything you need to know about where to find things in the Socially Distant repository, and where things belong.
## C# source code
The game's source code and main solution file are found in the `src/` directory.
You will find three projects:
- `AcidicGUI`: This is the UI system of Ritchie's Toolbox. You only need to touch this if you're adding new core functionality to the UI system.
- `SociallyDistant.Framework`: This is the core framework of Socially Distant itself. This is the library that all game mods, including the game itself, must reference. It contains almost all of the game's public API and data types, but almost no actual game code.
- `SociallyDistant`: This is the main game binary. All game code and assets live here.
There are also some third-party libraries, such as IMGUI, in the `vendor/` directory. Leave them alone unless you really know what you're doing. Note that these libraries are available as NuGet packages, but we compile them from source to ensure compatibility with our custom MonoGame fork. If you need to add any community-maintained MonoGame/FNA helper libraries, you should include them as source code in the `vendor` directory - ask a project maintainer for help if you need it.
## Game assets
As mentioned, game assets are found in the `SociallyDistant` project in `src`. They're specifically found in the `Content` directory within that project.
Only the bare minimum assets needed for the game to function are included here. Assets needed by Career Mode are not part of the open-source codebase, and Career Mode will therefore not be available.
You will find shaders, certain core textures, and `sdsh` scripts in the Content directory.
## `sdsh` game scripts
Socially Distant uses a custom scripting language for in-game missions, NPC dialogues, and other scripted events in the game. This is because defining these scripted encounters in C# would look ugly. These scripts are written in a subset of **Bash** called `sdsh`.
You can learn about how to read and write `sdsh` here: [`sdsh`: The Basics](sdsh/the-basics.md)
All `sdsh` scripts belong in `src/SociallyDistant/Content/Scripts` and must end with the `.sh` extension. Scripts will be checked during game build, and any syntax errors will prevent the build from succeeding, as if they were C# compiler errors.
## Documentation
The page you're reading right now exists in Markdown form, alongside all other guides on this site, inside the `docs/` directory. API documentation is generated from XML documentation comments in the game's code.
## Saved game data
Sometimes, you'll need to fuck around with saved game data or the game's settings, usually because something broke.
You can find all saved game data and settings in the following locations depending on your platform:
- **Windows**: `C:\Users\YOU\AppData\Local\acidic light\Socially Distant`
- **Linux**: `~/.local/share/acidic light/Socially Distant`
If you care about the contents of that directory, **make a backup of it** before ever touching the game's code. Once a newer version of Socially Distant writes to your save file, that file can no longer be loaded in older versions.
## Where everything starts
The entry-point of Socially Distant is `GameApplication` in the `SociallyDistant` project.
Happy hackin'.

248
docs/sdsh/the-basics.md Normal file
View file

@ -0,0 +1,248 @@
# The Basics of `sdsh`
Socially Distant is heavily narrative-driven. Almost all of the game's main story is told through encounters with non-player characters, as well as through missions. These in-game encounters are written in a subset of Bash called `sdsh`. This is also the same language used to parse commands in the in-game command shell.
### Why would I want to write `sdsh`?
Consider a mission where the player must hack into a network, download a file from a device inside the network, then delete the file from the hacked device.
There are three theoretical ways this mission could be programmed into the game. It could be written in C# directly, allowing it to have full access to the game's API. It could be written in a data markup language like YAML or JSON, and thus written in a data-driven way. Or, it could be written in an embedded scripting language like Lua, or, well, `sdsh`.
Missions can be highly dynamic, meaning data-driven design can be a lot more challenging to deal with. Allowing a mission to be written in C# means it has to be **compiled into the game**, making it harder to iterate on the game's story telling. Using an embedded scripting language means the mission can be re-loaded on the fly after a change, while also allowing the mission to do basic programmer things within reason.
Furthermore, the game already needs `sdsh` because a command shell is an integral part of gameplay.
Missions, as well as other in-game encounters, are also long-lasting tasks the game needs to execute over a long period of time. This means they either need to be asynchronous or run on a background thread. The `sdsh` interpreter is designed to be ran asynchronously, while the language itself hides this away from you as a writer or programmer. This makes `sdsh` perfect for scripting missions.
## How to write `sdsh`
Use the in-game terminal to play around with these code examples.
### Run a command
Commands are everything! They tell the game to do things.
For example, this command opens this website in the user's browser:
```sh
man
````
### Run a command with parameters
Many commands accept or require you to specify parameters that control their behaviour. Each parameter is separated by whitespace.
```sh
echo Hello world!
```
### String literals
Strings of text in `sdsh` are surrounded in apostrophes (single-quotes).
```sh
echo 'This is a string of text.'
```
If you need to use an apostrophe within a string, you can escape it with a backslash.
```sh
echo 'I\'m Ritchie.'
```
### Text expressions
Text expressions allow you to insert variables and other expressions inside a string of text. Text expressions are surrounded by double-quotes.
```sh
USER='ritchie'
echo "Hello, $USER!"
```
### Variables
You can define a variable like this: `NAME=expression`
```sh
VAR1=ritchie
VAR2=is
VAR3=here
```
And you can access the value of a variable like this `$NAME`
```sh
echo $VAR1 $VAR2 $VAR3
```
You can also access a variable's value like this: `${NAME}`
```sh
echo ${NAME}s
```
### Writing to a file
You can write the output of a command to a file, like this:
```sh
echo Hello world! > ~/hello.txt
```
Or, you can write to the end of an existing file, like this:
```sh
echo Hello world! >> ~/hello.txt
```
### Piping
You can use the output of one command as the input of another command. This is called piping. You can use piping to make a cow say some wise words.
```sh
fortune | cowsay
```
### Reading a file and using it as input
You can also use the contents of a file as the input of a command. You can have the contents of a file read aloud to you, like this:
```bash
speak < ~/hello.txt
```
### Command expansion
Command expansion allows you to treat the output of a command as if it were text. You can then store this text as a variable, or pass it as a parameter to another command.
```sh
COWFORTUNE=$(fortune | cowsay)
echo $COWFORTUNE
```
### Environment variables
You can export any variable as an environment variable using the `export` keyword. Use this to customize your prompt.
```sh
export PS1='My Cool Prompt >>> '
```
### Functions
Use functions to group a list of commands together into a single command you can use later.
```sh
function cowfortune() {
fortune | cowsay
}
cowfortune
```
You don't need the `function` keyword, adding it is a stylistic choice.
```sh
cowfortune() { fortune | cowsay }
```
### Function Parameters
You can pass parameters to functions just like you do other commands. Function parameters are accessed by numbered variables, `$0` is the name of the function itself.
```sh
function hello() {
echo "Hello, $1!";
}
hello ritchie
```
### Run multiple things at once
You can run two commands at once with the `&` operator. This command makes the game wait 5 seconds while reading some text from a file.
```sh
(speak < ~/hello.txt) & sleep 5000
```
### Run one command after another, on the same line
You can use a semi-colon to separate two commands on the same line. This code saves a random fortune to a file, prints the file to the screen, then reads it aloud.
```sh
fortune > ~/hello.txt; cat ~/hello.txt; speak < ~/hello.txt
```
### Run a command only if a previous command succeeds
You can use the `&&` operator to run the right-hand command if the left-hand command completed without error.
```sh
fortune > ~/hello.txt && cat ~/hello.txt && speak < ~/hello.txt
```
### Conditional expressions
Use the `if` command to run a set of commands if a given command runs successfully.
```sh
if file ~/hello.txt
then
speak < ~/hello.txt
fi
```
If you prefer the `then` keyword on the same line as the condition, don't forget a semi-colon before `then`.
```sh
if file ~/hello.txt; then
speak < ~/hello.txt
fi
```
You can also check if the condition command **didn't** succeed:
```sh
if ! file ~/hello.txt
then
echo 'File not found!'
fi
```
Or, run one set of commands on success and another on failure:
```sh
if file ~/hello.txt
then
speak < ~/hello.txt
else
echo 'File not found!'
fi
```
You can use the `elif` command in a similar way to `else`, except it will only run its commands if this secondary condition succeeds.
```sh
if file ~/hello.txt
then
speak < ~/hello.txt
elif file ~/world.txt
speak < ~/world.txt
else
echo 'File not found!'
fi
```
### Loops
You can repeat a set of commands while a condition is true, using the `while` command.
```sh
while true;
do
echo "Infinite loop."
done
```
### Text matching
You can use the `case` command to match a string of text against a set of patterns, running a set of commands based on what pattern matches first.
```sh
VARIABLE=ritchie
case $VARIABLE
ritchie)
echo Ritchie is here
;;
hari)
echo A wild evil skeleton appears...
;;
*)
echo Someone unknown showed up. Their name is $VARIABLE
;;
esac
```
When defining a pattern, preceding whitespace is ignored and a closing parenthesis marks the end of the pattern. When defining the commands to run for a given pattern, use the double-semi-colon (`;;`) to mark the end of the pattern's commands and the start of a new pattern. Use `esac` after the end of the last pattern to mark the end of the `case` command.
An asterisk (`*`) acts as a wildcard inside patterns.

View file

@ -1,4 +1,15 @@
- name: Introduction items:
href: introduction.md - name: Getting started
- name: Getting Started items:
href: getting-started.md - name: Building from source
href: building-from-source.md
- name: Contribution guidelines
href: contributing.md
- name: Project structure
href: project-structure.md
- name: Code style guide
href: code-style.md
- name: Game scripting (sdsh)
items:
- name: The Basics
href: sdsh/the-basics.md

View file

@ -2,10 +2,17 @@
_layout: landing _layout: landing
--- ---
# This is the **HOMEPAGE**. # **Socially Distant** Online Manual
Welcome to the Socially Distant online manual. Socially Distant is an up-coming narrative hacking game coming to Steam. This manual is for both players and developers. You can find info on how to play the game as well as how to both mod it and contribute to it. Fidd the documentation lacking or want to contribute to it? We're accepting merge requests!
Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. ## Getting started
## Quick Start Notes: You can find the game's code on [GitLab](https://gitlab.acidiclight.dev/sociallydistant/sociallydistant). This is the place to submit bug reports, discuss and contribute up-coming features and fixes, and to contribute changes to this manual. Note that it is not the place to ask for help playing the game, there are better places for this such as the Discord server or Steam Community.
For more help getting started, here are some useful pages:
- [Building from source](docs/building-from-source.md)
- [Contribution guidelines](docs/contributing.md)
- [Project structure](docs/project-structure.md)
- [Code style guide](docs/code-style.md)
1. Add images to the *images* folder if the file is referencing an image.

View file

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="UserContentModel"> <component name="UserContentModel">
<attachedFolders /> <attachedFolders>
<Path>../docs</Path>
</attachedFolders>
<explicitIncludes /> <explicitIncludes />
<explicitExcludes /> <explicitExcludes />
</component> </component>