diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 464dfbd..8dfae67 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -32,12 +32,12 @@ General Development - [General programming](en/general-development/codebase-info/conventions/general.md) - [Project programming](en/general-development/codebase-info/conventions/project.md) - [ECS Conventions](en/general-development/codebase-info/conventions/ecs.md) - - [Architecture](en/general-development/codebase-info/conventions/architecture.md) + - [Architecture Conventions](en/general-development/codebase-info/conventions/architecture.md) - [Networking Conventions](en/general-development/codebase-info/conventions/networking.md) - [Resource Conventions](en/general-development/codebase-info/conventions/resources.md) - - [Module Conventions](en/general-development/codebase-info/conventions/modules.md) - [Pull Request Guidelines](en/general-development/codebase-info/pull-request-guidelines.md) - [Codebase Organization](en/general-development/codebase-info/codebase-organization.md) + - [Modules](en/general-development/codebase-info/modules.md) - [Goob Reforged](en/general-development/codebase-info/goob-reforged/goob-reforged.md) - [Reforged Modules](en/general-development/codebase-info/goob-reforged/reforged-modules.md) - [Feature Proposals](en/general-development/feature-proposals.md) diff --git a/src/en/general-development/codebase-info/conventions/ecs.md b/src/en/general-development/codebase-info/conventions/ecs.md index 81b88cc..b74d4ea 100644 --- a/src/en/general-development/codebase-info/conventions/ecs.md +++ b/src/en/general-development/codebase-info/conventions/ecs.md @@ -60,21 +60,94 @@ When possible, try using the `EntitySystem` [proxy methods](https://github.com/s Examples (click to expand) ```csharp -// Without proxy methods... +// Without proxy methods - bad EntityManager.GetComponent(uid).EntityName; -// With proxy methods +// With proxy methods - good Name(uid); -// Without proxy methods... +// Without proxy methods - bad EntityManager.GetComponent(uid).Coordinates; -// With proxy methods +// With proxy methods - good Transform(uid).Coordinates; ``` +### Update loops +A lot of old code is accumulating frametime inside update loops to decide when to next run it. + +Accumulator example (bad): +```csharp + public override void Update(float frameTime) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + comp.Accumulator += frameTime; + + if (comp.Accumulator < UpdateInterval) + continue; + + comp.Accumulator -= UpdateInterval; + + // Code here + } +``` + +This is bad because of those reasons: +1. This makes the update loop impossible to synchronize between server and client, causing prediction and networking issues. +2. This approach uses the `float` type, and it is not precise enough in case of update loops, so it may cause rounding issues when the game is launched for a long time. +3. This constantly does the addition operation, which isn't bad on its own, but when there are hundreds of systems doing that the overhead can beocme noticeable. + +All of the above problems can be fixed by using `TimeSpan` type and `IGameTiming`. + +TimeSpan example (good): +```csharp + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit) + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + // Set the first update time after the entity is spawned. + // Without this it would update every single tick until NextUpdate catches up with the server time. + ent.Comp.NextUpdate = _timing.CurTime + ent.Comp.UpdateInterval; + Dirty(ent); + } + + public override void Update(float frameTime) + { + // CurTime is calculated so we do it only once outside the update loop instead of for every sigle entity. + var curTime = _timing.Curtime; + // Loop over all components, ignoring paused entities. + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.NextUpdate < curTime) + continue; // Not enough time has passed since the last update. + + // Set the time for the next update. + // Don't use + // comp.NextUpdate = curTime + UpdateInterval; + // because that eats the remainder with every update, causing the update loop to run slightly less often + // than given by UpdateInterval, which will be imprecise and can cause problems over large time durations. + comp.NextUpdate += UpdateInterval; + + // Dirty the component so that the client can reroll the NextUpdate datafield during predcition. + // Without this you will get mispredicts. + Dirty(uid, comp); + + // Do stuff here. + } + } +``` + + ## Events ### Method Events vs Entity System Methods diff --git a/src/en/general-development/codebase-info/goob-reforged/reforged-modules.md b/src/en/general-development/codebase-info/goob-reforged/reforged-modules.md index e6fd084..86bd3c7 100644 --- a/src/en/general-development/codebase-info/goob-reforged/reforged-modules.md +++ b/src/en/general-development/codebase-info/goob-reforged/reforged-modules.md @@ -62,11 +62,6 @@ flowchart TD style GoobShared stroke:#3498db GoobCommon[Content.Goobstation.Common] style GoobCommon stroke:#3498db - - ModuleServer[Content.Modules.Server] - style GoobShared stroke:#3498db - ModuleClient[Content.Modules.Client] - style GoobCommon stroke:#3498db subgraph CoreModules["Core Modules"] CoreServer @@ -97,8 +92,6 @@ flowchart TD CoreShared --> GoobShared CoreServer --> GoobServer CoreClient --> GoobClient - GoobServer --> ModuleServer - GoobClient --> ModuleClient ``` This means: @@ -137,8 +130,6 @@ flowchart TD style Lavaland stroke:#6b9bb3 Utils[Utils] style Utils stroke:#6b9bb3 - Modules[".Modules projects (end)"] - style Utils stroke:#6b9bb3 subgraph CustomModules["Custom Modules"] Goobstation @@ -150,10 +141,6 @@ flowchart TD Core --> Goobstation Core --> Lavaland Core --> Utils - - Goobstation --> Modules - Lavaland --> Modules - Utils --> Modules ``` The only exception for that convention are **library modules** that provide some general tools or API for other modules to use. @@ -174,8 +161,6 @@ flowchart TD style Lavaland stroke:#6b9bb3 Utils[Utils] style Utils stroke:#6b9bb3 - Modules[".Modules projects (end)"] - style Utils stroke:#6b9bb3 subgraph CustomModules["Custom Modules"] Goobstation @@ -188,9 +173,6 @@ flowchart TD Utils --> Goobstation Utils --> Lavaland - - Goobstation --> Modules - Lavaland --> Modules ``` ## Special module projects diff --git a/src/en/general-development/codebase-info/conventions/modules.md b/src/en/general-development/codebase-info/modules.md similarity index 94% rename from src/en/general-development/codebase-info/conventions/modules.md rename to src/en/general-development/codebase-info/modules.md index 5b05ca6..5d6916a 100644 --- a/src/en/general-development/codebase-info/conventions/modules.md +++ b/src/en/general-development/codebase-info/modules.md @@ -1,8 +1,4 @@ -# Modules Conventions - -```admonish warning "Attention: Placeholder!" -This section is a placeholder, pending an updated guide to be written -``` +# Modules ## What? @@ -100,4 +96,4 @@ When building your project: ## Verification -If you've done everything correctly, Content.Client or Content.Server should load your custom module on startup, with everything registering and initializing properly. \ No newline at end of file +If you've done everything correctly, Content.Client or Content.Server should load your custom module on startup, with everything registering and initializing properly.