Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/FarlandsModdingTeam/TerbinProyect/llms.txt

Use this file to discover all available pages before exploring further.

TerbinCommunicator is the central class in Terbin for inter-process communication over Windows named pipes. A single instance operates in either server or client mode and manages the full lifecycle of packet exchange: connection, background send/receive loops, request queuing, fragmentation, and timeout tracking. It implements IDisposable for safe resource cleanup.

Constructor

public TerbinCommunicator(
    bool pIsServer = false,
    CancellationToken pTokenCancellation = default,
    string pName = "TerbinPipe")
Creates a new communicator instance. When pIsServer is true, the instance immediately begins waiting for an incoming client connection asynchronously, starts background send/receive loops on connect, and fires OnNewClientConnect. When false, the pipe is a client that must call Connect() or TryConnect() manually.
pIsServer
bool
default:"false"
true to operate as the server side of the named pipe; false for client mode.
pTokenCancellation
CancellationToken
default:"default"
Cancellation token that stops background send and receive tasks when cancelled.
pName
string
default:"TerbinPipe"
The name of the named pipe. Both ends must use the same name to connect.
using var cts = new CancellationTokenSource();

var server = new TerbinCommunicator(
    pIsServer: true,
    pTokenCancellation: cts.Token,
    pName: "MyModPipe");

server.OnNewClientConnect += async () =>
{
    Console.WriteLine("Client connected!");
};

server.OnRecive += async (packet) =>
{
    // Handle incoming packet and return a response
    return InfoResponse.CreateSucces(packet.Head.IdRequest);
};

Properties

IsServer

public bool IsServer { get; }
Indicates whether this instance was initialized as the server side of the pipe. Set once in the constructor and cannot be changed afterwards.

MaximumResponseTime

public ushort MaximumResponseTime { get; set; }
Maximum number of seconds to wait for a response when using Communicate(). Defaults to TerbinProtocol.MAXIMUS_RESPONSE_TIME (8 seconds). If the timeout expires, Communicate() returns a PacketRequest whose Head.Status is CodeStatus.OverMaximumTime.
Call GiveProlong(idRequest) from inside a handler to extend the timeout for a long-running operation without changing this global value.

IsConnect

public bool IsConnect { get; }
Returns true when the underlying PipeStream reports it is currently connected. Read-only computed property backed directly by PipeStream.IsConnected.

Events

OnRecive

public event Func<PacketRequest, Task<InfoResponse?>>? OnRecive
Fires for every fully assembled, valid incoming packet. The delegate receives the reconstructed PacketRequest (with memory fragments already merged into Payload) and must return either:
  • An InfoResponse value — TerbinCommunicator automatically calls Reply() with the returned value.
  • null — suppresses the automatic reply. Use this when you have already replied via GiveResponse(), or when the packet is fire-and-forget.
Only one handler chain is invoked per packet. If multiple delegates are added to OnRecive, only the last registered delegate’s return value is used for the reply.

OnNewClientConnect

public event Func<Task>? OnNewClientConnect
Fires on the server side when a new client successfully connects and background tasks have started. Use this event to spin up a new server instance so additional clients can connect.
server.OnNewClientConnect += async () =>
{
    // Spawn a new server instance to accept the next client
    var nextServer = new TerbinCommunicator(
        pIsServer: true,
        pTokenCancellation: cts.Token,
        pName: "MyModPipe");
    nextServer.OnRecive += MyHandler;
    nextServer.OnNewClientConnect += ...; // chain again
};
This event only fires on server-mode instances (IsServer == true). Attaching it on a client has no effect.

Connection Methods

TryConnect

public async Task<bool> TryConnect()
Connects to the server pipe only if the pipe is not already connected and this instance is not a server. Useful as an idempotent “connect if needed” guard.
return
Task<bool>
true if a new connection was established; false if already connected, already a server, or connection failed.

Connect

public async Task<bool> Connect()
Calls ConnectAsync() on the underlying NamedPipeClientStream and starts the background send and receive loops. Must be called once on client-mode instances before sending or receiving packets.
return
Task<bool>
true if the connection succeeded; false if this instance is not a client pipe.

Send Methods

Communicate

public async Task<PacketRequest> Communicate(
    IdArray pActionMethod,
    byte[] pPayload,
    CodeStatus pStatus = CodeStatus.Execute,
    ushort? pId = null)
