DotBoxD Quick Start Guide
New to DotBoxD? Start with Getting started and the Tutorials. This page is a deeper reference for the RPC channel layer (transports, codecs, generated registry).
Get up and running with DotBoxD in 5 minutes.
Reach for RPC channels when you want request/response host capabilities behind a shared contract: one C# interface compiles to a typed proxy plus dispatcher, so you get easy interop with no hand-written marshaling and no runtime reflection on the hot path (AOT-friendly, runs on Unity/IL2CPP) — the interface is the single source of truth. Prefer a query/event pipeline instead when the host should receive only server-side filtered and projected data over a one-way push, or pushdown when you need to collapse N round-trips into one server-side batch.
1. Define Your Service Contract
Create a shared library with your service interface:
// Shared/IMyService.cs
using DotBoxD.Services.Attributes;
using MessagePack;
[DotBoxDService]
public interface IMyService
{
Task<GreetingResponse> GreetAsync(GreetingRequest request, CancellationToken ct = default);
}
[MessagePackObject]
public class GreetingRequest
{
[Key(0)] public string Name { get; set; } = "";
}
[MessagePackObject]
public class GreetingResponse
{
[Key(0)] public string Message { get; set; } = "";
[Key(1)] public DateTime ServerTime { get; set; }
}
2. Implement the Server
// Server/MyService.cs
public class MyService : IMyService
{
public Task<GreetingResponse> GreetAsync(GreetingRequest request, CancellationToken ct)
{
return Task.FromResult(new GreetingResponse
{
Message = $"Hello, {request.Name}!",
ServerTime = DateTime.UtcNow
});
}
}
// Server/Program.cs
using DotBoxD.Services;
using DotBoxD.Services.Generated;
using DotBoxD.Codecs.MessagePack;
using DotBoxD.Transports.Tcp;
// A host turns every accepted connection into a peer.
// Each peer provides your service; the generated ProvideMyService extension wires it up.
await using var host = RpcHost
.Listen(new TcpServerTransport(5050), new MessagePackRpcSerializer())
.ForEachPeer(peer => peer.ProvideMyService(new MyService()));
await host.StartAsync();
Console.WriteLine("Server running on port 5050");
Console.ReadLine();
await host.StopAsync(); // DisposeAsync also stops the host
3. Create the Client
// Client/Program.cs
using DotBoxD.Services;
using DotBoxD.Services.Generated;
using DotBoxD.Codecs.MessagePack;
using DotBoxD.Transports.Tcp;
var transport = new TcpTransport("localhost", 5050);
await transport.ConnectAsync();
// Over a connection, an RpcPeer can both provide and get services.
// RejectInboundCalls signals a get-only intent (this side never serves calls).
await using var peer = RpcPeer
.Over(transport.Connection!, new MessagePackRpcSerializer(),
new RpcPeerOptions { RejectInboundCalls = true })
.Start();
var service = peer.GetMyService();
var response = await service.GreetAsync(new GreetingRequest { Name = "World" });
Console.WriteLine(response.Message); // "Hello, World!"
Console.WriteLine(response.ServerTime);
4. Run It
# Terminal 1: Start server
dotnet run --project Server
# Terminal 2: Run client
dotnet run --project Client
Project References
Your shared project needs these references:
These
ProjectReferences assume you are building inside the cloned DotBoxD repo; if you installed from NuGet (getting started), reference the packages instead — theDotBoxD.Servicespackage bundlesDotBoxD.Services.SourceGeneratoras an analyzer automatically, so you never add the generator as a standalone reference.
<ItemGroup>
<PackageReference Include="MessagePack" Version="2.5.187" />
<ProjectReference Include="../DotBoxD.Services/DotBoxD.Services.csproj" />
<ProjectReference Include="../DotBoxD.Services.SourceGenerator/DotBoxD.Services.SourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
Server and client projects reference:
- Your shared project
DotBoxD.Transports.TcpDotBoxD.Codecs.MessagePack
For process-local IPC, use the dedicated named-pipe package instead of TCP:
dotnet add package DotBoxD.Transports.NamedPipes --prerelease
using DotBoxD.Transports.NamedPipes;
var serverTransport = new NamedPipeServerTransport("my-app-rpc");
var clientTransport = new NamedPipeClientTransport("my-app-rpc");
What Gets Generated?
The source generator creates:
- Proxy (
MyServiceProxy) - Caller-side stub that serializes calls - Dispatcher (
MyServiceDispatcher) - Provider-side router that deserializes and invokes - Extensions (
peer.GetMyService(),peer.ProvideMyService(impl)) - Convenience methods onRpcPeer - Registry factory (
DotBoxDGenerated) - Typed proxy/dispatcher factory backed by generated delegates - Service catalog (
DotBoxDGenerated.Services) - Array-backedGeneratedServicedescriptors - Registration sink (
DotBoxDGenerated.RegisterServices(...)) - Direct generic calls for service/proxy registrations - Generated implementation sink (
DotBoxDGenerated.RegisterGeneratedServices(...)) - Direct generic calls for service/proxy/dispatcher registrations
You can use the generated factory directly when building framework-style APIs:
using DotBoxD.Services.Generated;
// CreateProxy takes an IRpcInvoker — pass an RpcPeer.
var proxy = DotBoxDGenerated.CreateProxy<IMyService>(peer);
var dispatcher = DotBoxDGenerated.CreateDispatcher<IMyService>(new MyService());
foreach (var service in DotBoxDGenerated.Services)
{
Console.WriteLine($"{service.ServiceType.Name}: {service.ProxyType.Name}");
}
For DI containers or host registries that need generic service/implementation pairs,
implement IDotBoxDServiceRegistrationSink and pass it to the generated callback:
using DotBoxD.Services.Generated;
public sealed class MySink : IDotBoxDServiceRegistrationSink
{
public void AddService<TService, TImplementation>()
where TService : class
where TImplementation : TService
{
// Register TService -> TImplementation in the host container.
}
}
DotBoxDGenerated.RegisterServices(new MySink());
If the host needs both generated implementation types, use
IDotBoxDGeneratedServiceRegistrationSink:
using DotBoxD.Services.Generated;
using DotBoxD.Services.Server;
public sealed class GeneratedSink : IDotBoxDGeneratedServiceRegistrationSink
{
public void AddService<TService, TProxy, TDispatcher>()
where TService : class
where TProxy : TService
where TDispatcher : IServiceDispatcher
{
// Register TService -> TProxy and TDispatcher without scanning assemblies.
}
}
DotBoxDGenerated.RegisterGeneratedServices(new GeneratedSink());
Next Steps
- Unity Integration Guide - Full Unity setup
- WebSocket Transport Guide - WebSocket setup for browsers and WebGL
- Generated Service Registry - Factory and registry usage
- Named Pipe Transport - Local IPC setup
- Samples - Working examples
- API Reference - Detailed API docs