400 lines
10 KiB
Markdown
400 lines
10 KiB
Markdown
# 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.
|