Sends a packet and blocks asynchronously until a response with the same request ID is received (or the timeout expires). This is the primary method when you need a confirmed result from the other side.
pActionMethod
IdArray
required
The action routing key identifying the handler to invoke on the other end.
pPayload
byte[]
required
The raw data body to transmit. Automatically fragmented if it exceeds TerbinProtocol.MAX_PLD (0xFFF0 bytes).
pStatus
CodeStatus
default:"CodeStatus.Execute"
The status code stamped into the outgoing packet header.
pId
ushort?
default:"null"
Explicit request ID. When null, a new ID is generated via MiniID.NewS.
return
Task<PacketRequest>
The response packet. Check Head.Status for success (CodeStatus.Succes) or error codes such as CodeStatus.OverMaximumTime.

Send

public async Task<PacketRequest?> Send(
    IdArray pActionMethod,
    byte[] pPayload,
    CodeStatus pStatus = CodeStatus.Execute,
    ushort? pId = null)
Sends a packet without waiting for a response (fire-and-forget). The response, if any, will arrive later via OnRecive.
pActionMethod
IdArray
required
The action routing key.
pPayload
byte[]
required
The data body to transmit.
pStatus
CodeStatus
default:"CodeStatus.Execute"
The status code to embed in the packet header.
pId
ushort?
default:"null"
Explicit request ID. When null, a new ID is generated via MiniID.NewS.
return
Task<PacketRequest?>
null on success. Returns an error PacketRequest (e.g., CodeStatus.OverMaximunPacket) only if fragmentation fails.

SendBytes

public async Task<PacketRequest?> SendBytes(
    IdArray pActionMethod,
    CodeStatus pStatus = CodeStatus.Execute,
    ushort? pId = null,
    params byte[] pPayload)
Convenience overload of Send that accepts the payload as a params byte[] for inline byte literals.
pActionMethod
IdArray
required
The action routing key.
pStatus
CodeStatus
default:"CodeStatus.Execute"
The status code to embed in the packet header.
pId
ushort?
default:"null"
Explicit request ID.
pPayload
params byte[]
required
Individual bytes passed inline as variadic arguments.
return
Task<PacketRequest?>
null on success or a fragmentation error packet.
// Send a single-byte command without constructing an array
await communicator.SendBytes(
    new IdArray((byte)CodeServices.Ping),
    CodeStatus.Execute,
    null,
    0x01);

Fragmentation Methods

These methods are public but are primarily called internally by the send pipeline. You may call them directly when building custom fragmentation logic.

HandleSendSigle

public async Task HandleSendSigle(
    IdArray pActionMethod,
    byte[] pPayload,
    ushort pIdRequest,
    CodeStatus pStatus)
Enqueues a single, unfragmented packet for transmission. Called automatically by Send / Communicate when pPayload.Length <= TerbinProtocol.MAX_PLD.
pActionMethod
IdArray
required
The action routing key for the packet.
pPayload
byte[]
required
Data that fits within one packet.
pIdRequest
ushort
required
The request ID to stamp into the header.
pStatus
CodeStatus
required
The status code for the header.

HandleSendFragment

public async Task<PacketRequest?> HandleSendFragment(
    IdArray pActionMethod,
    byte[] pPayload,
    ushort pIdRequest,
    CodeStatus pStatus)
Handles payloads that exceed TerbinProtocol.MAX_PLD by splitting them into chunks of TerbinProtocol.FRAGMENT_IN bytes (65,450 bytes, 0xFFAA), requesting a remote memory slot via SoliciteRequestMemory(), uploading each fragment via Load(), and then sending the final packet.
return
Task<PacketRequest?>
null on success. Returns an error packet if CheckExecution fails, memory cannot be allocated, or the packet count exceeds TerbinProtocol.FINAL_PACKET.

SoliciteRequestMemory

public async Task<PacketRequest> SoliciteRequestMemory()
Sends a CodeTerbinProtocol.Solicit packet to the remote end and awaits a memory slot ID in Payload[0]. Called internally before uploading fragments.
return
Task<PacketRequest>
Response packet. On success, Head.Status == CodeStatus.Succes and Payload[0] contains the allocated memory ID.

Load

public async Task<bool> Load(
    ushort pOrderRequest,
    byte pIdMemory,
    byte[] pPayload,
    ushort? pIdRequest = null)
