diff --git a/docs/building-from-source.md b/docs/building-from-source.md new file mode 100644 index 00000000..b2b71e80 --- /dev/null +++ b/docs/building-from-source.md @@ -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. \ No newline at end of file diff --git a/docs/code-style.md b/docs/code-style.md new file mode 100644 index 00000000..9389ceb4 --- /dev/null +++ b/docs/code-style.md @@ -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 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" +``` + diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000..0006ed44 --- /dev/null +++ b/docs/contributing.md @@ -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 ` 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 +``` + +``` +Document various layout widgets + +Add API documentation for the Stack Panel, +Flex Panel, Wrap Panel, and Scroll View. + +Signed-off-by: Human Person +``` + +``` +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 +``` + +## 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! diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 8b3a7945..00000000 --- a/docs/getting-started.md +++ /dev/null @@ -1 +0,0 @@ -# Getting Started \ No newline at end of file diff --git a/docs/introduction.md b/docs/introduction.md deleted file mode 100644 index f6ecaa67..00000000 --- a/docs/introduction.md +++ /dev/null @@ -1 +0,0 @@ -# Introduction \ No newline at end of file diff --git a/docs/project-structure.md b/docs/project-structure.md new file mode 100644 index 00000000..ade7abe7 --- /dev/null +++ b/docs/project-structure.md @@ -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'. \ No newline at end of file diff --git a/docs/sdsh/the-basics.md b/docs/sdsh/the-basics.md new file mode 100644 index 00000000..8e05d0c5 --- /dev/null +++ b/docs/sdsh/the-basics.md @@ -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. \ No newline at end of file diff --git a/docs/toc.yml b/docs/toc.yml index d3663b90..73a050cc 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -1,4 +1,15 @@ -- name: Introduction - href: introduction.md -- name: Getting Started - href: getting-started.md \ No newline at end of file +items: + - name: Getting started + items: + - 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 \ No newline at end of file diff --git a/index.md b/index.md index 6023cdd3..ef6e6993 100644 --- a/index.md +++ b/index.md @@ -2,10 +2,17 @@ _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. \ No newline at end of file diff --git a/src/.idea/.idea.sociallydistant/.idea/indexLayout.xml b/src/.idea/.idea.sociallydistant/.idea/indexLayout.xml index 7b08163c..196248d8 100644 --- a/src/.idea/.idea.sociallydistant/.idea/indexLayout.xml +++ b/src/.idea/.idea.sociallydistant/.idea/indexLayout.xml @@ -1,7 +1,9 @@ - + + ../docs +