Overview

Michitai LAN Multiplayer is a comprehensive networking library for building local area network multiplayer games and applications. It provides both Unity and .NET implementations with support for multiple platforms including Windows, Linux, macOS, Android, and iOS.

Key Features

  • Cross-platform LAN discovery and communication
  • UDP broadcast for server discovery
  • TCP client-server architecture for reliable data transfer
  • Binary length-prefixed framing with NoDelay and SocketAsyncEventArgs I/O — built for 30-60 Hz messaging
  • Unreliable UDP state channel for per-frame synchronization (positions, transforms, inputs)
  • Transparent GZip payload compression
  • Queued request/response with fire-and-forget sends
  • Thread-safe data synchronization
  • JSON serialization for game data
  • Player authentication and management
  • Command system for terminal-like operations
Breaking change: the current release uses a binary length-prefixed frame protocol. It cannot communicate with builds that used the legacy string-delimiter protocol. Make sure all clients and servers run the same version.

Architecture

The library is organized into several key namespaces:

Michitai.Lan

Root namespace containing platform enumeration and core types.

  • EPlatform - Platform enumeration (Windows, Linux, MacOS, Android, iOS, Standalone, Mobile)

Michitai.Lan.Data

Data structures and JSON storage interfaces for game data management.

  • IJsonStorage - Interface for JSON serialization/deserialization
  • PlayerGameData - Player game data with JSON capabilities
  • PlayerCharacterData - Player character-specific data
  • PlayerWorldData - Player world-specific data

Michitai.Lan.Net

Core networking functionality for LAN operations.

  • Lan - Static class for IP address and broadcast mask operations
  • UDPBroadcast - UDP broadcast functionality for network discovery
  • UDPChannel - Unreliable UDP datagram channel for high-frequency state sync
  • Frame / FrameBuffer - Binary wire framing and streaming decoder
  • Compressor - GZip payload compression with configurable threshold
  • TCPClient - TCP client for reliable connections
  • TCPServer - TCP server for accepting client connections
  • TCPServerClient - Server-side representation of connected clients
  • PortRange - Port range management with predefined ranges
  • Message - Base message class
  • AppMessage - Application-level message
  • IdentifiedMessage - Message with client identification
  • LocatedMessage - Message with sender endpoint information

Michitai.Lan.Net.Multiplayer

High-level multiplayer game networking components.

  • Client - Multiplayer client for connecting to servers
  • Server - Multiplayer server for managing game sessions
  • BroadcastClient - Client-side broadcast discovery
  • BroadcastServer - Server-side broadcast announcement
  • Multiplayer - Main multiplayer coordination class

Michitai.Lan.Net.Multiplayer.Data

Data structures specific to multiplayer networking.

  • ClientGameData - Client-side game data
  • ServerGameData - Server-side game data with player management
  • ServerClientGameData - Combined server client data
  • Credentials - Player authentication credentials
  • ServerInfo - Server information for discovery
  • LocatedServerInfo - Discovered server information
  • ServerInfoStack - Stack of discovered servers
  • MultiplayerGamesData - Multiplayer game metadata

Michitai.Lan.Net.Multiplayer.Commands

Command system for terminal-like operations.

  • Command - Command structure and execution
  • Terminal - Terminal interface for command input

Directory Structure

.NET Implementation (dotnet/)

dotnet/
├── Data/
│   ├── IJsonStorage.cs          # JSON storage interface
│   ├── PlayerGameData.cs        # Player game data
│   ├── PlayerCharacterData.cs   # Player character data
│   └── PlayerWorldData.cs       # Player world data
├── Net/
│   ├── Lan.cs                   # LAN IP operations
│   ├── UDPBroadcast.cs          # UDP broadcast
│   ├── UDPChannel.cs            # Unreliable UDP state channel
│   ├── Frame.cs                 # Binary frame codec + streaming decoder
│   ├── Compression.cs           # GZip payload compression
│   ├── TCPClient.cs             # TCP client
│   ├── TCPServer.cs             # TCP server
│   ├── TCPServerClient.cs       # Server client representation
│   ├── PortRange.cs             # Port range management
│   ├── Message.cs               # Base message
│   ├── AppMessage.cs            # Application message
│   ├── IdentifiedMessage.cs     # Identified message
│   ├── LocatedMessage.cs        # Located message
│   ├── Multiplayer/
│   │   ├── Client.cs            # Multiplayer client
│   │   ├── Server.cs            # Multiplayer server
│   │   ├── BroadcastClient.cs   # Broadcast discovery client
│   │   ├── BroadcastServer.cs   # Broadcast announcement server
│   │   ├── Multiplayer.cs       # Main multiplayer class
│   │   ├── Data/                # Multiplayer data structures
│   │   │   ├── ClientGameData.cs
│   │   │   ├── ServerGameData.cs
│   │   │   ├── ServerClientGameData.cs
│   │   │   ├── Credentials.cs
│   │   │   ├── ServerInfo.cs
│   │   │   ├── LocatedServerInfo.cs
│   │   │   ├── ServerInfoStack.cs
│   │   │   └── MultiplayerGamesData.cs
│   │   └── Commands/            # Command system
│   │       ├── Command.cs
│   │       └── Terminal.cs
│   └── Debugging/               # Debugging utilities
├── EPlatform.cs                 # Platform enumeration
├── michitai-lan.csproj          # Project file
└── michitai-lan.sln             # Solution file

