# OkonaPad - WebGL Controller Integration SDK

## Table of Contents
1. [Introduction](#introduction)
2. [System Requirements](#system-requirements)
3. [Quick Start Guide](#quick-start-guide)
4. [API Reference](#api-reference)
5. [Integration with Okona Online](#integration-with-okona-online)
6. [Troubleshooting](#troubleshooting)
7. [Support](#support)

## Introduction

OkonaPad is a lightweight Unity SDK for integrating smartphone controllers into WebGL games hosted on the Okona Online platform. The SDK receives controller input from the website shell via JavaScript interop, translating it into Unity Input System events.

**Key Features:**
- Seamless Unity Input System integration
- Support for up to 6 concurrent controllers
- Binary protocol for efficient input transmission
- Controller slot system with disconnect/reconnect handling
- Custom InputDevice with buttons, D-pad, and joystick
- Haptic feedback support via rumble callbacks

**Architecture Overview:**
```
[Phone Browser] --> [Okona Online Website] --> [JavaScript Interop] --> [OkonaInputBridge] --> [Unity Input System]
```

The Okona Online website handles all networking (WebSocket connections, QR codes, controller UI). Your Unity WebGL game only needs to receive input through the OkonaInputBridge component.

## System Requirements

### Unity Requirements
- Unity 6 (6000.3.2f1) or higher
- Input System package (com.unity.inputsystem) v1.17.0+

### Build Target
- **WebGL** (primary target for Okona Online integration)

### Project Settings
1. Open `Edit > Project Settings > Player > Other Settings`
2. Set `Active Input Handling` to `Both` or `Input System (New)`
3. Ensure the Input System package is installed via Package Manager

## Quick Start Guide

### 1. Add OkonaInputBridge to Your Scene

Create an empty GameObject and add the `OkonaInputBridge` component:

```csharp
using OkonaPad.Input;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    [SerializeField] private OkonaInputBridge inputBridge;

    void Awake()
    {
        // OkonaInputBridge automatically registers the OkonaPadControllerDevice
        // with Unity's Input System on initialization
    }
}
```

Or simply add the `OkonaInputBridge` component to any GameObject in your scene via the Unity Inspector.

### 2. Configure Input Actions

1. Open or create an Input Actions asset
2. Map your actions to OkonaPad controls using `<OkonaPad>/buttonA`, `<OkonaPad>/leftStick`, etc.
3. The `OkonaPadControllerDevice` appears as `OkonaPad` in the Input System
4. Use `<OkonaPad>` as the device requirement in your control scheme

### 3. Build for WebGL

1. File > Build Settings > WebGL
2. Build your project
3. Deploy to Okona Online platform

The website shell automatically establishes connections with phone controllers and forwards input to your game.

## API Reference

### OkonaInputBridge

The main component that receives input from the website and updates Unity Input System devices.

**Location:** `OkonaPad.Input.OkonaInputBridge`

**Lifecycle Methods (called by website shell via JavaScript SendMessage):**

```csharp
// Called when a phone controller connects or reconnects
public void OnControllerConnected(int controllerId)

// Called when a phone controller temporarily disconnects (device preserved)
public void OnControllerDisconnected(int controllerId)

// Called when a phone controller is permanently removed (device destroyed)
public void OnControllerRemoved(int controllerId)

// Called with binary input data (JSON with base64-encoded binary payload)
// Format: {"controllerId":1,"data":"base64string"}
public void ReceiveControllerMessageBase64(string jsonData)
```

**Events:**

```csharp
// New device created for the first time
event Action<int, OkonaPadControllerDevice> OnDeviceConnected;

// Temporary disconnect - device preserved, show "reconnecting" UI
event Action<int> OnDeviceDisconnected;

// Previously disconnected device has reconnected
event Action<int, OkonaPadControllerDevice> OnDeviceReconnected;

// Permanent removal - device destroyed, clean up player
event Action<int> OnDeviceRemoved;
```

**Helper Methods:**

```csharp
// Get the device for a specific controller ID (null if not found)
public OkonaPadControllerDevice GetDevice(int controllerId)

// Get connection state for a controller
public DeviceConnectionState? GetConnectionState(int controllerId)

// Get all controller IDs (connected and disconnected)
public int[] GetAllControllerIds()

// Get only connected controller IDs
public int[] GetConnectedControllerIds()

// Enable/disable debug logging
public void EnableDebugLogging(bool enable)
```

### OkonaPadControllerDevice

Custom Unity InputDevice representing a smartphone controller. This is NOT a Gamepad - it only exposes controls that physically exist on the phone controller.

**Location:** `OkonaPad.Input.OkonaPadControllerDevice`

**Controls:**

| Control | Type | Description |
|---------|------|-------------|
| `ButtonA` | ButtonControl | A button |
| `ButtonB` | ButtonControl | B button |
| `ButtonX` | ButtonControl | X button |
| `ButtonY` | ButtonControl | Y button |
| `StartButton` | ButtonControl | Start button |
| `SelectButton` | ButtonControl | Select button |
| `DPad` | DpadControl | Directional pad |
| `LeftStick` | StickControl | Analog joystick |

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `ControllerId` | int | Controller slot identifier (1-6) |

**Input Binding Paths:**

| Control | Binding Path |
|---------|-------------|
| A button | `<OkonaPad>/buttonA` |
| B button | `<OkonaPad>/buttonB` |
| X button | `<OkonaPad>/buttonX` |
| Y button | `<OkonaPad>/buttonY` |
| Start | `<OkonaPad>/startButton` |
| Select | `<OkonaPad>/selectButton` |
| D-pad | `<OkonaPad>/dpad` |
| Left Stick | `<OkonaPad>/leftStick` |

**Haptic Feedback:**

OkonaPadControllerDevice implements `IDualMotorRumble`. Subscribe to the static event to forward rumble to controllers:

```csharp
OkonaPadControllerDevice.OnRumbleRequested += (controllerId, lowFreq, highFreq) =>
{
    // Forward haptic feedback to the phone controller
};
```

**Usage Example:**

```csharp
using OkonaPad.Input;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    private OkonaPadControllerDevice controller;

    void Start()
    {
        // Subscribe to OkonaInputBridge events
        var bridge = OkonaInputBridge.Instance;
        if (bridge != null)
        {
            bridge.OnDeviceConnected += (id, device) => controller = device;
            bridge.OnDeviceRemoved += (id) => { if (controller?.ControllerId == id) controller = null; };
        }
    }

    void Update()
    {
        if (controller == null) return;

        // Read button states
        if (controller.ButtonA.wasPressedThisFrame)
            Jump();

        // Read joystick
        Vector2 movement = controller.LeftStick.ReadValue();
        Move(movement);

        // Read D-Pad
        if (controller.DPad.up.isPressed)
            MoveUp();
    }
}
```

**Using PlayerInputManager (Recommended):**

For multiplayer, use Unity's `PlayerInputManager` with "Join Players When Button Is Pressed". Players auto-destroy when their device is removed:

```csharp
private PlayerInput _playerInput;

void Awake()
{
    _playerInput = GetComponent<PlayerInput>();
    _playerInput.onDeviceLost += (pi) => Destroy(gameObject);
}
```

### BinaryProtocol

Defines the binary message format for controller input. Messages use a length-prefixed format:

```
[Length: 2 bytes BE][MessageType: 1 byte][Payload: variable]
```

**Location:** `OkonaPad.Networking.Binary`

**Message Types:**

| Type | Value | Payload | Description |
|------|-------|---------|-------------|
| Button | `0x00` | `[buttonCode:1][pressed:1]` | Button press/release |
| Dpad | `0x01` | `[direction:1][pressed:1]` | D-pad direction |
| Joystick | `0x02` | `[x:2][y:2]` (int16 BE) | Left joystick |
| Join | `0x03` | (none) | Connection handshake |
| Pong | `0x04` | (none) | Keep-alive response |
| Rumble | `0x05` | `[duration:2]` (uint16 BE) | Haptic feedback (Unity --> server) |

**Button Codes:**

| Button | Value |
|--------|-------|
| A | `0x00` |
| B | `0x01` |
| X | `0x02` |
| Y | `0x03` |
| Start | `0x04` |
| Select | `0x05` |

**D-pad Directions:**

| Direction | Value |
|-----------|-------|
| Up | `0x00` |
| Down | `0x01` |
| Left | `0x02` |
| Right | `0x03` |

## Integration with Okona Online

### How It Works

1. **Website Shell**: The Okona Online website hosts your WebGL game in an iframe or container
2. **Controller Management**: The website handles phone connections via WebSocket and manages controller slots (1-6)
3. **Input Forwarding**: Controller input is forwarded to your game via Unity's `SendMessage()`
4. **Rumble Feedback**: Your game sends haptic feedback through `OkonaSendRumbleToController`, which calls `window.okonaOnRumble()` on the website

### JavaScript Interop

The SDK includes `OkonaInterop.jslib` which sets up communication between Unity and the website shell.

**Website --> Unity (via SendMessage):**
```javascript
// When a phone controller connects:
unityInstance.SendMessage('OkonaInputBridge', 'OnControllerConnected', controllerId);

// When a phone controller temporarily disconnects:
unityInstance.SendMessage('OkonaInputBridge', 'OnControllerDisconnected', controllerId);

// When a phone controller is permanently removed:
unityInstance.SendMessage('OkonaInputBridge', 'OnControllerRemoved', controllerId);

// When input data arrives (binary data as base64 in JSON):
unityInstance.SendMessage('OkonaInputBridge', 'ReceiveControllerMessageBase64',
    JSON.stringify({ controllerId: controllerId, data: base64EncodedData }));
```

**Unity --> Website (via jslib):**
```javascript
// The website shell should define this to receive rumble commands:
window.okonaOnRumble = function(controllerId, durationMs) {
    // Forward haptic feedback to the phone controller via WebSocket
};
```

### Deployment

1. Build your Unity project for WebGL
2. Upload to Okona Online
3. Configure your game settings in the Okona dashboard
4. The platform handles controller connections automatically

## Troubleshooting

### Controller Not Detected

- Verify `OkonaInputBridge` component is in the scene
- Ensure the GameObject is named exactly `OkonaInputBridge` (required for SendMessage)
- Check browser console for JavaScript errors
- Verify Input System package is installed and configured

### Input Not Responding

- Check that Input Actions use `<OkonaPad>/` binding paths (not `<Gamepad>/`)
- Verify the device is registered: check `InputSystem.devices` for `OkonaPadControllerDevice`
- Enable debug logging: `OkonaInputBridge.Instance.EnableDebugLogging(true)`

### Rumble Not Working

- Ensure `window.okonaOnRumble` callback is defined in the website shell
- Check that the controller supports haptic feedback
- Verify the device implements `IDualMotorRumble` by calling `device.SetMotorSpeeds(lowFreq, highFreq)`

### WebGL Build Issues

- Ensure "Input System (New)" is enabled in Project Settings
- Check that assembly definitions are properly configured
- Verify no server-side code is included (WebGL cannot run HTTP/WebSocket servers)

### Debug Logging

Enable verbose logging via the bridge:

```csharp
OkonaInputBridge.Instance.EnableDebugLogging(true);
```

Check the browser's developer console (F12) for log output.

## Support

### Getting Help
- Email: dave.robinson124@gmail.com
- Website: https://earthcrosser.github.io/evm-website/

### Reporting Issues
When reporting issues, please include:
1. Unity version
2. Browser and version
3. Device types (host and controller)
4. Browser console logs
5. Steps to reproduce

---

OkonaPad SDK is designed for seamless integration with Okona Online. For the full platform experience, visit [Okona Online](https://okonaonline.com).
