![]() |
VOOZH | about |
dotnet add package CoreGame.SharedMeta.Core --version 0.29.2
NuGet\Install-Package CoreGame.SharedMeta.Core -Version 0.29.2
<PackageReference Include="CoreGame.SharedMeta.Core" Version="0.29.2" />
<PackageVersion Include="CoreGame.SharedMeta.Core" Version="0.29.2" />Directory.Packages.props
<PackageReference Include="CoreGame.SharedMeta.Core" />Project file
paket add CoreGame.SharedMeta.Core --version 0.29.2
#r "nuget: CoreGame.SharedMeta.Core, 0.29.2"
#:package CoreGame.SharedMeta.Core@0.29.2
#addin nuget:?package=CoreGame.SharedMeta.Core&version=0.29.2Install as a Cake Addin
#tool nuget:?package=CoreGame.SharedMeta.Core&version=0.29.2Install as a Cake Tool
<p align="center"> <img src="docs/images/banner.jpg" alt="SharedMeta Banner" width="800"/> </p>
<p align="center"> <strong>Unified Game Logic Engine — write once, run on client and server with deterministic replay</strong> </p>
<p align="center"> <a href="https://github.com/CoreGameIO/SharedMeta/releases"><img src="https://img.shields.io/github/v/release/CoreGameIO/SharedMeta?style=flat-square" alt="Release"></a> <a href="https://github.com/CoreGameIO/SharedMeta/blob/main/LICENSE"><img src="https://img.shields.io/github/license/CoreGameIO/SharedMeta?style=flat-square" alt="License"></a> <a href="https://unity.com"><img src="https://img.shields.io/badge/Unity-6+-black?style=flat-square&logo=unity" alt="Unity 6+"></a> <a href="https://dotnet.microsoft.com"><img src="https://img.shields.io/badge/.NET-8%20%7C%2010-512BD4?style=flat-square&logo=dotnet" alt=".NET"></a> <a href="https://learn.microsoft.com/dotnet/orleans/"><img src="https://img.shields.io/badge/Orleans-9-orange?style=flat-square" alt="Orleans"></a> </p>
<p align="center"> <a href="#quick-start-unity">Quick Start</a> • <a href="#what-you-can-build">Features</a> • <a href="#architecture">Architecture</a> • <a href="#key-concepts">Key Concepts</a> • <a href="#examples">Examples</a> • <a href="docs/GUIDE.md">Documentation</a> </p>
Write game logic once in C# — it runs on the server (Orleans grains) and replays on the client (Unity / .NET) with optimistic execution, automatic rollback, and desync detection.
Player profiles and progression — experience, levels, inventory, currencies. State is persisted per-player, changes are optimistic (instant on client, validated on server).
Turn-based and card games — shared game rules execute identically on both sides. Matchmaking, lobbies, multi-entity game sessions with deterministic random for shuffles and draws.
Cooperative and async multiplayer — one player's action modifies another player's state via cross-entity calls. Energy systems, trading, expeditions that span multiple entities.
Economy and resource systems — crafting, shops, timers, regeneration. Server-only random for loot drops and rewards (client can't predict or cheat). ServerPatch mode for bandwidth-efficient state diffs.
Reactive UI with change tracking — mark state fields with [Tracked], subscribe to typed change notifications. Push-based — no polling or snapshot diffs. Client-only, zero server overhead.
Live-ops and admin tools — server-side triggers push events to clients. Subscribers react to state changes. Hot-swappable transport (WebSocket or HTTP polling) and serializer (MemoryPack or MessagePack).
Query and inspection — check any entity's status without subscribing. Get brief info about other players, check if a game session is active, preview inventory — all via lightweight read-only calls with optional open access.
Add to Packages/manifest.json:
{
"dependencies": {
"com.coregame.sharedmeta": "https://github.com/CoreGameIO/SharedMeta.git#upm"
}
}
Tools > SharedMeta > Project Wizard in Unity menu.
Configure:
Meta.Shared, e.g. MyGame.Shared)PlayerProfile)The Dependencies section auto-detects and installs required packages (serializer, transport).
Use the three generation tabs:
| Tab | Generates | Output |
|---|---|---|
| Shared Project | State class, service interface, implementation, .csproj | Unity folder + .NET mirror with linked sources |
| Server Project | ASP.NET Core server with Orleans, transport, auth | Standalone .NET project |
| Client Scripts | MetaGameClient.cs MonoBehaviour + logger |
Unity Assets folder |
Start the server from Unity: Tools > SharedMeta > Server Runner — click Start.
Or from terminal:
cd Meta.Server
dotnet run
Press Play in Unity — MetaGameClient connects automatically.
Add NuGet packages to your .csproj:
<ItemGroup>
<PackageReference Include="CoreGame.SharedMeta.Core" Version="0.12.3" />
<PackageReference Include="CoreGame.SharedMeta.Client" Version="0.12.3" />
<PackageReference Include="CoreGame.SharedMeta.Serialization.MemoryPack" Version="0.12.3" />
<PackageReference Include="CoreGame.SharedMeta.Transport.SignalR.Client" Version="0.12.3" />
<PackageReference Include="CoreGame.SharedMeta.Generator" Version="0.12.3"
PrivateAssets="all" OutputItemType="analyzer" />
</ItemGroup>
Client transport packages have no server dependencies (no Orleans, no ASP.NET). Works with Godot (Godot.NET.Sdk), console apps, or any net8.0+ project.
For MessagePack SignalR protocol (optional, better performance):
<PackageReference Include="CoreGame.SharedMeta.Transport.SignalR.MessagePack" Version="0.12.3" />
dotnet run --project examples/CardGame_TheFool/CardGame.Server
dotnet run --project examples/CardGame_TheFool/CardGame.Client
┌─────────────────────────────────────────────────────────────────┐
│ Meta Layer (SharedMeta.Core, YourGame.Shared) │
│ Business logic: services, state, [MetaService] / [MetaMethod] │
│ Code generation: dispatchers, API clients, context injection │
└─────────────────────────────────────────────────────────────────┘
↕
┌──────────────────────────────────────────────────────────────────────────────┐
│ Middleware Layer (SharedMeta.Client, SharedMeta.Server) │
│ MetaContext, replay mechanism, execution modes │
│ Optimistic / Server / Local / CrossOptimistic / ServerPatch / ServerReplace │
└──────────────────────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────────┐
│ Serialization Layer (SharedMeta.Serialization.*) │
│ IMetaSerializer, MemoryPack / MessagePack implementations │
└─────────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────────┐
│ Transport Layer (SharedMeta.Transport.*) │
│ IConnection: SignalR WebSocket, HTTP long-polling, InProcess │
└─────────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────────┐
│ Server Backend (SharedMeta.Server.Core, Orleans) │
│ IMetaProvider<TState>, EntityGrain, SessionManager │
└─────────────────────────────────────────────────────────────────┘
Each layer depends only on the layers above it. Swap serializers, transports, or backends without changing game logic.
[MemoryPackable(GenerateType.VersionTolerant), MessagePackObject] // pick one or both
public partial class GameState : ISharedState
{
[Key(0), MemoryPackOrder(0)] public int Score { get; set; }
[Key(1), MemoryPackOrder(1)] public List<string> Items { get; set; } = new();
}
[MemoryPackable(GenerateType.VersionTolerant)] + [MemoryPackOrder(n)] — MemoryPack (default, zero-copy, safe field evolution)[MessagePackObject] + [Key(n)] — MessagePack (cross-platform, schema-flexible)Choose one serializer or use both. Orleans [GenerateSerializer]/[Id(n)] are not needed on game state — they're only used internally by the framework. The wizard configures this automatically.
[MetaService("IGameService")]
public interface IGameService
{
[MetaMethod(ExecutionMode.Optimistic)]
void AddItem(string itemId);
[MetaMethod(ExecutionMode.Server)]
void GrantReward(int amount);
}
[MetaServiceImpl(typeof(IGameService))]
public partial class GameServiceImpl : IGameService
{
// Context is injected by source generator
public void AddItem(string itemId)
{
State.Items.Add(itemId);
}
public void GrantReward(int amount)
{
// ServerRandom only generates on server; client replays from payload
int bonus = Context.ServerRandom!.Next(10);
State.Score += amount + bonus;
}
}
| Mode | Client | Server | Use Case |
|---|---|---|---|
| Optimistic | Executes immediately, rolls back on mismatch | Authoritative execution | UI-responsive actions (move, play card) |
| Server | Waits for server response | Executes with ServerRandom | Loot drops, matchmaking, secrets |
| Local | Local-only, no RPC | — | UI state, client-side filtering |
| CrossOptimistic | Executes on own state | Routes to target entity's grain | Cross-entity interactions |
| ServerPatch | Receives state diff from server | Sends patch instead of full state | Large state, bandwidth optimization |
| ServerReplace | Receives full state from server | Executes and sends complete state | Map generation, full reset, bulk state changes |
// Optimistic random — same algorithm & seed on both sides
int roll = Context.Random!.Next(6) + 1;
// Server random — generated on server, replayed on client
int loot = Context.ServerRandom!.Next(100);
Read-only calls to any entity without subscribing — useful for checking status, fetching brief info in multiplayer.
[MetaService(StateType = typeof(GameState))]
public interface IGameService : IMetaService
{
[MetaMethod(Mode = ExecutionMode.Query)] // queryable, respects access policy
bool IsActive();
[MetaMethod(Mode = ExecutionMode.Query, OpenAccess = true)] // queryable, anyone can call
string GetPublicInfo();
}
Client usage:
// No subscription — one-off network call
var queryApi = new GameServiceQueryApi(connection, serializer);
var info = await queryApi.EntityApi("entity-123").GetPublicInfoAsync();
// Already subscribed — executes locally, no network call
var gameApi = client.GetService<GameServiceApiClient>();
bool active = gameApi.IsActive(); // synchronous, reads local state
Read another entity's state from shared code — no explicit dependency injection needed:
var otherState = Context.GetState<PlayerProfile>("other-player-id");
if (otherState != null)
// use otherState.Level, otherState.Name, etc.
Recorded to replay payload for deterministic client replay.
var client = new MetaClient(connection, serializer);
await client.ConnectAsync("player-123", "entity-456");
// Generated typed API client
var gameApi = client.GetService<IGameServiceApiClient>();
gameApi.AddItem("sword_01");
// Subscribe to state changes
client.OnStateChanged += state => UpdateUI((GameState)state);
Open Tools > SharedMeta > Server Runner in the Unity menu. This opens an Editor window where you can:
-- 5001 for a different port)The server process survives Unity domain reloads (script compilation) and is automatically stopped when the Editor quits. The Runner tracks the process PID across reloads so it can re-attach to a running server.
cd YourGame.Server
dotnet run
By default the server listens on http://localhost:5000. Pass a port as argument: dotnet run -- 5001.
To test multiplayer locally (e.g. matchmaking), you need two Unity clients connecting to the same server:
All clients connect to the same server URL. The server handles session management and entity routing via Orleans grains.
SharedLibs/RiderPlugin at the CoreGameIO/SharedLibs repo is a Rider plugin that bridges [MetaService] interface methods with every method generated for them by SharedMeta.Generator:
[MetaMethod] walks both the interface itself and the generated *ApiClient, *EntityQueryApi, {I}EntityCaller, *EntityRecorder, *EntityReplayer, *LocalEntityCaller counterparts in one pass — call sites that go through any generated layer end up in the same results window.[MetaMethod] as an additional jump target alongside the standard one.The plugin matches by reading [GeneratedFromMetaMethod(typeof(IFoo), "Bar")] — emitted on every generated mirror by SharedMeta ≥ 0.16.0 — so there is no naming-convention guesswork. Older generated code is invisible to the plugin; regenerate against the new SharedMeta version.
Install: grab the pre-built zip from SharedLibs/RiderPlugin/dist/ (or build it yourself with ./gradlew buildPlugin), then Settings → Plugins → ⚙ → Install Plugin from Disk… in Rider.
Multiplayer turn-based card game with matchmaking lobby. Two players, attack/defend mechanics, trump suit. Demonstrates: Optimistic execution for card plays, Server mode for deck shuffle, lobby system with triggers, multi-entity game state.
Single-player dungeon exploration with procedural map generation. Two entities (Profile + Expedition) connected via cross-entity calls for energy/money economy. Demonstrates: CrossOptimistic for responsive movement, cross-entity SpendEnergy/AddMoney calls, [MetaInit] for procedural map generation with Context.Random, [Tracked] fields for push-based UI updates, [MetaConfig] for game balance, Query Calls to check expedition status without subscribing, generation mode choice (ServerReplace vs Optimistic) showing the difference between server-authoritative and client-predicted state. Full Unity client with runtime-generated UI.
| Directory | Description |
|---|---|
Runtime/Core/ |
Core framework: attributes, interfaces, meta context, random |
Runtime/Client/ |
Client-side dispatcher, message buffer, MetaClient |
Runtime/Transport/ |
Conditional transport assemblies: HTTP Polling, SignalR client |
Runtime/Serialization/ |
MemoryPack serialization |
Runtime/Orleans.Stubs/ |
Stub attributes for Unity (no Orleans dependency) |
Editor/ |
Project Wizard, Server Runner, pre-built source generator DLL |
src/SharedMeta.Generator/ |
Source generator: dispatchers, API clients, context injection |
src/SharedMeta.Server/ |
Server-side meta context and cross-entity calls |
src/SharedMeta.Server.Core/ |
EntityGrain, MetaProvider, file storage, session management |
src/SharedMeta.Orleans/ |
Orleans grain integration |
src/SharedMeta.Transport.SignalR/ |
SignalR transport — server (MetaHub) + MessagePack protocol |
src/SharedMeta.Transport.SignalR.Client/ |
SignalR client-only (JSON default, no server deps) |
src/SharedMeta.Transport.SignalR.MessagePack/ |
MessagePack protocol extension for SignalR |
src/SharedMeta.Transport.HttpPolling/ |
HTTP polling transport — server endpoints |
src/SharedMeta.Transport.HttpPolling.Client/ |
HTTP polling client-only (HttpClient, no server deps) |
src/SharedMeta.Auth/ |
JWT authentication middleware |
examples/ |
CardGame_TheFool, Expedition — full working examples |
tests/ |
Integration and unit tests |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. net11.0 net11.0 is compatible. |
| .NET Core | netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 netstandard2.1 is compatible. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 5 NuGet packages that depend on CoreGame.SharedMeta.Core:
| Package | Downloads |
|---|---|
|
CoreGame.SharedMeta.Server
Server-side meta context and cross-entity call infrastructure. |
|
|
CoreGame.SharedMeta.Server.Core
Server core: EntityGrain, MetaProviderBase, SessionManagerGrain, FileGrainStorage, persistence policies. |
|
|
CoreGame.SharedMeta.Serialization.MessagePack
MessagePack-based IMetaSerializer implementation using Orleans [Id(n)] attributes for schema resolution. |
|
|
CoreGame.SharedMeta.Orleans
Orleans integration: LobbyGrain, OrleansLobbyRequester, Orleans serialization configuration. |
|
|
CoreGame.SharedMeta.Serialization.MemoryPack
MemoryPack-based IMetaSerializer implementation for RPC payload serialization. Targets netstandard2.1 (Unity) and net10.0. |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.29.2 | 0 | 6/18/2026 |
| 0.29.1 | 5 | 6/18/2026 |
| 0.29.0 | 85 | 6/17/2026 |
| 0.28.0 | 196 | 6/16/2026 |
| 0.27.0 | 298 | 6/12/2026 |
| 0.26.6 | 417 | 6/7/2026 |
| 0.26.5 | 376 | 6/5/2026 |
| 0.26.4 | 371 | 6/5/2026 |
| 0.26.3 | 372 | 6/4/2026 |
| 0.26.2 | 370 | 6/3/2026 |
| 0.26.1 | 371 | 6/1/2026 |
| 0.26.0 | 381 | 6/1/2026 |
| 0.25.1 | 374 | 5/31/2026 |
| 0.25.0 | 382 | 5/31/2026 |
| 0.24.2 | 371 | 5/29/2026 |
| 0.24.1 | 376 | 5/28/2026 |
| 0.24.0 | 375 | 5/28/2026 |
| 0.23.1 | 382 | 5/23/2026 |
| 0.23.0 | 451 | 5/22/2026 |
| 0.22.0 | 436 | 5/16/2026 |