# Forge ICOM Server (Internal Communication) A standalone TCP server for inter-Arma3-server communication. ICOM enables multiple Arma 3 servers to communicate with each other, facilitating cross-server events like mission spawning, supply drops, and military reports. ## Architecture ``` [Arma Server 1] <---> [Forge ICOM] <---> [Arma Server 2] (Extension) (TCP 9090) (Extension) Client Client ``` Each Arma server's extension connects to the ICOM server as a client. ICOM routes messages between servers based on target IDs or broadcasts to all connected servers. ## Configuration The ICOM server can be configured using a `config.toml` file. Create one from the example: ```powershell cp bin/icom/config.example.toml config.toml ``` Place `config.toml` in the same directory as the `forge-icom` executable or in the current working directory. ### Configuration Options ```toml [server] # Host to bind to # "0.0.0.0" = All interfaces (allows remote connections) # "127.0.0.1" = Localhost only host = "0.0.0.0" # Port to listen on port = 9090 ``` **Defaults**: If no config file is found, defaults to `0.0.0.0:9090`. ## Building ```powershell # Build release binary cargo build --release -p forge-icom # The executable will be at: # target/release/forge-icom.exe (Windows) ``` ## Running ```powershell # Run the ICOM server ./target/release/forge-icom.exe # Or during development cargo run -p forge-icom ``` The server will listen on `0.0.0.0:9090` by default. ## Design Philosophy ### Generic Event System ICOM uses a **generic event-based architecture** instead of predefined message types. This means: - ✅ **Flexibility**: Add new event types without changing the ICOM server code - ✅ **Simplicity**: Only one `Event` message type instead of multiple specialized types - ✅ **Decoupled**: ICOM doesn't need to know about your game logic - ✅ **Future-proof**: Easy to extend as your needs evolve ICOM simply routes events between servers - your application logic determines what each event means. ## Message Protocol All messages are JSON objects sent as newline-delimited strings. Each message has a `type` field that determines its structure. ### Register First message from each Arma server to identify itself: ```json { "type": "register", "server_id": "server_1" } ``` Response: ```json { "type": "registered", "session_id": "uuid-here" } ``` ### Event (Send to Specific Server) Send an event with arbitrary JSON data to a specific server: ```json { "type": "event", "target_server": "server_2", "event_name": "supply_drop", "data": { "coords": [1234.5, 5678.9, 0.0], "supplies": ["ammo_box", "medical_supplies"] } } ``` Another example: ```json { "type": "event", "target_server": "server_2", "event_name": "spawn_mission", "data": { "mission_type": "convoy_ambush", "difficulty": "hard", "location": [1234, 5678, 0] } } ``` ### Broadcast Send event to all connected servers (except sender): ```json { "type": "broadcast", "event_name": "global_alert", "data": { "message": "Nuclear strike incoming!", "severity": "critical" } } ``` ### Acknowledgment Response to successful message delivery: ```json { "type": "ack", "message_id": null, "success": true, "error": null } ``` Error response: ```json { "type": "ack", "message_id": null, "success": false, "error": "Target server 'server_3' not found" } ``` ## Integration with Arma Extension ### Client Library The `forge-icom` crate includes a `client` module that provides a high-level API for connecting to ICOM: ```rust use forge_icom::client::IComClient; use forge_icom::Message; use serde_json::json; // Connect and register (automatically handles registration) let client = IComClient::connect("127.0.0.1:9090", "server_1".to_string()).await?; // Send event to another server client.send_event( "server_2", "supply_drop", json!({ "coords": [1234.5, 5678.9, 0.0], "supplies": ["ammo", "medical"] }) ).await?; // Broadcast to all servers client.broadcast( "global_alert", json!({"message": "Server restart in 5 minutes"}) ).await?; // Listen for incoming events client.listen_for_events(|msg| { match msg { Message::Event { event_name, data, .. } => { // Forward to Arma via callback } _ => {} } Ok(()) }).await?; ``` ### Extension Integration The Forge server extension includes full ICOM integration: 1. **Initialization**: Connect with `icom:connect` after the ICOM hub is running. 2. **Event Listener**: Spawns background task to receive events continuously 3. **Callback System**: Forwards events to Arma via CBA event handlers 4. **Extension Commands**: Provides SQF commands to send/receive events **Important Notes**: - The extension uses `try_read()` to avoid deadlocks when accessing context from async tasks - Broadcast events are **not** sent back to the originating server - Connection is initiated through the `icom:connect` extension command. ### SQF Usage #### Connecting to ICOM ```sqf // Connect manually (if not using automatic startup connection) private _result = "forge_server" callExtension ["icom:connect", ["127.0.0.1:9090", "server_1"]]; systemChat _result; // "Connection initiated" or "ERROR: Already connected" ``` #### Sending Events ```sqf // Send event to specific server private _data = createHashMapFromArray [ ["coords", [1234, 5678, 0]], ["supplies", ["ammo_box", "medical_supplies"]] ]; "forge_server" callExtension ["icom:send_event", ["server_2", "supply_drop", (toJSON _data)]]; // Spawn mission on another server private _missionData = createHashMapFromArray [ ["mission_type", "convoy_ambush"], ["difficulty", "hard"], ["location", [1234, 5678, 0]] ]; "forge_server" callExtension ["icom:send_event", ["server_2", "spawn_mission", (toJSON _missionData)]]; // Broadcast to all servers (except sender) private _alertData = createHashMapFromArray [ ["message", "Nuclear strike incoming!"], ["severity", "critical"] ]; "forge_server" callExtension ["icom:broadcast", ["global_alert", (toJSON _alertData)]]; ``` #### Receiving Events Handle incoming events with a CBA event handler: ```sqf ["forge_icom_event", { params ["_eventName", "_data"]; switch (_eventName) do { case "supply_drop": { private _coords = _data get "coords"; private _supplies = _data get "supplies"; // Create supply drop at coordinates [_coords, _supplies] call YourMod_fnc_createSupplyDrop; }; case "spawn_mission": { private _missionType = _data get "mission_type"; private _location = _data get "location"; // Spawn the mission [_missionType, _location] call YourMod_fnc_spawnMission; }; case "global_alert": { private _message = _data get "message"; // Show alert to all players [_message] remoteExec ["hint", 0]; }; default { diag_log format ["[ICOM] Unhandled event: %1", _eventName]; }; }; }] call CBA_fnc_addEventHandler; ``` ## Production Deployment ### As Windows Service You can run ICOM as a Windows service using tools like NSSM: ```powershell # Install NSSM winget install NSSM.NSSM # Create service nssm install ForgeICOM "C:\path\to\forge-icom.exe" nssm start ForgeICOM ``` ### Docker (for Linux servers) ```dockerfile FROM rust:1.70 as builder WORKDIR /app COPY . . RUN cargo build --release -p forge-icom FROM debian:bookworm-slim COPY --from=builder /app/target/release/forge-icom /usr/local/bin/ EXPOSE 9090 CMD ["forge-icom"] ``` ```bash docker build -t forge-icom . docker run -d -p 9090:9090 --name forge-icom forge-icom ``` ## Features - **Async I/O**: Non-blocking message handling using Tokio - **Multiple connections**: Handle dozens of Arma servers simultaneously - **Generic event system**: Send arbitrary JSON data without predefined message types - **Message routing**: Direct events to specific servers or broadcast to all - **Session management**: Track connected servers with UUIDs - **Duplicate connection handling**: Automatically replaces old connections when server reconnects - **Automatic cleanup**: Remove disconnected servers from registry - **Graceful error handling**: Clients continue running even when target servers are offline ## Testing ### Running Examples ```powershell # Terminal 1: Start ICOM server cargo run --bin forge-icom # Terminal 2: Start server_1 (listener) cargo run --example server_1_client # Terminal 3: Start server_2 (sender) cargo run --example server_2_client ``` You should see events flow from server_2 → ICOM → server_1. ### Test with Extension 1. Start ICOM server 2. Start Arma 3 server with Forge extension 3. In Arma, connect manually (if needed): ```sqf "forge_server" callExtension ["icom:connect", ["127.0.0.1:9090", "server_1"]] ``` 4. Set up CBA event handler in mission init: ```sqf ["forge_icom_event", { params ["_eventName", "_data"]; systemChat format ["ICOM Event: %1", _eventName]; }] call CBA_fnc_addEventHandler; ``` 5. Run example sender client to test event reception: ```powershell cargo run --example server_2_client ``` 6. Check logs at `@forge_server/logs/icom.log` to verify events are received ## Next Steps 1. **Run the examples** to see the system in action 2. **Add CBA event handler** in your mission to process `forge_icom_event` 3. **Define your event types** (supply_drop, spawn_mission, etc.) based on your needs 4. **Test with multiple Arma servers** locally 5. **Deploy ICOM server** to production (Windows Service or Docker) 6. **Configure server IDs** in extension config for each server ## Monitoring The ICOM server logs all important events to stdout: - 🔥 Server startup - 📡 New connections - ✅ Server registrations - 📢 Broadcast messages - 📨 Message forwarding - 🗑️ Server disconnections - ❌ Errors Consider redirecting output to a file or logging service for production.