start the repo
This commit is contained in:
610
modernized-client/docs/LOGIN_SYSTEM.md
Normal file
610
modernized-client/docs/LOGIN_SYSTEM.md
Normal file
@@ -0,0 +1,610 @@
|
||||
# OpenOSRS Login System Documentation
|
||||
|
||||
This document provides comprehensive documentation for the OpenOSRS modernized client login system, designed specifically for AI agent automation.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Architecture](#architecture)
|
||||
3. [Core Components](#core-components)
|
||||
4. [Agent API](#agent-api)
|
||||
5. [Usage Examples](#usage-examples)
|
||||
6. [Error Handling](#error-handling)
|
||||
7. [Security Features](#security-features)
|
||||
8. [Advanced Features](#advanced-features)
|
||||
9. [Configuration](#configuration)
|
||||
10. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Overview
|
||||
|
||||
The OpenOSRS login system provides a complete, agent-friendly authentication solution for Old School RuneScape. It offers both synchronous and asynchronous login methods, comprehensive error handling, OTP support, automatic reconnection, and extensive event monitoring.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Agent-Optimized**: Designed specifically for AI agents with programmatic interfaces
|
||||
- **Comprehensive Authentication**: Support for username/password and OTP authentication
|
||||
- **Asynchronous Operations**: Non-blocking login operations with CompletableFuture support
|
||||
- **Event-Driven**: Complete event system for monitoring login state changes
|
||||
- **Auto-Reconnection**: Automatic reconnection with configurable retry logic
|
||||
- **Error Handling**: Detailed error codes and messages for all failure scenarios
|
||||
- **Session Management**: Proper session token and ID handling
|
||||
- **Security**: Encrypted packet transmission and secure credential handling
|
||||
|
||||
## Architecture
|
||||
|
||||
The login system follows a layered architecture:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Agent API │ ← High-level agent interface
|
||||
├─────────────────────────────────────────┤
|
||||
│ Login Screen │ ← UI abstraction layer
|
||||
├─────────────────────────────────────────┤
|
||||
│ Login State │ ← State management
|
||||
├─────────────────────────────────────────┤
|
||||
│ Event System │ ← Event handling
|
||||
├─────────────────────────────────────────┤
|
||||
│ Network Protocol │ ← Network communication
|
||||
├─────────────────────────────────────────┤
|
||||
│ Client Core │ ← Core client systems
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
### LoginState
|
||||
|
||||
**Location**: `com.openosrs.client.core.CoreStates.LoginState`
|
||||
|
||||
Manages the current login state and credentials.
|
||||
|
||||
```java
|
||||
// State enumeration
|
||||
public enum State {
|
||||
LOGGED_OUT, // Not logged in
|
||||
LOGGING_IN, // Login in progress
|
||||
LOGGED_IN, // Successfully logged in
|
||||
LOGIN_FAILED, // Login failed
|
||||
LOGGED_OUT_TIMEOUT // Logged out due to timeout
|
||||
}
|
||||
|
||||
// Key methods
|
||||
public void setCredentials(String username, String password, String otp)
|
||||
public void attemptLogin()
|
||||
public void logout()
|
||||
public State getState()
|
||||
public boolean isValidCredentials()
|
||||
```
|
||||
|
||||
### LoginScreen
|
||||
|
||||
**Location**: `com.openosrs.client.core.LoginScreen`
|
||||
|
||||
Provides high-level login screen management for agents.
|
||||
|
||||
```java
|
||||
// Synchronous login methods
|
||||
public LoginResult login(String username, String password)
|
||||
public LoginResult login(String username, String password, String otp)
|
||||
public LoginResult loginWithTimeout(String username, String password, int timeoutSeconds)
|
||||
|
||||
// Asynchronous login methods
|
||||
public CompletableFuture<LoginResult> loginAsync(String username, String password)
|
||||
public void loginAsync(String username, String password, Consumer<LoginResult> callback)
|
||||
|
||||
// State queries
|
||||
public boolean isLoggedIn()
|
||||
public boolean isLoginInProgress()
|
||||
public LoginState.State getCurrentState()
|
||||
```
|
||||
|
||||
### LoginHandler
|
||||
|
||||
**Location**: `com.openosrs.client.engine.NetworkProtocol.LoginHandler`
|
||||
|
||||
Handles the network protocol for login authentication.
|
||||
|
||||
```java
|
||||
// Core functionality
|
||||
public void handleLoginResponse(NetworkEngine.IncomingMessage message)
|
||||
public void sendLoginRequest(NetworkEngine.OutgoingMessage message)
|
||||
public boolean isLoginInProgress()
|
||||
|
||||
// Supported login response codes
|
||||
LOGIN_SUCCESS = 0
|
||||
LOGIN_INVALID_CREDENTIALS = 3
|
||||
LOGIN_ACCOUNT_DISABLED = 4
|
||||
LOGIN_ALREADY_ONLINE = 5
|
||||
LOGIN_WORLD_FULL = 7
|
||||
// ... and 17 more error codes
|
||||
```
|
||||
|
||||
### EventSystem
|
||||
|
||||
**Location**: `com.openosrs.client.core.EventSystem`
|
||||
|
||||
Provides comprehensive event handling for login operations.
|
||||
|
||||
```java
|
||||
// Login-specific event types
|
||||
LOGIN_ATTEMPT_STARTED
|
||||
LOGIN_SUCCESS
|
||||
LOGIN_FAILED
|
||||
LOGIN_PROGRESS
|
||||
LOGOUT
|
||||
DISCONNECTED
|
||||
RECONNECTING
|
||||
|
||||
// Event listener management
|
||||
public void addListener(EventType type, Consumer<Event> listener)
|
||||
public void removeListener(EventType type, Consumer<Event> listener)
|
||||
public void fireEvent(EventType type, Event event)
|
||||
```
|
||||
|
||||
## Agent API
|
||||
|
||||
**Location**: `com.openosrs.client.api.AgentAPI`
|
||||
|
||||
The primary interface for AI agents to interact with the login system.
|
||||
|
||||
### Basic Login Methods
|
||||
|
||||
```java
|
||||
AgentAPI api = new AgentAPI(clientCore);
|
||||
api.initialize();
|
||||
|
||||
// Synchronous login (blocking)
|
||||
AgentAPI.LoginResult result = api.login("username", "password");
|
||||
if (result.isSuccess()) {
|
||||
System.out.println("Login successful! Session: " + result.getSessionId());
|
||||
} else {
|
||||
System.out.println("Login failed: " + result.getMessage());
|
||||
}
|
||||
|
||||
// Asynchronous login (non-blocking)
|
||||
CompletableFuture<AgentAPI.LoginResult> future = api.loginAsync("username", "password");
|
||||
future.thenAccept(result -> {
|
||||
if (result.isSuccess()) {
|
||||
System.out.println("Async login successful!");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### OTP Support
|
||||
|
||||
```java
|
||||
// Login with One-Time Password
|
||||
AgentAPI.LoginResult result = api.login("username", "password", "123456");
|
||||
```
|
||||
|
||||
### Callback-Based Login
|
||||
|
||||
```java
|
||||
// Set up login callbacks
|
||||
api.setLoginCallbacks(
|
||||
result -> System.out.println("Success: " + result),
|
||||
error -> System.out.println("Error: " + error),
|
||||
progress -> System.out.println("Progress: " + progress)
|
||||
);
|
||||
|
||||
// Start async login
|
||||
api.loginAsync("username", "password", "otp");
|
||||
```
|
||||
|
||||
### Auto-Reconnection
|
||||
|
||||
```java
|
||||
// Enable auto-reconnection
|
||||
api.setAutoReconnect(true, 30, 5); // 30 second delay, 5 max attempts
|
||||
|
||||
// Auto-reconnection will now handle disconnections automatically
|
||||
```
|
||||
|
||||
### State Monitoring
|
||||
|
||||
```java
|
||||
// Check login state
|
||||
boolean loggedIn = api.isLoggedIn();
|
||||
boolean loginInProgress = api.isLoginInProgress();
|
||||
LoginState.State state = api.getLoginState();
|
||||
|
||||
// Get game state (when logged in)
|
||||
AgentAPI.Position position = api.getPlayerPosition();
|
||||
int health = api.getPlayerHealth();
|
||||
boolean moving = api.isPlayerMoving();
|
||||
```
|
||||
|
||||
### Event Listening
|
||||
|
||||
```java
|
||||
// Listen for login events
|
||||
api.addEventListener(EventSystem.EventType.LOGIN_SUCCESS, event -> {
|
||||
System.out.println("Login successful!");
|
||||
});
|
||||
|
||||
api.addEventListener(EventSystem.EventType.LOGIN_FAILED, event -> {
|
||||
System.out.println("Login failed!");
|
||||
});
|
||||
|
||||
api.addEventListener(EventSystem.EventType.DISCONNECTED, event -> {
|
||||
System.out.println("Connection lost!");
|
||||
});
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Simple Login Example
|
||||
|
||||
```java
|
||||
public class SimpleAgent {
|
||||
public static void main(String[] args) {
|
||||
ClientCore clientCore = new ClientCore();
|
||||
AgentAPI api = new AgentAPI(clientCore);
|
||||
|
||||
try {
|
||||
clientCore.initialize();
|
||||
api.initialize();
|
||||
|
||||
AgentAPI.LoginResult result = api.login("myusername", "mypassword");
|
||||
|
||||
if (result.isSuccess()) {
|
||||
System.out.println("Successfully logged in!");
|
||||
|
||||
// Agent is now ready to play
|
||||
while (api.isLoggedIn()) {
|
||||
// Game logic here
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Login failed: " + result.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
api.shutdown();
|
||||
clientCore.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Agent with Auto-Reconnection
|
||||
|
||||
```java
|
||||
public class AdvancedAgent {
|
||||
private ClientCore clientCore;
|
||||
private AgentAPI api;
|
||||
private volatile boolean running = true;
|
||||
|
||||
public void start() {
|
||||
clientCore = new ClientCore();
|
||||
api = new AgentAPI(clientCore);
|
||||
|
||||
try {
|
||||
clientCore.initialize();
|
||||
api.initialize();
|
||||
|
||||
// Enable auto-reconnection
|
||||
api.setAutoReconnect(true, 30, 10);
|
||||
|
||||
// Set up event monitoring
|
||||
setupEventListeners();
|
||||
|
||||
// Initial login
|
||||
performLogin();
|
||||
|
||||
// Main game loop
|
||||
gameLoop();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupEventListeners() {
|
||||
api.addEventListener(EventSystem.EventType.LOGIN_SUCCESS, event -> {
|
||||
System.out.println("✅ Connected and ready!");
|
||||
});
|
||||
|
||||
api.addEventListener(EventSystem.EventType.DISCONNECTED, event -> {
|
||||
System.out.println("🔌 Connection lost - auto-reconnection will handle this");
|
||||
});
|
||||
|
||||
api.addEventListener(EventSystem.EventType.LOGIN_FAILED, event -> {
|
||||
System.out.println("❌ Login failed - check credentials");
|
||||
});
|
||||
}
|
||||
|
||||
private void performLogin() {
|
||||
String username = System.getenv("OSRS_USERNAME");
|
||||
String password = System.getenv("OSRS_PASSWORD");
|
||||
String otp = System.getenv("OSRS_OTP");
|
||||
|
||||
if (username == null || password == null) {
|
||||
throw new IllegalArgumentException("Username and password must be set in environment variables");
|
||||
}
|
||||
|
||||
api.loginAsync(username, password, otp).thenAccept(result -> {
|
||||
if (!result.isSuccess()) {
|
||||
System.err.println("Initial login failed: " + result.getMessage());
|
||||
running = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void gameLoop() {
|
||||
while (running) {
|
||||
try {
|
||||
if (api.isLoggedIn()) {
|
||||
// Perform game actions
|
||||
doGameAction();
|
||||
} else {
|
||||
// Wait for reconnection
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doGameAction() {
|
||||
// Example game logic
|
||||
AgentAPI.Position pos = api.getPlayerPosition();
|
||||
int health = api.getPlayerHealth();
|
||||
|
||||
System.out.println("Player at " + pos + ", health: " + health);
|
||||
|
||||
try {
|
||||
Thread.sleep(5000); // Wait 5 seconds between actions
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
if (api != null) {
|
||||
api.logout();
|
||||
api.shutdown();
|
||||
}
|
||||
if (clientCore != null) {
|
||||
clientCore.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The login system provides comprehensive error handling with specific error codes and messages.
|
||||
|
||||
### Login Error Codes
|
||||
|
||||
| Code | Constant | Description |
|
||||
|------|----------|-------------|
|
||||
| 0 | LOGIN_SUCCESS | Login successful |
|
||||
| 3 | LOGIN_INVALID_CREDENTIALS | Invalid username or password |
|
||||
| 4 | LOGIN_ACCOUNT_DISABLED | Account has been disabled |
|
||||
| 5 | LOGIN_ALREADY_ONLINE | Account is already logged in |
|
||||
| 6 | LOGIN_SERVER_UPDATED | Game updated - reload client |
|
||||
| 7 | LOGIN_WORLD_FULL | World is full |
|
||||
| 8 | LOGIN_LOGIN_SERVER_OFFLINE | Login server offline |
|
||||
| 9 | LOGIN_LOGIN_LIMIT_EXCEEDED | Too many connections |
|
||||
| 10 | LOGIN_BAD_SESSION_ID | Unable to connect |
|
||||
| 11 | LOGIN_FORCE_PASSWORD_CHANGE | Password change required |
|
||||
| 12 | LOGIN_NEED_MEMBERS_ACCOUNT | Members account required |
|
||||
| 13 | LOGIN_COULD_NOT_COMPLETE_LOGIN | Could not complete login |
|
||||
| 14 | LOGIN_SERVER_BEING_UPDATED | Server being updated |
|
||||
| 15 | LOGIN_RECONNECTING | Reconnecting |
|
||||
| 16 | LOGIN_LOGIN_ATTEMPTS_EXCEEDED | Too many login attempts |
|
||||
| 17 | LOGIN_MEMBERS_ONLY_AREA | Members only area |
|
||||
| 18 | LOGIN_LOCKED_ACCOUNT | Account locked |
|
||||
| 19 | LOGIN_CLOSE_OTHER_CONNECTION | Close other connection |
|
||||
| 20 | LOGIN_MALFORMED_PACKET | Malformed packet |
|
||||
| 21 | LOGIN_NO_REPLY_FROM_LOGIN_SERVER | No reply from login server |
|
||||
| 22 | LOGIN_ERROR_LOADING_PROFILE | Error loading profile |
|
||||
| 23 | LOGIN_UNKNOWN_REPLY_FROM_LOGIN_SERVER | Unknown reply from login server |
|
||||
| 26 | LOGIN_IP_BLOCKED | IP address blocked |
|
||||
|
||||
### Error Handling Examples
|
||||
|
||||
```java
|
||||
AgentAPI.LoginResult result = api.login("username", "password");
|
||||
|
||||
if (!result.isSuccess()) {
|
||||
String message = result.getMessage();
|
||||
|
||||
if (message.contains("Invalid username or password")) {
|
||||
// Handle credential error
|
||||
System.out.println("Credentials are incorrect");
|
||||
} else if (message.contains("World is full")) {
|
||||
// Try different world
|
||||
System.out.println("World is full, trying different world");
|
||||
} else if (message.contains("too many login attempts")) {
|
||||
// Wait before retrying
|
||||
System.out.println("Rate limited, waiting before retry");
|
||||
Thread.sleep(60000); // Wait 1 minute
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Features
|
||||
|
||||
### Credential Security
|
||||
|
||||
- Credentials are not stored in plain text
|
||||
- Passwords are cleared from memory after use
|
||||
- OTP codes are handled securely
|
||||
- Session tokens are encrypted in transmission
|
||||
|
||||
### Network Security
|
||||
|
||||
- All login packets are encrypted
|
||||
- Session verification prevents replay attacks
|
||||
- Automatic session timeout handling
|
||||
- Secure random session ID generation
|
||||
|
||||
### Best Practices
|
||||
|
||||
```java
|
||||
// Store credentials securely
|
||||
String username = System.getenv("OSRS_USERNAME");
|
||||
String password = System.getenv("OSRS_PASSWORD");
|
||||
String otp = System.getenv("OSRS_OTP");
|
||||
|
||||
// Use environment variables, not hardcoded strings
|
||||
AgentAPI.LoginResult result = api.login(username, password, otp);
|
||||
|
||||
// Clear sensitive data
|
||||
username = null;
|
||||
password = null;
|
||||
otp = null;
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Session Management
|
||||
|
||||
```java
|
||||
// Get session information
|
||||
if (api.isLoggedIn()) {
|
||||
LoginState loginState = clientCore.getLoginState();
|
||||
int sessionId = loginState.getSessionId();
|
||||
String sessionToken = loginState.getSessionToken();
|
||||
|
||||
System.out.println("Session ID: " + sessionId);
|
||||
System.out.println("Session Token: " + sessionToken);
|
||||
}
|
||||
```
|
||||
|
||||
### Timeout Configuration
|
||||
|
||||
```java
|
||||
// Custom login timeout
|
||||
AgentAPI.LoginResult result = api.login("username", "password", null, 60); // 60 second timeout
|
||||
```
|
||||
|
||||
### Progress Monitoring
|
||||
|
||||
```java
|
||||
api.setLoginCallbacks(
|
||||
null, // success callback
|
||||
null, // error callback
|
||||
progress -> System.out.println("Login progress: " + progress) // progress callback
|
||||
);
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# Required for login
|
||||
export OSRS_USERNAME="your_username"
|
||||
export OSRS_PASSWORD="your_password"
|
||||
|
||||
# Optional
|
||||
export OSRS_OTP="123456" # If using OTP
|
||||
export OSRS_WORLD="301" # Preferred world
|
||||
export OSRS_AUTO_RECONNECT="true" # Enable auto-reconnection
|
||||
```
|
||||
|
||||
### System Properties
|
||||
|
||||
```bash
|
||||
# Enable debug logging
|
||||
-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG
|
||||
|
||||
# Custom timeouts
|
||||
-Dosrs.login.timeout=30000
|
||||
-Dosrs.reconnect.delay=30000
|
||||
-Dosrs.reconnect.maxAttempts=5
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Login Timeout**
|
||||
```
|
||||
Error: Login timeout or error: TimeoutException
|
||||
```
|
||||
*Solution*: Increase timeout or check network connectivity
|
||||
|
||||
**Invalid Credentials**
|
||||
```
|
||||
Error: Invalid username or password
|
||||
```
|
||||
*Solution*: Verify credentials, check for typos
|
||||
|
||||
**World Full**
|
||||
```
|
||||
Error: This world is full. Please use a different world
|
||||
```
|
||||
*Solution*: Try different world or wait for space
|
||||
|
||||
**Too Many Attempts**
|
||||
```
|
||||
Error: Too many login attempts. Please wait a few minutes
|
||||
```
|
||||
*Solution*: Wait before retrying, implement exponential backoff
|
||||
|
||||
### Debug Logging
|
||||
|
||||
Enable debug logging to troubleshoot issues:
|
||||
|
||||
```java
|
||||
// In code
|
||||
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
|
||||
|
||||
// Or via JVM arguments
|
||||
-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG
|
||||
-Dorg.slf4j.simpleLogger.showDateTime=true
|
||||
-Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss.SSS
|
||||
```
|
||||
|
||||
### Network Issues
|
||||
|
||||
```java
|
||||
// Test network connectivity
|
||||
if (!api.isLoggedIn()) {
|
||||
// Check if it's a network issue
|
||||
try {
|
||||
InetAddress.getByName("oldschool.runescape.com").isReachable(5000);
|
||||
System.out.println("Network connectivity OK");
|
||||
} catch (IOException e) {
|
||||
System.out.println("Network connectivity issues: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Running the Example
|
||||
|
||||
To run the login example:
|
||||
|
||||
```bash
|
||||
# Using Gradle
|
||||
./gradlew runLoginExample
|
||||
|
||||
# Or compile and run directly
|
||||
./gradlew build
|
||||
java -cp build/libs/modernized-client-1.0.0.jar com.openosrs.client.examples.ExampleLoginAgent
|
||||
```
|
||||
|
||||
The example provides an interactive menu to test different login scenarios:
|
||||
|
||||
1. Basic Login (Synchronous)
|
||||
2. Async Login (CompletableFuture)
|
||||
3. Login with OTP
|
||||
4. Login with Callbacks
|
||||
5. Auto-Reconnection Demo
|
||||
6. Error Handling Demo
|
||||
7. Show Game State
|
||||
8. Logout and Exit
|
||||
|
||||
---
|
||||
|
||||
**Note**: This documentation covers the complete login system implementation. All features are designed to work seamlessly with AI agents while providing the flexibility and reliability needed for automated gameplay.
|
||||
Reference in New Issue
Block a user