Restructure protobufs and use them everywhere

This commit is contained in:
DatCaptainHorse
2025-10-21 18:41:45 +03:00
parent 32341574dc
commit 67f9a7d0a0
37 changed files with 3455 additions and 3074 deletions

View File

@@ -1,7 +1,5 @@
use crate::proto::proto::proto_input::InputType::{
ControllerAttach, ControllerAxis, ControllerButton, ControllerDetach, ControllerRumble,
ControllerStick, ControllerTrigger,
};
use crate::proto::proto::ProtoControllerAttach;
use crate::proto::proto::proto_message::Payload;
use anyhow::Result;
use std::collections::HashMap;
use std::sync::Arc;
@@ -48,157 +46,234 @@ impl ControllerInput {
pub struct ControllerManager {
vimputti_client: Arc<vimputti::client::VimputtiClient>,
cmd_tx: mpsc::Sender<crate::proto::proto::ProtoInput>,
cmd_tx: mpsc::Sender<Payload>,
rumble_tx: mpsc::Sender<(u32, u16, u16, u16)>, // (slot, strong, weak, duration_ms)
attach_tx: mpsc::Sender<ProtoControllerAttach>,
}
impl ControllerManager {
pub fn new(
vimputti_client: Arc<vimputti::client::VimputtiClient>,
) -> Result<(Self, mpsc::Receiver<(u32, u16, u16, u16)>)> {
let (cmd_tx, cmd_rx) = mpsc::channel(100);
let (rumble_tx, rumble_rx) = mpsc::channel(100);
) -> Result<(
Self,
mpsc::Receiver<(u32, u16, u16, u16)>,
mpsc::Receiver<ProtoControllerAttach>,
)> {
let (cmd_tx, cmd_rx) = mpsc::channel(512);
let (rumble_tx, rumble_rx) = mpsc::channel(256);
let (attach_tx, attach_rx) = mpsc::channel(64);
tokio::spawn(command_loop(
cmd_rx,
vimputti_client.clone(),
rumble_tx.clone(),
attach_tx.clone(),
));
Ok((
Self {
vimputti_client,
cmd_tx,
rumble_tx,
attach_tx,
},
rumble_rx,
attach_rx,
))
}
pub async fn send_command(&self, input: crate::proto::proto::ProtoInput) -> Result<()> {
self.cmd_tx.send(input).await?;
pub async fn send_command(&self, payload: Payload) -> Result<()> {
self.cmd_tx.send(payload).await?;
Ok(())
}
}
struct ControllerSlot {
controller: ControllerInput,
session_id: String,
last_activity: std::time::Instant,
}
// Returns first free controller slot from 0-7
fn get_free_slot(controllers: &HashMap<u32, ControllerSlot>) -> Option<u32> {
for slot in 0..8 {
if !controllers.contains_key(&slot) {
return Some(slot);
}
}
None
}
async fn command_loop(
mut cmd_rx: mpsc::Receiver<crate::proto::proto::ProtoInput>,
mut cmd_rx: mpsc::Receiver<Payload>,
vimputti_client: Arc<vimputti::client::VimputtiClient>,
rumble_tx: mpsc::Sender<(u32, u16, u16, u16)>,
attach_tx: mpsc::Sender<ProtoControllerAttach>,
) {
let mut controllers: HashMap<u32, ControllerInput> = HashMap::new();
while let Some(input) = cmd_rx.recv().await {
if let Some(input_type) = input.input_type {
match input_type {
ControllerAttach(data) => {
// Check if controller already exists in the slot, if so, ignore
if controllers.contains_key(&(data.slot as u32)) {
tracing::warn!(
"Controller slot {} already occupied, ignoring attach",
data.slot
let mut controllers: HashMap<u32, ControllerSlot> = HashMap::new();
while let Some(payload) = cmd_rx.recv().await {
match payload {
Payload::ControllerAttach(data) => {
let session_id = data.session_id.clone();
// Check if this session already has a slot (reconnection)
let existing_slot = controllers
.iter()
.find(|(_, slot)| slot.session_id == session_id && !session_id.is_empty())
.map(|(slot_num, _)| *slot_num);
let slot = existing_slot.or_else(|| get_free_slot(&controllers));
if let Some(slot) = slot {
if let Ok(mut controller) =
ControllerInput::new(data.id.clone(), &vimputti_client).await
{
let rumble_tx = rumble_tx.clone();
let attach_tx = attach_tx.clone();
controller
.device_mut()
.on_rumble(move |strong, weak, duration_ms| {
let _ = rumble_tx.try_send((slot, strong, weak, duration_ms));
})
.await
.map_err(|e| {
tracing::warn!(
"Failed to register rumble callback for slot {}: {}",
slot,
e
);
})
.ok();
// Return to attach_tx what slot was assigned
let attach_info = ProtoControllerAttach {
id: data.id.clone(),
slot: slot as i32,
session_id: session_id.clone(),
};
match attach_tx.send(attach_info).await {
Ok(_) => {
controllers.insert(
slot,
ControllerSlot {
controller,
session_id: session_id.clone(),
last_activity: std::time::Instant::now(),
},
);
tracing::info!(
"Controller {} attached to slot {} (session: {})",
data.id,
slot,
session_id
);
}
Err(e) => {
tracing::error!(
"Failed to send attach info for slot {}: {}",
slot,
e
);
}
}
} else {
tracing::error!(
"Failed to create controller of type {} for slot {}",
data.id,
slot
);
}
}
}
Payload::ControllerDetach(data) => {
if controllers.remove(&(data.slot as u32)).is_some() {
tracing::info!("Controller detached from slot {}", data.slot);
} else {
tracing::warn!("No controller found in slot {} to detach", data.slot);
}
}
Payload::ControllerButton(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
if let Some(button) = vimputti::Button::from_ev_code(data.button as u16) {
let device = controller.controller.device();
device.button(button, data.pressed);
device.sync();
}
} else {
tracing::warn!("Controller slot {} not found for button event", data.slot);
}
}
Payload::ControllerStick(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
let device = controller.controller.device();
if data.stick == 0 {
// Left stick
device.axis(vimputti::Axis::LeftStickX, data.x);
device.sync();
device.axis(vimputti::Axis::LeftStickY, data.y);
} else if data.stick == 1 {
// Right stick
device.axis(vimputti::Axis::RightStickX, data.x);
device.sync();
device.axis(vimputti::Axis::RightStickY, data.y);
}
device.sync();
} else {
tracing::warn!("Controller slot {} not found for stick event", data.slot);
}
}
Payload::ControllerTrigger(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
let device = controller.controller.device();
if data.trigger == 0 {
// Left trigger
device.axis(vimputti::Axis::LowerLeftTrigger, data.value);
} else if data.trigger == 1 {
// Right trigger
device.axis(vimputti::Axis::LowerRightTrigger, data.value);
}
device.sync();
} else {
tracing::warn!("Controller slot {} not found for trigger event", data.slot);
}
}
Payload::ControllerAxis(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
let device = controller.controller.device();
if data.axis == 0 {
// dpad x
device.axis(vimputti::Axis::DPadX, data.value);
} else if data.axis == 1 {
// dpad y
device.axis(vimputti::Axis::DPadY, data.value);
}
device.sync();
}
}
Payload::ClientDisconnected(data) => {
tracing::info!(
"Client disconnected, cleaning up controller slots: {:?} (client session: {})",
data.controller_slots,
data.session_id
);
// Remove all controllers for the disconnected slots
for slot in &data.controller_slots {
if controllers.remove(&(*slot as u32)).is_some() {
tracing::info!(
"Removed controller from slot {} (client session: {})",
slot,
data.session_id
);
} else {
if let Ok(mut controller) =
ControllerInput::new(data.id.clone(), &vimputti_client).await
{
let slot = data.slot as u32;
let rumble_tx = rumble_tx.clone();
controller
.device_mut()
.on_rumble(move |strong, weak, duration_ms| {
let _ = rumble_tx.try_send((slot, strong, weak, duration_ms));
})
.await
.map_err(|e| {
tracing::warn!(
"Failed to register rumble callback for slot {}: {}",
slot,
e
);
})
.ok();
controllers.insert(data.slot as u32, controller);
tracing::info!("Controller {} attached to slot {}", data.id, data.slot);
} else {
tracing::error!(
"Failed to create controller of type {} for slot {}",
data.id,
data.slot
);
}
tracing::warn!(
"No controller found in slot {} to cleanup (client session: {})",
slot,
data.session_id
);
}
}
ControllerDetach(data) => {
if controllers.remove(&(data.slot as u32)).is_some() {
tracing::info!("Controller detached from slot {}", data.slot);
} else {
tracing::warn!("No controller found in slot {} to detach", data.slot);
}
}
ControllerButton(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
if let Some(button) = vimputti::Button::from_ev_code(data.button as u16) {
let device = controller.device();
device.button(button, data.pressed);
device.sync();
}
} else {
tracing::warn!("Controller slot {} not found for button event", data.slot);
}
}
ControllerStick(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
let device = controller.device();
if data.stick == 0 {
// Left stick
device.axis(vimputti::Axis::LeftStickX, data.x);
device.sync();
device.axis(vimputti::Axis::LeftStickY, data.y);
} else if data.stick == 1 {
// Right stick
device.axis(vimputti::Axis::RightStickX, data.x);
device.sync();
device.axis(vimputti::Axis::RightStickY, data.y);
}
device.sync();
} else {
tracing::warn!("Controller slot {} not found for stick event", data.slot);
}
}
ControllerTrigger(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
let device = controller.device();
if data.trigger == 0 {
// Left trigger
device.axis(vimputti::Axis::LowerLeftTrigger, data.value);
} else if data.trigger == 1 {
// Right trigger
device.axis(vimputti::Axis::LowerRightTrigger, data.value);
}
device.sync();
} else {
tracing::warn!("Controller slot {} not found for trigger event", data.slot);
}
}
ControllerAxis(data) => {
if let Some(controller) = controllers.get(&(data.slot as u32)) {
let device = controller.device();
if data.axis == 0 {
// dpad x
device.axis(vimputti::Axis::DPadX, data.value);
} else if data.axis == 1 {
// dpad y
device.axis(vimputti::Axis::DPadY, data.value);
}
device.sync();
}
}
// Rumble will be outgoing event..
ControllerRumble(_) => {
//no-op
}
_ => {
//no-op
}
}
_ => {
//no-op
}
}
}