Unity Implementation (unity/)

unity/
├── Data/
│   ├── IJsonStorage.cs          # JSON storage interface
│   └── JsonStorage.cs           # JSON storage implementation
├── Net/
│   ├── Lan.cs                   # LAN IP operations
│   ├── UDPBroadcast.cs          # UDP broadcast
│   ├── UDPChannel.cs            # Unreliable UDP state channel
│   ├── Frame.cs                 # Binary frame codec + streaming decoder
│   ├── Compression.cs           # GZip payload compression
│   ├── TCPClient.cs             # TCP client
│   ├── TCPServer.cs             # TCP server
│   ├── TCPServerClient.cs       # Server client representation
│   ├── PortRange.cs             # Port range management
│   ├── Message.cs               # Base message
│   ├── AppMessage.cs            # Application message
│   ├── IdentifiedMessage.cs     # Identified message
│   ├── LocatedMessage.cs        # Located message
│   ├── Multiplayer/
│   │   ├── Client.cs            # Multiplayer client
│   │   ├── Server.cs            # Multiplayer server
│   │   ├── BroadcastClient.cs   # Broadcast discovery client
│   │   ├── BroadcastServer.cs   # Broadcast announcement server
│   │   ├── MobileBroadcastClient.cs   # Mobile broadcast client
│   │   ├── MobileBroadcastServer.cs   # Mobile broadcast server
│   │   ├── Multiplayer.cs       # Main multiplayer class
│   │   ├── Data/                # Multiplayer data structures
│   │   │   ├── ClientGameData.cs
│   │   │   ├── ServerGameData.cs
│   │   │   ├── ServerClientGameData.cs
│   │   │   ├── Credentials.cs
│   │   │   ├── ServerInfo.cs
│   │   │   ├── LocatedServerInfo.cs
│   │   │   ├── ServerInfoStack.cs
│   │   │   └── MultiplayerGamesData.cs
│   │   ├── Commands/            # Command system
│   │   │   ├── Command.cs
│   │   │   └── Terminal.cs
│   │   └── Chat/                # Chat functionality
│   │       ├── ChatClient.cs
│   │       └── ChatServer.cs
│   └── Debugging/               # Debugging utilities
└── EPlatform.cs                 # Platform enumeration

Platform Support

The library supports multiple platforms through the EPlatform enumeration:

Platform Value Description
Windows 1 Windows desktop platform
Linux 2 Linux desktop platform
MacOS 4 macOS desktop platform
Standalone 7 All desktop platforms (Windows + Linux + macOS)
Android 8 Android mobile platform
iOS 16 iOS mobile platform
Mobile 24 All mobile platforms (Android + iOS)

Platform flags can be combined using bitwise operations for multi-platform support.

Key Components

LAN Operations

The Lan static class provides utilities for working with local network addresses:

// Get all local IPv4 addresses
IPAddress[] addresses = Lan.LocalIPv4Addresses(EPlatform.Standalone);

// Get broadcast masks for discovery
IPAddress[] masks = Lan.LocalIPv4Masks(EPlatform.Standalone);

// Try-get pattern with error handling
if (Lan.TryGetLocalIPv4Addresses(EPlatform.Standalone, out var ips)) {
    // Use addresses
}

UDP Broadcast

UDPBroadcast enables network discovery through UDP broadcasting:

// Create broadcast client
var broadcast = new UDPBroadcast(ipAddress, port);

// Send discovery message
await broadcast.SendAsync(broadcastAddress, portRange, message);

// Receive responses
var response = await broadcast.ReceiveAsync(timeoutMs);

