Create comprehensive README documentation for PurrLobby
PurrLobby 🐱
A lightweight, fast, and reliable lobby service for multiplayer games. PurrLobby provides an alternative to Steam Lobby Service and Unity Lobby Service, offering real-time lobby management with WebSocket communication.
🌟 Features
- Multi-Game Support: Scope lobbies per game using unique Game IDs
- Real-time Updates: WebSocket-based live lobby updates and member status changes
- Player Management: Join/leave lobbies, ready status, custom display names
- Custom Properties: Store arbitrary key-value data on lobbies
- Search & Discovery: Find available lobbies with filtering
- Rate Limiting: Built-in protection against abuse (300 requests/minute per IP)
- Statistics: Global and per-game player/lobby statistics
- Production Ready: Includes compression, HTTPS, security headers, and proxy support
- Web Interface: Built-in dashboard for monitoring and testing
- OpenAPI/Swagger: Full API documentation with interactive testing
🚀 Quick Start
Prerequisites
- .NET 8.0 SDK
- Windows, macOS, or Linux
Installation & Running
- Clone the repository:
git clone https://github.com/ExilProductions/PurrLobby.git
cd PurrLobby
- Build the project:
dotnet build
- Run the service:
cd PurrLobby
dotnet run
- Access the web interface: https://localhost:7225
- View API documentation: https://localhost:7225/swagger
🔧 Configuration
Environment Variables
Configure the service using appsettings.json or environment variables:
ASPNETCORE_ENVIRONMENT: Set toProductionfor production deploymentASPNETCORE_URLS: Configure listening URLs (default:https://localhost:7225;http://localhost:5123)
Domain Configuration
For production deployment, update the cookie domain in Program.cs:
Domain = "your-domain.com" // Line 134
Rate Limiting
Default rate limits (configurable in Program.cs):
- 300 requests per minute per IP address
- 100 queued requests per IP
- Returns
429 Too Many Requestswhen exceeded
📚 API Usage
1. Set Game Context
Before using lobby endpoints, set your game's GUID:
curl -X POST "https://your-domain.com/session/game" \
-H "Content-Type: application/json" \
-d '{"gameId": "your-game-guid-here"}'
This sets a secure cookie that scopes all subsequent requests to your game.
2. Create a Lobby
curl -X POST "https://your-domain.com/lobbies" \
-H "Content-Type: application/json" \
-H "Cookie: gameId=your-game-guid" \
-d '{
"ownerUserId": "player123",
"ownerDisplayName": "PlayerName",
"maxPlayers": 4,
"properties": {
"gameMode": "competitive",
"map": "dust2"
}
}'
3. Search Lobbies
curl "https://your-domain.com/lobbies/search?maxRoomsToFind=10" \
-H "Cookie: gameId=your-game-guid"
4. Join a Lobby
curl -X POST "https://your-domain.com/lobbies/{lobbyId}/join" \
-H "Content-Type: application/json" \
-H "Cookie: gameId=your-game-guid" \
-d '{
"userId": "player456",
"displayName": "AnotherPlayer"
}'
5. WebSocket Connection for Real-time Updates
Connect to lobby-specific WebSocket for live updates:
const ws = new WebSocket(`wss://your-domain.com/ws/lobbies/${lobbyId}?userId=${userId}`);
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Lobby update:', data);
// Handle events: member_joined, member_left, member_ready, lobby_data, etc.
};
🌐 Web Interface
The service includes a web dashboard at the root URL featuring:
- Global Statistics: Total players and lobbies across all games
- Game-Specific Stats: Players and lobbies for your specific game
- Game ID Management: Easy way to set and track your game
- Direct API Access: Links to Swagger documentation
📊 Monitoring & Statistics
Global Statistics
GET /stats/global/players- Total players across all gamesGET /stats/global/lobbies- Total lobbies across all games
Game-Specific Statistics
GET /stats/{gameId}/players- Active players for a specific gameGET /stats/{gameId}/lobbies- Lobby count for a specific game
Health Check
GET /health- Service health status
🏗️ Architecture
Components
- Program.cs: Main application configuration and API endpoints
- LobbyService: Core business logic for lobby management
- LobbyEventHub: WebSocket connection management and real-time events
- Lobby Models: Data structures for lobbies and users
Key Features Implementation
- Cookie-based Game Scoping: Uses secure HTTP-only cookies for game context
- In-Memory Storage: Fast performance with concurrent collections
- Event-Driven Updates: Real-time WebSocket notifications for all lobby changes
- Production Security: Rate limiting, HTTPS enforcement, proxy header trust
- Comprehensive Logging: HTTP request logging and structured application logs
🚢 Deployment
Docker (Recommended)
Create a Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["PurrLobby/PurrLobby.csproj", "PurrLobby/"]
RUN dotnet restore "PurrLobby/PurrLobby.csproj"
COPY . .
WORKDIR "/src/PurrLobby"
RUN dotnet build "PurrLobby.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "PurrLobby.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "PurrLobby.dll"]
Reverse Proxy Configuration
For production deployment behind a reverse proxy (nginx, Apache, etc.), ensure proper header forwarding for rate limiting and security:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
🔌 Client Integration Examples
Unity C#
public async Task<bool> CreateLobby(string gameId, string userId, string displayName)
{
var client = new HttpClient();
// Set game context
await client.PostAsync($"{baseUrl}/session/game",
new StringContent($"{{\"gameId\": \"{gameId}\"}}", Encoding.UTF8, "application/json"));
// Create lobby
var lobbyData = new {
ownerUserId = userId,
ownerDisplayName = displayName,
maxPlayers = 4,
properties = new { gameMode = "casual" }
};
var response = await client.PostAsync($"{baseUrl}/lobbies",
new StringContent(JsonUtility.ToJson(lobbyData), Encoding.UTF8, "application/json"));
return response.IsSuccessStatusCode;
}
JavaScript/Web
class PurrLobbyClient {
constructor(baseUrl, gameId) {
this.baseUrl = baseUrl;
this.gameId = gameId;
}
async setGameContext() {
await fetch(`${this.baseUrl}/session/game`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ gameId: this.gameId })
});
}
async createLobby(userId, displayName, maxPlayers = 4) {
const response = await fetch(`${this.baseUrl}/lobbies`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
ownerUserId: userId,
ownerDisplayName: displayName,
maxPlayers: maxPlayers,
properties: {}
})
});
return response.json();
}
}
🛠️ Development
Building from Source
# Clone repository
git clone https://github.com/ExilProductions/PurrLobby.git
cd PurrLobby
# Restore dependencies and build
dotnet restore
dotnet build
# Run with hot reload for development
cd PurrLobby
dotnet watch run
Running Tests
dotnet test
Code Analysis
The project includes code analysis rules. Fix any warnings before deploying:
dotnet build --verbosity normal
📝 API Reference
Full API documentation is available via Swagger UI when the service is running:
- Local: https://localhost:7225/swagger
- Production:
https://your-domain.com/swagger
Authentication
Most endpoints require a gameId cookie set via POST /session/game. The cookie:
- Is HTTP-only and secure
- Expires after 7 days
- Scopes all requests to your specific game
WebSocket Events
The lobby WebSocket (/ws/lobbies/{lobbyId}) broadcasts these events:
member_joined- New player joinedmember_left- Player left lobbymember_ready- Player readiness changedlobby_data- Custom lobby property updatedlobby_deleted- Lobby was deleted
🤝 Contributing
This is an independent project by ExilProductions, not affiliated with PurrNet.
Issues & Support
- For bugs or feature requests, open an issue on GitHub
- For direct support, contact: exil_s on Discord
- Please don't contact PurrNet developers about this service
Development Guidelines
- Fork the repository
- Create a feature branch
- Make your changes with appropriate tests
- Ensure code analysis passes
- Submit a pull request
⚠️ Disclaimer
This service is independent and runs on personal infrastructure. It may experience downtime or instability. It's provided as-is for development and testing purposes. For production use, consider self-hosting or implementing additional redundancy.
📄 License
This project is open source. Check the repository for license details.
🔗 Links
- Live Instance: https://purrlobby.exil.dev
- API Documentation: https://purrlobby.exil.dev/swagger
- GitHub Repository: https://github.com/ExilProductions/PurrLobby
Made with 💜 by ExilProductions