Skip to content

Commit a4760d8

Browse files
sunnamed434claude
andcommitted
feat: example plugin for the Unturned Redist .Publicized package
Minimal RocketMod plugin (UnturnedRedistExample) that references the RocketModFix Rocket + Unturned/UnityEngine redist packages and, on load, reads a non-public Unturned field (Provider.isDedicatedUGCInstalled) via the .Publicized redist — proving publicized access compiles and runs. Includes a build CI workflow and Dependabot (NuGet + GitHub Actions). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d38930b commit a4760d8

6 files changed

Lines changed: 190 additions & 0 deletions

File tree

.github/dependabot.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
version: 2
2+
updates:
3+
# GitHub Actions used by the build workflow.
4+
- package-ecosystem: "github-actions"
5+
directory: "/"
6+
schedule:
7+
interval: "weekly"
8+
day: "monday"
9+
time: "09:00"
10+
timezone: "UTC"
11+
open-pull-requests-limit: 10
12+
reviewers:
13+
- "@me"
14+
assignees:
15+
- "@me"
16+
commit-message:
17+
prefix: "deps"
18+
include: "scope"
19+
labels:
20+
- "dependencies"
21+
- "github-actions"
22+
23+
# NuGet packages referenced by the plugin (RocketMod + redist).
24+
- package-ecosystem: "nuget"
25+
directory: "/"
26+
schedule:
27+
interval: "weekly"
28+
day: "monday"
29+
time: "09:00"
30+
timezone: "UTC"
31+
open-pull-requests-limit: 10
32+
reviewers:
33+
- "@me"
34+
assignees:
35+
- "@me"
36+
commit-message:
37+
prefix: "deps"
38+
include: "scope"
39+
labels:
40+
- "dependencies"
41+
- "nuget"
42+
allow:
43+
- dependency-type: "direct"

.github/workflows/build.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Build
2+
3+
on: [push, pull_request, workflow_dispatch]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v4
10+
11+
- uses: actions/setup-dotnet@v4
12+
with:
13+
dotnet-version: '8.0.x'
14+
15+
# Proves the plugin compiles against the .Publicized redist.
16+
- name: Build
17+
run: dotnet build -c Release
18+
19+
- name: Upload plugin DLL
20+
uses: actions/upload-artifact@v4
21+
with:
22+
name: UnturnedRedistExample
23+
path: bin/Release/net461/UnturnedRedistExample.dll
24+
if-no-files-found: error

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
bin/
2+
obj/
3+
*.user
4+
.vs/

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,57 @@
11
# RocketModFix.Unturned.Redist.Example
22