// Cleanup
broadcast.Stop();

Multiplayer Server

The Server class manages multiplayer game sessions:

// Create server
var server = new Server(
    name: "MyGameServer",
    serverGameData: gameData,
    ip: ipAddress,
    port: 7777
);

// Subscribe to events
server.OnClientConnected += (id) => Console.WriteLine($"Client {id} connected");
server.OnClientDisconnected += (id) => Console.WriteLine($"Client {id} disconnected");
server.OnRequest += (msg) => HandleRequest(msg);

// Start server
server.Start();

// Register new player
var credentials = server.RegisterNewPlayer(playerData);

// Log in player
server.LogInPlayer(clientId, credentials);

// Send response
server.Response(identifiedMessage);

// Stop server
server.Stop();

Multiplayer Client

The Client class connects to multiplayer servers:

// Create client
var client = new Client(
    clientGameData: myData,
    gameData: playerData,
    ip: serverIp,
    port: 7777
);

// Subscribe to events
client.OnResponse += (msg) => HandleResponse(msg);
client.OnDisconnected += () => Console.WriteLine("Disconnected");

// Start client
client.Start();

// Send request
if (client.CanRequest) {
    client.Request(message);
}

// Stop client
client.Stop();

Port Management

PortRange provides predefined port ranges and port management:

// Use predefined ranges
var broadcastPorts = PortRange.Broadcast;        // 64512-65535
var dynamicPorts = PortRange.Dynamic;            // 49152-65535
var registeredPorts = PortRange.Registered;      // 1024-49151

// Create custom range
var customRange = new PortRange(8000, 9000);

// Get port store for random port selection
var store = customRange.RangeStore;
int randomPort = store.RandomPort;  // Gets and removes a random port

Data Serialization

All game data implements IJsonStorage for JSON serialization:

// Create player data
var playerData = new PlayerGameData();

// Serialize object to JSON
playerData.Set(myPlayerObject);

// Get JSON string
string json = playerData.Json;

// Deserialize JSON to object
var player = playerData.Get<MyPlayerType>();

// Thread-safe access
lock (playerData) {
    // Access data safely
}

Thread Safety

The library implements thread-safe operations using locks:

  • All data structures use private lock objects for synchronization
  • JSON storage properties are thread-safe with lock guards
  • Server client collections are protected with locks
  • Credentials and ID properties are thread-safe

Message Flow

The library uses a message-based communication pattern:

  1. Discovery Phase: Servers broadcast their presence via UDP
  2. Connection Phase: Clients connect via TCP to discovered servers
  3. Authentication Phase: Players authenticate with credentials
  4. Game Phase: Request/response over TCP for reliable data; UDP datagrams for per-frame state
  5. Disconnection Phase: Clean disconnect with resource cleanup

Transport Protocol

The current release uses a binary wire protocol on two channels:

Reliable Channel (TCP)

Every message is sent as a frame: [4-byte little-endian payload length][1-byte flags][UTF-8 payload]. Flag 0x01 marks a GZip-compressed payload. The streaming decoder handles fragmented reads and multiple frames per read, and rejects frames announcing more than Frame.MaxPayloadSize (32 MB by default). Sockets run with NoDelay and SocketAsyncEventArgs for low-latency, low-allocation I/O, and writes are queued so concurrent sends never interleave.

Use this channel for anything that must arrive: commands, login, world data, request/response traffic.

State Channel (UDP)

UDPChannel carries datagrams of [1-byte flags][payload] — unreliable, unordered, no retransmission. It binds to the same port number as the TCP server (UDP and TCP port spaces are independent), so a discovered ServerInfo endpoint already tells clients where to send state.

Use this channel for transient per-frame values at 30-60 Hz where a dropped packet is immediately superseded by the next one — positions, transforms, inputs. Keep datagrams under UDPChannel.SafeDatagramSize (1472 bytes) to avoid IP fragmentation.

// Client -> server, each frame
Multiplayer.SendState(new Message(JsonUtility.ToJson(playerTransform)));

// Server: receive state from clients
Multiplayer.OnState += (IPEndPoint from, Message msg) => {
    var transform = JsonUtility.FromJson(msg.GetMessage);
    ApplyState(from, transform);
};

// Server -> all clients that have sent state
Multiplayer.BroadcastState(new Message(JsonUtility.ToJson(worldSnapshot)));

// Server -> one client
Multiplayer.SendState(clientEndPoint, new Message(JsonUtility.ToJson(playerSnapshot)));

