Author: Dolfe
ULTRAKILL Multiplayer Utility is a lightweight, Steam-based peer-to-peer (P2P) utility library for creating multiplayer features in ULTRAKILL mods using BepInEx. It provides a high-level interface for lobby creation, network messaging, data synchronization, and designed to reduce boilerplate and the pain of setting up steamworks.
- Features
- Getting Started
- To Do
- Lobby Management
- Data Transmission
- Callbacks & Events
- Best Practices
- Ideas or Additions
- License
- Lightweight P2P communication built on Steam networking.
- Easy-to-use data sending (reliable, unreliable, etc.).
- Lobby creation, search, join, leave, and chat utils.
- Automatic serialization and deserialization of data.
- Custom type synchronization and event based observation.
- Optional support for using steam 480 (for testing), or mods.
- All LobbyManager methods are documented
Set lobby settings method
Add documentation for current_lobby
- ULTRAKILL installed via Steam.
- BepInEx (x64) installed and functioning.
- Your project or mod should reference
MultiplayerUtil.dll.
- Place
MultiplayerUtil.dllin theBepInEx/plugins/directory. - Ensure that any mod using this utility declares it as a dependency.
- Hook into
MultiplayerUtil.Callbacks.StartupCompletebefore invoking any Steam functionality.
You can easily manage Steam lobbies via static methods under MultiplayerUtil.LobbyManager:
// Create a new public lobby for 4 players
MU.LobbyManager.CreateLobby(
lobbyName: "New lobby",
maxPlayers: 4,
publicLobby: true,
cracked: false,
mods: false,
modIdentifier: ("YourMod", "Tag")
);// Search for existing lobbies using your identifier
var lobbies = await LobbyManager.FetchLobbies(("YourMod", "Tag"));// Join a lobby by its ulong ID
LobbyManager.JoinLobbyWithID(lobbyId);// Disconnect from current lobby
LobbyManager.Disconnect();MyClass exampleData = new MyClass();
exampleData.playerPos = ...;
LobbyManager.SendData(exampleData, SendMethod.UnreliableNoDelay);
// Make sure your class is System.SerializableAvailable send methods:
UnreliableFast but no guarantee of delivery or order, Ok at what it doesUnreliableNoDelayVery fast, no delay, no guarantee of it arriving or being in order, Good for fast changing data such as player positionsReliableGuaranteed delivery, but not necessarily ordered, Good for one time updatesReliableWithBufferingReliable and ensures they are sent in the right order, Good for bulk data sending
You can also:
LobbyManager.SendToLobbyOwner(data, SendMethod.Reliable);To observe incoming data:
ObserveManager.SubscribeToType(typeof(MyClass), out Callbacks.SenderUnityEvent myEvent);
myEvent.AddListener(payload =>
{
var obj = Data.Deserialize<MyClass>(payload.Item1);
var sender = payload.Item2;
});Hook into a range of events:
Callbacks.StartupCompleteCallbacks.TimeToSendImportantDataCallbacks.TimeToSendUnimportantDataCallbacks.OnLobbyMemberJoinedCallbacks.OnLobbyMemberLeaveCallbacks.OnChatMessageReceivedCallbacks.OnLobbyCreatedCallbacks.OnLobbyEnteredCallbacks.OnP2PConnectionFailed
Example:
Callbacks.OnChatMessageReceived.AddListener(msg => Debug.Log($"Chat: {msg}"));- Use
UnreliableNoDelayfor real-time data (e.g., positions). - Use
ReliableWithBufferingfor sequential state or synced actions. - Make sure the classes that contain the data you are sending are serialized
- Keep serialized classes small and avoid large strings.
- Use primitives (e.g.,
byte,short) when possible to reduce size. - Catch exceptions around network operations to avoid crashes.
Just message me on discord (@dolfelive) or create a issue/pull request
Dolfe AGPLish License Refer to LICENSE.md