3+
A minimal [RocketMod](https://github.com/RocketModFix/RocketModFix) plugin showing how to consume the RocketModFix NuGet packages in an Unturned server plugin — in particular the [`RocketModFix.Unturned.Redist`](https://github.com/RocketModFix/RocketModFix.Unturned.Redist) **`.Publicized`** package, which lets a plugin read members that are non-public in the stock game.
4+
5+
> This is an example **consumer** plugin. Its own namespace is `UnturnedRedistExample` — i.e. what *you'd* write in your own plugin — it just **references** the RocketModFix packages.
6+
7+
## What it demonstrates
8+
9+
[`UnturnedRedistExamplePlugin.cs`](UnturnedRedistExamplePlugin.cs) is one class. On load it reads `SDG.Unturned.Provider.isDedicatedUGCInstalled` — a field that is **non-public** in the stock `Assembly-CSharp.dll` — and logs it:
10+
11+
```csharp
12+
protected override void Load()
13+
{
14+
// non-public in stock Unturned; reachable only via the .Publicized redist
15+
var ugcInstalled = Provider.isDedicatedUGCInstalled;
16+
Logger.Log($"[UnturnedRedistExample] Loaded. ... = {ugcInstalled} ...");
17+
}
18+
```
19+
20+
That single line is the whole point:
21+
22+
- It **compiles** only because the project references `RocketModFix.Unturned.Redist.Server.Publicized` (the publicizer rewrites that field to public).
23+
- It **runs** only because the `.csproj` sets `<AllowUnsafeBlocks>true</AllowUnsafeBlocks>`, which lets Unturned's Mono runtime skip the access check.
24+
25+
## Packages used
26+
27+
| Package | Why |
28+
| --- | --- |
29+
| [`RocketModFix.Rocket.Unturned`](https://www.nuget.org/packages/RocketModFix.Rocket.Unturned) | The RocketMod API — provides the `RocketPlugin` base class and `Logger`. |
30+
| [`RocketModFix.Unturned.Redist.Server.Publicized`](https://www.nuget.org/packages/RocketModFix.Unturned.Redist.Server.Publicized) | Unturned's server assemblies, **publicized** so the plugin can touch non-public members. Use [`…Server`](https://www.nuget.org/packages/RocketModFix.Unturned.Redist.Server) (without `.Publicized`) if you don't need that. |
31+
| [`RocketModFix.UnityEngine.Redist`](https://www.nuget.org/packages/RocketModFix.UnityEngine.Redist) | UnityEngine assemblies — required because Unturned types derive from `UnityEngine.MonoBehaviour`. |
32+
33+
See the [redist README](https://github.com/RocketModFix/RocketModFix.Unturned.Redist#using-a-publicized-package) for more on `.Publicized` and `AllowUnsafeBlocks`.
34+
35+
## Build
36+
37+
```bash
38+
dotnet build -c Release
39+
```
40+
41+
The plugin DLL lands in `bin/Release/net461/UnturnedRedistExample.dll`. CI builds it on every push — see [`.github/workflows/build.yaml`](.github/workflows/build.yaml).
42+
43+
## Verify it loads on a server
44+
45+
1. Install [RocketModFix](https://github.com/RocketModFix/RocketModFix#installation) on an Unturned dedicated server (copy the `Rocket.Unturned` folder into `Modules/`) and start it once so it generates `Servers/<ServerID>/Rocket/`.
46+
2. Copy the built `UnturnedRedistExample.dll` into `Servers/<ServerID>/Rocket/Plugins/` (the DLL goes directly in `Plugins/`, not a subfolder).
47+
3. Start the server and watch the console for:
48+
49+
```
50+
[UnturnedRedistExample] Loaded. Read non-public Provider.isDedicatedUGCInstalled = ... via the .Publicized redist.
51+
```
52+
53+
That line confirms two things at once: the plugin **loaded**, and the **publicized** member was read successfully **at runtime** (so `AllowUnsafeBlocks` did its job). If publicization were broken you'd instead get a `FieldAccessException` / "is inaccessible" error.
54+
55+
## License
56+
57+
MIT — see [LICENSE](LICENSE).

UnturnedRedistExample.csproj

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net461</TargetFramework>
5+
<!-- Required by any .Publicized redist: lets Mono skip the runtime access
6+
check on the originally-private members. See README. -->
7+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<!-- RocketMod API. Brings Rocket.Core (RocketPlugin, Logger) + Rocket.API. -->
12+
<PackageReference Include="RocketModFix.Rocket.Unturned" Version="4.23.1" />
13+
14+
<!-- Unturned's managed assemblies, publicized so the plugin can read
15+
non-public members. Floats to the latest stable build (the redist
16+
auto-updates each game patch); pin a version if you want reproducibility. -->
17+
<PackageReference Include="RocketModFix.Unturned.Redist.Server.Publicized" Version="3.*" />
18+
19+
<!-- UnityEngine assemblies. Needed because Unturned types (e.g. Provider)
20+
derive from UnityEngine.MonoBehaviour. -->
21+
<PackageReference Include="RocketModFix.UnityEngine.Redist" Version="2022.3.*" />
22+
23+
<!-- Lets `net461` build on Linux/macOS CI without a Windows targeting pack. -->
24+
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
25+
</ItemGroup>
26+
27+
</Project>

UnturnedRedistExamplePlugin.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Rocket.Core.Logging;
2+
using Rocket.Core.Plugins;
3+
using SDG.Unturned;
4+
5+
namespace UnturnedRedistExample
6+
{
7+
/// <summary>
8+
/// Minimal RocketMod plugin that demonstrates consuming the
9+
/// <c>RocketModFix.Unturned.Redist.Server.Publicized</c> NuGet package.
10+
///
11+
/// On load it reads <see cref="Provider.isDedicatedUGCInstalled"/> — a field
12+
/// that is non-public in the stock <c>Assembly-CSharp.dll</c>. That line only
13+
/// compiles because we reference the <c>.Publicized</c> redist (which rewrites
14+
/// the field to public), and only runs because the .csproj sets
15+
/// <c>AllowUnsafeBlocks</c> (which lets Mono skip the runtime access check).
16+
/// Seeing the log line in the server console is end-to-end proof that
17+
/// publicized access works.
18+
/// </summary>
19+
public class UnturnedRedistExamplePlugin : RocketPlugin
20+
{
21+
protected override void Load()
22+
{
23+
// isDedicatedUGCInstalled is non-public in stock Unturned — reachable
24+
// here only through the .Publicized redist.
25+
var ugcInstalled = Provider.isDedicatedUGCInstalled;
26+
27+
Logger.Log(
28+
$"[UnturnedRedistExample] Loaded. Read non-public " +
29+
$"Provider.isDedicatedUGCInstalled = {ugcInstalled} via the .Publicized redist.");
30+
}
31+
32+
protected override void Unload()
33+
{
34+
Logger.Log("[UnturnedRedistExample] Unloaded.");
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)