Compression

Payloads of 256 bytes or more are GZip-compressed automatically when compression shrinks them. Tune with Compressor.Enabled and Compressor.Threshold.

Request Semantics

Client.Request() implements one-at-a-time request/response: calls made while awaiting a response are queued (Client.PendingRequests) and flushed in order. Client.Send() bypasses the queue for fire-and-forget TCP messages. Backpressure is observable via TCPClient.PendingWrites and TCPServer.PendingWrites.

Usage Examples

Example 1: Complete Server Setup

This example shows how to set up a complete multiplayer server with discovery, player registration, and game state synchronization:

// Initialize platform
#if UNITY_STANDALONE
    EPlatform platform = EPlatform.Standalone;
#elif UNITY_ANDROID
    EPlatform platform = EPlatform.Android;
#endif

// Configure server
Multiplayer.Name = "Car Driving Multiplayer";
IPAddress[] ips = null;
bool success = Lan.TryGetLocalIPv4Addresses(platform, out ips);
Multiplayer.IpAddress = success ? ips[0] : IPAddress.Any;

// Create server data
ServerGameData serverData = new ServerGameData(Guid.NewGuid().ToString());

// Start server with broadcast discovery
Multiplayer.StartServer(
    platform: platform,
    serverGameData: serverData,
    processMessage: (LocatedMessage msg) => {
        // Process broadcast discovery requests
        Command command = JsonUtility.FromJson(msg.Message.Message);
        if (msg.Message.Name == "car-driving-multiplayer" && 
            command == Command.New("get-server-info"))
        {
            ServerInfo info = new ServerInfo(
                Multiplayer.Server.IPEndPoint.Port,
                Multiplayer.Name,
                serverData.ServerID,
                serverData.Clients.Length
            );
            return new AppMessage(1, "car-driving-multiplayer", 
                JsonUtility.ToJson(Command.New("server-info").Arg(JsonUtility.ToJson(info))));
        }
        return new AppMessage(1, "car-driving-multiplayer", "denied");
    },
    receiveRequestsDelayMilliseconds: 500
);

// Handle server requests
Multiplayer.Server.OnRequest += (identifiedMessage) => {
    Terminal terminal = JsonUtility.FromJson(identifiedMessage.Message.GetMessage);
    Command[] commands = terminal.Commands;
    Terminal response_terminal = Terminal.New();
    
    foreach (Command cmd in commands)
    {
        if (cmd == Command.New("get-server-id"))
        {
            response_terminal.Next("server-id")
                .Arg($"{Multiplayer.Server.PublicServerData.ServerID}");
        }
        else if (cmd == Command.New("register"))
        {
            JsonStorage data = new JsonStorage(cmd.Arguments[1]);
            Credentials credentials = Multiplayer.Server.RegisterNewPlayer(data);
            response_terminal.Next("credentials")
                .Arg($"{JsonUtility.ToJson(credentials)}");
        }
        else if (cmd == Command.New("log-in"))
        {
            Credentials credentials = JsonUtility.FromJson(cmd.Arguments[1]);
            if (Multiplayer.Server.Contains(credentials))
            {
                Multiplayer.Server.LogInPlayer(identifiedMessage.ID, credentials);
                response_terminal.Next("log-in-successful");
            }
            else
            {
                response_terminal.Next("log-in-error");
            }
        }
        else if (cmd == Command.New("set-game-data"))
        {
            bool success = Multiplayer.Server.TryGetLoggedInPlayerPrivateData(
                identifiedMessage.ID, out ServerClientGameData data);
            if (success)
            {
                data.Data.Json = cmd.Arguments[1];
            }
        }
        else if (cmd == Command.New("get-server-data"))
        {
            response_terminal.Next("server-data")
                .Arg($"{JsonUtility.ToJson(Multiplayer.Server.PublicServerData)}");
        }
    }
    
    Multiplayer.Server.Response(new IdentifiedMessage(
        new Message(JsonUtility.ToJson(response_terminal)), 
        identifiedMessage.ID
    ));
};

Example 2: Complete Client Setup

This example shows how to set up a client that discovers servers, connects, and synchronizes game state:

// Initialize platform
#if UNITY_STANDALONE
    EPlatform platform = EPlatform.Standalone;
#elif UNITY_ANDROID
    EPlatform platform = EPlatform.Android;
#endif

