FishMMO-SharedUtility
A lightweight, pure C# / netstandard2.1 class library containing cross-cutting
utility code shared between the FishMMO Unity client and the FishMMO-Database
server project. The library has no dependency on UnityEngine, FishNet, or EF
Core, which lets it be referenced from both Unity and headless .NET tooling.
The post-build target copies FishMMO-SharedUtility.dll into
../FishMMO-Unity/Assets/Dependencies/ so Unity picks it up automatically as a
managed plugin.
Table of Contents
- Description
- Supported Platforms
- Architecture
- Key Components
- What Belongs Here
- Configuration
- Build
- Consuming from FishMMO-DB
- Flow Diagram
Description
FishMMO-SharedUtility is the lowest layer of the FishMMO C# stack. It is the
only assembly that is safe to reference from every other C# project in the
monorepo, including FishMMO-DB, FishMMO-AppHealthMonitor, the WebServers, and
the Unity managed scripts. Its scope is intentionally narrow:
- Pure-data validators (e.g. SRP-6a-friendly password and username rules).
- Generic, allocation-aware data structures (
CircularBuffer<T>,SetOnce<T>). - Math and bit utilities that aren't already in BCL.
- String / dictionary compression that wraps
System.IO.Compression. - A large set of primitive and collection extensions used throughout the codebase.
Supported Platforms
| Target | Status |
|---|---|
| .NET Standard 2.1 | Yes |
| Unity 6.3 LTS (IL2CPP / Mono) | Yes (via Assets/Dependencies/) |
| .NET 8.0 server projects | Yes |
| Requirement | Version |
|---|---|
| .NET SDK | 8.0+ |
| Language Version | latest |
| Nullable Reference Types | Enabled |
ZString |
2.6.0 |
Architecture
FishMMO-SharedUtility/
├── Authentication.cs # Username/password validators
├── CircularBuffer.cs # Allocation-light ring buffer
├── Configuration.cs # Flat key/value store with typed accessors + file I/O
├── FastActivator.cs # Expression-tree compiled object factory
├── MathHelper.cs # Vector/scalar/clamp helpers
├── IReference.cs # Reference-equality interface contract
├── RefWrapper.cs # Boxed reference wrapper for value types
├── SetOnce.cs # Write-once latch (no validator)
├── Compression/
│ ├── StringCompression.cs # GZip-based string round-trip
│ └── DictionaryCompression.cs
└── Extensions/
├── ArrayExtensions.cs
├── DirectoryExtensions.cs
├── EnumExtensions.cs
├── IListExtensions.cs
├── ProcessExtensions.cs
├── RandomExtensions.cs
├── StringExtensions.cs
├── TypeExtensions.cs
└── Primitive/ # Byte, Short, Int, Long, Float bit helpers
All public types live in the FishMMO.Shared namespace.
Key Components
Top-level utilities
| Type | Responsibility |
|---|---|
Authentication |
Static validators for usernames, passwords, character names — the same rules used by the LoginServer and account-creation flows. |
CircularBuffer<T> |
Circular doubly-linked list — an unbounded, thread-safe container for reference types. |
Configuration |
Thread-safe flat key/value store (Dictionary<string, string> behind a ReaderWriterLockSlim, keys case-insensitive) with typed accessors, key=value file I/O and environment-variable overrides. There is no node tree — an older description of this type said there was. |
FastActivator<TResult> |
Compiled-expression factory — faster than Activator.CreateInstance and avoids reflection per-call. |
MathHelper |
Numeric helpers (clamp, lerp, snapping). |
RefWrapper<T> |
Wraps a value type so reference-comparison works (used for parameter capture). |
SetOnce<T> |
Latch that allows exactly one assignment; throws thereafter (no validator parameter). |
IReference |
Marker interface for objects compared by reference. |
Compression
| Type | Responsibility |
|---|---|
StringCompression |
GZip compress/decompress for arbitrary UTF-8 strings. |
DictionaryCompression |
Compresses dictionaries of strings using a shared dictionary frame. |
Extensions
| Namespace | Highlights |
|---|---|
Extensions/*Extensions.cs |
ArrayExtensions, IListExtensions (binary search, swap, shuffle), StringExtensions (case-insensitive contains, hex), TypeExtensions (assignable-from cache), RandomExtensions (range pickers), DirectoryExtensions, EnumExtensions, ProcessExtensions. |
Extensions/Primitive/ |
ByteExtensions, ShortExtensions, IntExtensions, LongExtensions, FloatExtensions, and their bit-twiddling helpers (IntBitExtensions, LongBitExtensions). |
What Belongs Here
| Include | Do NOT Include |
|---|---|
| Validation helpers (Authentication, naming rules) | Anything that references UnityEngine |
| Pure math / string / collection utilities | Database / EF Core entities or services |
| Shared constants and enums | Networking code that depends on FishNet |
| Compression / allocation-free helpers | Logging — use FishMMO-Logger instead |
If a candidate utility imports UnityEngine, FishNet, or Microsoft.EntityFrameworkCore, it does not belong here.
Configuration
None of its own — the library is dependency-injection-free and runtime-config-free. It provides
the Configuration type that the game and servers use for theirs; a few of its guarantees are
load-bearing enough to state here, because the client's whole settings system rests on them.
Values are written and read with CultureInfo.InvariantCulture, and float/double use the
round-trip ("R") format so a stored value parses back to identical bits. Set<T> routes numeric
types through that path explicitly: a generic type parameter is invisible to overload resolution,
so a caller writing Set(key, someFloat) from inside another generic method reaches the generic
overload rather than the float one. This used to call value.ToString() — the current culture
— while every reader parsed invariantly, so on a comma-decimal locale 0.75f was stored as
"0,75" and read back as 75, the comma accepted as a digit-group separator.
A value that is present but unreadable yields the caller's defaultValue, not the type's
default. Every TryGet* returns false and assigns the supplied fallback; they previously left
result at 0/false, so a truncated write or a hand edit meant zero rather than the documented
default. Float and double parse with NumberStyles.Float, which rejects digit-group separators
instead of absorbing them.
File format. One key=value per line, UTF-8 without BOM (a BOM is stripped on read), # and
; comments, split on the first = so values may contain more. Malformed lines are skipped with
a warning rather than aborting the load. Remove deletes a key outright — callers that want
"absent" should use it rather than storing an empty string.
Environment overrides. Any read prefers FISHMMO_CONFIG_<KEY> (uppercased, ./:/- → _)
over the stored value, so operators can supply secrets without committing them. Overrides are
deliberately not included in GetKeys(), which reports what the file holds.
Build
dotnet build FishMMO-SharedUtility.slnx
The CopyToUnityDependencies MSBuild target copies the built DLL into
../../FishMMO-Unity/Assets/Dependencies/ after every successful build (both
Debug and Release).
Consuming from FishMMO-DB
FishMMO-DB references this project via a <ProjectReference>. No manual DLL
copying is needed for the database side.
<ProjectReference Include="../../FishMMO-SharedUtility/FishMMO-SharedUtility/FishMMO-SharedUtility.csproj" />
Flow Diagram
flowchart LR
SU[FishMMO-SharedUtility<br/>netstandard2.1]
SU -->|ProjectReference| DB[FishMMO-DB]
SU -->|ProjectReference| LOG[FishMMO-Logger]
SU -->|ProjectReference| AHM[FishMMO-AppHealthMonitor]
SU -->|ProjectReference| WEB[FishMMO-WebServers]
SU -->|DLL copy after build| UN[FishMMO-Unity<br/>Assets/Dependencies/]
UN --> CL[Unity Client / Editor]
UN --> SV[Unity Headless Server builds]