Uploads a single fragment to a previously allocated remote memory slot. Throws if pPayload.Length >= TerbinProtocol.MAX_PLD.
pOrderRequest
ushort
required
The fragment’s position in the sequence (1-based, ushort.MaxValue reserved for the final packet).
pIdMemory
byte
required
The memory slot ID returned by SoliciteRequestMemory().
pPayload
byte[]
required
The fragment bytes. Must be smaller than TerbinProtocol.MAX_PLD.
pIdRequest
ushort?
default:"null"
The parent request ID. Auto-generated if null.
return
Task<bool>
Always true on success; throws on payload size violation.

Response and Queue Methods

GiveResponse

public void GiveResponse(PacketRequest pCapsule)
Manually resolves a pending Communicate() awaiter identified by pCapsule.Head.IdRequest. Use this from inside an OnRecive handler when you need to resolve the awaiter early (e.g., before an async sub-operation completes) and then return null to suppress the automatic reply.
pCapsule
PacketRequest
required
The packet to deliver as the response. Its Head.IdRequest must match the ID of a pending Communicate() call.

GiveProlong

public void GiveProlong(ushort pIdRequest)
Resets the timeout clock for a pending Communicate() call to another full MaximumResponseTime seconds. Call this from a long-running handler to prevent the caller from timing out.
pIdRequest
ushort
required
The request ID whose timeout should be extended.

Reply

public async Task Reply(InfoResponse pInfo)
Sends a response back to the caller by routing through the normal send pipeline using the fields of pInfo. This is called automatically when OnRecive returns a non-null InfoResponse, but you can call it directly for deferred replies.
pInfo
InfoResponse
required
The structured response to send. pInfo.IdRequest must match the original request’s ID.

addQueue

public async Task addQueue(PacketRequest pCapsule)
Directly enqueues a pre-built PacketRequest into the outbound send queue and signals the background send loop. Prefer Send / Communicate for normal usage.
pCapsule
PacketRequest
required
A fully constructed packet ready to be transmitted.
An overload builds and enqueues the packet from individual header fields, which is how the internal send pipeline calls it:
public async Task addQueue(
    ushort pOrderRequest,
    CodeStatus pStatus,
    IdArray pActionMethod,
    byte pIdMemory,
    byte[] pSectionPayload,
    ushort pIdRequest)
pOrderRequest
ushort
required
Fragment order (TerbinProtocol.ORDER_SINGLE for unfragmented, 1 for first fragment, ushort.MaxValue for last).
pStatus
CodeStatus
required
The status code to stamp into the header.
pActionMethod
IdArray
required
The action routing key for the packet.
pIdMemory
byte
required
Memory slot identifier. Use (byte)CodeTerbinMemory.NotAsign for non-fragmented packets.
pSectionPayload
byte[]
required
The raw bytes to carry as the packet payload.
pIdRequest
ushort
required
The request ID to stamp into the header.

Static Pipe Helpers

These static members simplify named-pipe creation with Terbin’s recommended settings (asynchronous, bidirectional, byte-transmission mode).

NewTerbinPipe

public static NamedPipeServerStream NewTerbinPipe { get; }
Creates and returns a new NamedPipeServerStream using default settings and the default pipe name "TerbinPipe". Each access creates a new stream.

NewClientTerbinPipe

public static NamedPipeClientStream NewClientTerbinPipe { get; }
Creates and returns a new NamedPipeClientStream using default settings and the default pipe name "TerbinPipe". Each access creates a new stream.

CreateServerPipe

public static NamedPipeServerStream CreateServerPipe(string pName = "TerbinPipe")
Creates a NamedPipeServerStream with PipeDirection.InOut, MaxAllowedServerInstances, PipeTransmissionMode.Byte, and PipeOptions.Asynchronous.
pName
string
default:"TerbinPipe"
The pipe name. Must match the name used by the client.

CreateClientPipe

public static NamedPipeClientStream CreateClientPipe(string pName = "TerbinPipe")
Creates a NamedPipeClientStream targeting the local machine (.) with PipeDirection.InOut and PipeOptions.Asynchronous.
pName
string
default:"TerbinPipe"
The pipe name. Must match the name used by the server.
// Build a custom server using the factory, then hand it to TerbinCommunicator
NamedPipeServerStream pipe = TerbinCommunicator.CreateServerPipe("PluginManagerPipe");
// pipe can be wrapped or inspected before being used

IDisposable

TerbinCommunicator implements IDisposable. Always dispose the instance (or use a using block) to close the pipe and release stream resources.
using var communicator = new TerbinCommunicator(false, cts.Token);
await communicator.Connect();
// ... use communicator ...
// Disposed automatically at end of scope

Build docs developers (and LLMs) love