// Start broadcast discovery in main menu
Multiplayer.StartBroadcastClient(
    platform: platform,
    request: new AppMessage(1, "car-driving-multiplayer", 
        JsonUtility.ToJson(Command.New("get-server-info"))),
    onReceiveResponse: (LocatedMessage response) => {
        if (response.Message.Name == "car-driving-multiplayer")
        {
            Command command = JsonUtility.FromJson(response.Message.Message);
            if (command == Command.New("server-info"))
            {
                ServerInfo serverInfo = JsonUtility.FromJson(command.Arguments[1]);
                // Store server info for user selection
                discoveredServers.Add(new LocatedServerInfo(serverInfo, response.IPEndPoint));
            }
        }
    },
    receiveResponsesMilliseconds: 5000,
    repeatAfterMilliseconds: 5000
);

// User selects server - connect to it
Multiplayer.IpAddress = selectedServer.IPEndPoint.Address;
Multiplayer.Port = selectedServer.ServerInfo.Port;

// Start client
Multiplayer.StartClient();

// Handle responses
Multiplayer.Client.OnResponse += (message) => {
    Terminal terminal = JsonUtility.FromJson(message.GetMessage);
    Command[] commands = terminal.Commands;
    
    foreach (Command cmd in commands)
    {
        if (cmd == Command.New("server-id"))
        {
            server_id = cmd.Arguments[1];
            // Register with game data
            GameData data = new GameData(new CharacterData(...));
            Multiplayer.Client.GameData = new JsonStorage(JsonUtility.ToJson(data));
            Multiplayer.Client.Request(new Message(
                JsonUtility.ToJson(Terminal.New("register").Arg(JsonUtility.ToJson(data)))
            ));
        }
        else if (cmd == Command.New("credentials"))
        {
            Credentials credentials = JsonUtility.FromJson(cmd.Arguments[1]);
            Multiplayer.Client.ClientData = new ClientGameData(server_id, credentials);
            Multiplayer.Client.Request(new Message(
                JsonUtility.ToJson(Terminal.New("log-in").Arg(JsonUtility.ToJson(credentials)))
            ));
        }
        else if (cmd == Command.New("log-in-successful"))
        {
            Multiplayer.Client.Request(new Message(
                JsonUtility.ToJson(Terminal.New("get-server-data"))
            ));
        }
        else if (cmd == Command.New("server-data"))
        {
            ServerGameData server_data = JsonUtility.FromJson(cmd.Arguments[1]);
            Multiplayer.Client.ServerData = server_data;
            // Update remote players
            UpdateRemotePlayers(server_data.Clients);
        }
    }
};

// Game loop - send player state at ~30-60 Hz over the unreliable UDP channel.
// Requests that must arrive (login, world data) still go through Client.Request over TCP.
private void Update()
{
    if (Multiplayer.IsClient)
    {
        if (Time.time >= updateRate + lastUpdate)
        {
            lastUpdate = Time.time;
            
            // Per-frame state: fast, no head-of-line blocking, latest-wins
            Multiplayer.Client.SendState(new Message(
                JsonUtility.ToJson(new GameData(characterData))
            ));
        }
    }
}

Example 3: Game Data Serialization

This example shows how to create serializable game data structures:

// Define serializable game data classes
[Serializable]
public class GameData
{
    public CharacterData character_data;
    public WorldData world_data;
}

[Serializable]
public class CharacterData
{
    public float position_x;
    public float position_y;
    public float position_z;
    public float rotation_x;
    public float rotation_y;
    public float rotation_z;
    public bool lights_on;
    public int car_index;
    // Add more fields as needed
}

[Serializable]
public class WorldData
{
    public string world_name;
    public int level_index;
    // Add world-specific data
}

// Use JSON storage for serialization
PlayerGameData playerData = new PlayerGameData();
playerData.Set(new GameData(characterData, worldData));

// Get JSON string
string json = playerData.Json;

// Deserialize back
GameData data = playerData.Get<GameData>();

Example 4: Command Processing Pattern

This example shows the recommended pattern for processing commands on the server:

