Skip to main content

Networking

Namespace: DeadworksManaged.Api

Send and intercept Source 2 network messages between server and clients.

NetMessages

Entry point for sending and hooking protobuf network messages.

Sending Messages

// Create a protobuf message
var msg = new CCitadelUserMsg_HudGameAnnouncement
{
TitleLocstring = "GAME OVER",
DescriptionLocstring = "The match has ended"
};

// Send to all players
NetMessages.Send(msg, RecipientFilter.All);

// Send to one player
NetMessages.Send(msg, RecipientFilter.Single(playerSlot));

Hooking Outgoing Messages (Server → Client)

// Hook before a message is sent to clients
NetMessages.HookOutgoing<CCitadelUserMsg_ChatMsg>(ctx =>
{
// ctx.Message — the protobuf message
// ctx.Recipients — modifiable recipient set
// ctx.MessageId — numeric message ID

// Modify recipients
ctx.Recipients.Remove(someSlot);

return HookResult.Handled;
});

Hooking Incoming Messages (Client → Server)

// Hook when server receives a message from a client
NetMessages.HookIncoming<CCitadelUserMsg_ChatMsg>(ctx =>
{
// ctx.Message — the protobuf message from client
// ctx.SenderSlot — who sent it
// ctx.MessageId — numeric message ID

return HookResult.Handled;
});

Unhooking

NetMessages.UnhookOutgoing<CCitadelUserMsg_ChatMsg>(myHandler);
NetMessages.UnhookIncoming<CCitadelUserMsg_ChatMsg>(myHandler);

Using the Attribute (Alternative)

For chat message hooks, you can also use the [NetMessageHandler] attribute:

[NetMessageHandler]
public HookResult OnChatMsgOutgoing(OutgoingMessageContext<CCitadelUserMsg_ChatMsg> ctx)
{
// Process outgoing chat
return HookResult.Handled;
}

RecipientFilter

Bitmask of player slots that should receive a message.

Static Members

MemberDescription
RecipientFilter.AllA filter targeting all 64 possible player slots
RecipientFilter.Single(int slot)A filter targeting exactly one player

Instance Methods

MethodDescription
Add(int slot)Adds a player slot
Remove(int slot)Removes a player slot
HasRecipient(int slot)Returns true if slot is included

Properties

PropertyTypeDescription
MaskulongRaw bitmask where bit i indicates slot i is included

Example: Custom Recipient List

var filter = new RecipientFilter();
foreach (var controller in Players.GetAll())
{
if (ShouldReceive(controller))
filter.Add(controller.EntityIndex);
}
NetMessages.Send(msg, filter);

Message Contexts

OutgoingMessageContext<T>

Carries a server→client message with destination recipients.

PropertyTypeDescription
MessageTThe protobuf message being sent
RecipientsRecipientFilterTarget players (modifiable)
MessageIdintNumeric network message ID

IncomingMessageContext<T>

Carries a client→server message with sender info.

PropertyTypeDescription
MessageTThe protobuf message from client
SenderSlotintPlayer slot of the sender
MessageIdintNumeric network message ID

NetMessageRegistry

Maps protobuf message types to network message IDs.

MethodReturnsDescription
GetMessageId<T>()intMessage ID for type T, or -1
GetMessageId(Type)intMessage ID for protobuf type, or -1
RegisterManual<T>(int)voidManually register type with specific ID

Common Message Types (Verified Sendable)

All of the following have been tested with NetMessages.Send() and confirmed to send without error:

Message TypeDescription
CCitadelUserMsg_ForceShopClosedForce-close the shop UI
CCitadelUserMsg_HudGameAnnouncementLarge HUD announcement with title and description
CCitadelUserMsg_KillStreakKill streak notification (uses PlayerPawn and NumKills)
CCitadelUserMsg_TeamMsgTeam message
CCitadelUserMsg_PlayerRespawnedPlayer respawn notification (uses PlayerPawn)
CCitadelUserMsg_TriggerDamageFlashDamage flash effect (uses EntindexFlashVictim, FlashValue, FlashType)
CCitadelUserMsg_PostProcessingAnimPost-processing animation
CCitadelUserMsg_MusicQueueMusic queue message
CCitadelUserMsg_AbilitiesChangedAbilities changed notification (uses PurchaserPlayerSlot)
CCitadelUserMsg_CameraControllerCamera controller message
CCitadelUserMsg_SetClientCameraAnglesForce client camera to specific angles
CCitadelUserMsg_ChatMsgChat message
Non-Sendable Messages

CCitadelUserMessage_GameOver has no registered message ID and will throw an error when sent.

Set Client Camera Angles

Forces a player's camera to look in a specific direction. This is the only confirmed working method for server-side camera control — schema writes and Teleport angles only affect the hero model, not the client camera.

NetMessages.Send(new CCitadelUserMsg_SetClientCameraAngles {
PlayerSlot = slot, // target player slot (int)
CameraAngles = new CMsgQAngle {
X = pitch, // vertical angle (negative = look up)
Y = yaw, // horizontal angle
Z = 0 // roll (usually 0)
}
}, RecipientFilter.Single(slot));
Third-Person Camera Offset

The game uses a right-shoulder third-person camera, offset ~35 units to the right of EyePosition. When calculating aim angles (e.g. for auto-aim), offset the source point along the character's right vector:

float yawRad = (float)(pawn.ViewAngles.Y * Math.PI / 180.0);
float rightX = (float)Math.Sin(yawRad);
float rightY = -(float)Math.Cos(yawRad);
var cameraSrc = new Vector3(
pawn.EyePosition.X + rightX * 35f,
pawn.EyePosition.Y + rightY * 35f,
pawn.EyePosition.Z
);
// Then calculate pitch/yaw from cameraSrc to target

HUD Announcement Example

var msg = new CCitadelUserMsg_HudGameAnnouncement
{
TitleLocstring = "ANNOUNCEMENT TITLE",
DescriptionLocstring = "Description text here"
};
NetMessages.Send(msg, RecipientFilter.All);

See Also