// Server-side command processing
Multiplayer.Server.OnRequest += (identifiedMessage) => {
    Terminal terminal = JsonUtility.FromJson<Terminal>(identifiedMessage.Message.GetMessage);
    Command[] commands = terminal.Commands;
    Terminal response_terminal = Terminal.New();
    
    foreach (Command cmd in commands)
    {
        switch (cmd.Arguments[0])
        {
            case "/get-server-id":
                // Return server ID
                response_terminal.Next("server-id")
                    .Arg($"{Multiplayer.Server.PublicServerData.ServerID}");
                break;
                
            case "/register":
                // Register new player
                JsonStorage data = new JsonStorage(cmd.Arguments[1]);
                Credentials credentials = Multiplayer.Server.RegisterNewPlayer(data);
                response_terminal.Next("credentials")
                    .Arg($"{JsonUtility.ToJson(credentials)}");
                break;
                
            case "/log-in":
                // Authenticate player
                Credentials credentials = JsonUtility.FromJson<Credentials>(cmd.Arguments[1]);
                if (Multiplayer.Server.Contains(credentials))
                {
                    Multiplayer.Server.LogInPlayer(identifiedMessage.ID, credentials);
                    response_terminal.Next("log-in-successful");
                }
                else
                {
                    response_terminal.Next("log-in-error");
                }
                break;
                
            case "/set-game-data":
                // Update player game state
                bool success = Multiplayer.Server.TryGetLoggedInPlayerPrivateData(
                    identifiedMessage.ID, out ServerClientGameData data);
                if (success)
                {
                    data.Data.Json = cmd.Arguments[1];
                }
                break;
                
            case "/get-server-data":
                // Return all players' public data
                response_terminal.Next("server-data")
                    .Arg($"{JsonUtility.ToJson(Multiplayer.Server.PublicServerData)}");
                break;
                
            default:
                // Unknown command
                response_terminal.Next("error").Arg("Unknown command");
                break;
        }
    }
    
    // Send response
    Multiplayer.Server.Response(new IdentifiedMessage(
        new Message(JsonUtility.ToJson(response_terminal)), 
        identifiedMessage.ID
    ));
};

Example 5: Thread-Safe Data Access

This example shows how to safely access data in a multi-threaded environment:

// All data structures use locks for thread safety
// Example from ServerGameData
private readonly object _clients_lock;
private ServerClientGameData[] _clients;

public ServerClientGameData[] Clients
{
    get
    {
        lock (_clients_lock)
        {
            return _clients;
        }
    }
    set
    {
        lock (_clients_lock)
        {
            _clients = value;
        }
    }
}

// Safe access pattern
public void AddPlayer(ServerClientGameData player)
{
    lock (_clients_lock)
    {
        ServerClientGameData[] players = new ServerClientGameData[_clients.Length + 1];
        
        for (int i = 0; i < _clients.Length; i++)
        {
            players[i] = _clients[i];
        }
        
        players[_clients.Length] = player;
        _clients = players;
    }
}

// Usage in game code
lock (serverData)
{
    // Access data safely
    var players = serverData.Clients;
    foreach (var player in players)
    {
        // Process player data
    }
}

Example 6: Platform-Specific Initialization

This example shows how to handle different platforms:

// Platform detection
private EPlatform GetPlatform()
{
#if UNITY_STANDALONE_WIN
    return EPlatform.Windows;
#elif UNITY_STANDALONE_LINUX
    return EPlatform.Linux;
#elif UNITY_STANDALONE_OSX
    return EPlatform.MacOS;
#elif UNITY_ANDROID
    return EPlatform.Android;
#elif UNITY_IOS
    return EPlatform.IOS;
#else
    return EPlatform.Standalone;
#endif
}

// Platform-specific IP retrieval
EPlatform platform = GetPlatform();
IPAddress[] addresses = Lan.LocalIPv4Addresses(platform);

// Platform-specific broadcast
if ((platform & EPlatform.Mobile) != 0)
{
    // Use mobile broadcast implementation
    // MobileBroadcastClient / MobileBroadcastServer
}
else
{
    // Use desktop broadcast implementation
    // BroadcastClient / BroadcastServer
}

Building

.NET Project

# Build the project
dotnet build dotnet/michitai-lan.csproj

# Run tests (if available)
dotnet test

Unity Project

Copy the Unity folder contents to your Unity project's Assets folder. The Unity version uses Unity's JSON serialization instead of System.Text.Json.

Dependencies

.NET Version

  • .NET Framework 4.8 or .NET 6.0+
  • System.Net.Sockets
  • System.Threading
  • No external JSON dependency — a built-in serializer shim (DataContractJsonSerializer) is included

Unity Version

  • Unity 2019.4 or later
  • Unity's built-in JSON serialization

License

This project is released under the MIT No Attribution (MIT-0) license. You are free to use, modify, and distribute it for personal or commercial projects without payment or attribution. See the Terms and Conditions and the repository LICENSE file for the full text.

Support

For support and inquiries, contact: support@michitai.com

Website: https://michitai.com