Introduce the ability of creating rooms on the server

Antonio Scandurra created

Change summary

Cargo.lock                             |   1 
crates/client/src/call.rs              |   8 
crates/client/src/client.rs            |   1 
crates/client/src/user.rs              |  19 +
crates/collab/src/integration_tests.rs |  19 +
crates/collab/src/rpc.rs               |  11 +
crates/collab/src/rpc/store.rs         |  40 +++-
crates/room/Cargo.toml                 |   3 
crates/room/src/participant.rs         |  28 ++
crates/room/src/room.rs                |  62 +++++-
crates/rpc/proto/zed.proto             | 250 +++++++++++++++++----------
crates/rpc/src/proto.rs                |   8 
12 files changed, 312 insertions(+), 138 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -4458,6 +4458,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "client",
+ "collections",
  "gpui",
  "project",
 ]

crates/client/src/call.rs 🔗

@@ -0,0 +1,8 @@
+use crate::User;
+use std::sync::Arc;
+
+#[derive(Clone)]
+pub struct Call {
+    pub from: Vec<Arc<User>>,
+    pub room_id: u64,
+}

crates/client/src/client.rs 🔗

@@ -1,6 +1,7 @@
 #[cfg(any(test, feature = "test-support"))]
 pub mod test;
 
+pub mod call;
 pub mod channel;
 pub mod http;
 pub mod user;

crates/client/src/user.rs 🔗

@@ -1,9 +1,11 @@
+use crate::call::Call;
+
 use super::{http::HttpClient, proto, Client, Status, TypedEnvelope};
 use anyhow::{anyhow, Context, Result};
 use collections::{hash_map::Entry, BTreeSet, HashMap, HashSet};
-use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
+use futures::{channel::mpsc, future, AsyncReadExt, Future, Stream, StreamExt};
 use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task};
-use postage::{prelude::Stream, sink::Sink, watch};
+use postage::{broadcast, sink::Sink, watch};
 use rpc::proto::{RequestMessage, UsersResponse};
 use std::sync::{Arc, Weak};
 use util::TryFutureExt as _;
@@ -66,6 +68,7 @@ pub struct UserStore {
     outgoing_contact_requests: Vec<Arc<User>>,
     pending_contact_requests: HashMap<u64, usize>,
     invite_info: Option<InviteInfo>,
+    incoming_calls: broadcast::Sender<Call>,
     client: Weak<Client>,
     http: Arc<dyn HttpClient>,
     _maintain_contacts: Task<()>,
@@ -116,6 +119,7 @@ impl UserStore {
             client.add_message_handler(cx.handle(), Self::handle_update_invite_info),
             client.add_message_handler(cx.handle(), Self::handle_show_contacts),
         ];
+        let (incoming_calls, _) = broadcast::channel(32);
         Self {
             users: Default::default(),
             current_user: current_user_rx,
@@ -123,6 +127,7 @@ impl UserStore {
             incoming_contact_requests: Default::default(),
             outgoing_contact_requests: Default::default(),
             invite_info: None,
+            incoming_calls,
             client: Arc::downgrade(&client),
             update_contacts_tx,
             http,
@@ -138,7 +143,7 @@ impl UserStore {
             }),
             _maintain_current_user: cx.spawn_weak(|this, mut cx| async move {
                 let mut status = client.status();
-                while let Some(status) = status.recv().await {
+                while let Some(status) = status.next().await {
                     match status {
                         Status::Connected { .. } => {
                             if let Some((this, user_id)) = this.upgrade(&cx).zip(client.user_id()) {
@@ -198,6 +203,10 @@ impl UserStore {
         self.invite_info.as_ref()
     }
 
+    pub fn incoming_calls(&self) -> impl 'static + Stream<Item = Call> {
+        self.incoming_calls.subscribe()
+    }
+
     async fn handle_update_contacts(
         this: ModelHandle<Self>,
         message: TypedEnvelope<proto::UpdateContacts>,
@@ -493,7 +502,7 @@ impl UserStore {
             .unbounded_send(UpdateContacts::Clear(tx))
             .unwrap();
         async move {
-            rx.recv().await;
+            rx.next().await;
         }
     }
 
@@ -503,7 +512,7 @@ impl UserStore {
             .unbounded_send(UpdateContacts::Wait(tx))
             .unwrap();
         async move {
-            rx.recv().await;
+            rx.next().await;
         }
     }
 

crates/collab/src/integration_tests.rs 🔗

@@ -34,6 +34,7 @@ use project::{
     DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId,
 };
 use rand::prelude::*;
+use room::Room;
 use rpc::PeerId;
 use serde_json::json;
 use settings::{Formatter, Settings};
@@ -90,14 +91,24 @@ async fn test_share_project_in_room(
         )
         .await;
 
-    let room_a = Room::create(client_a.clone()).await.unwrap();
+    let room_a = cx_a
+        .update(|cx| Room::create(client_a.clone(), cx))
+        .await
+        .unwrap();
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
     // room.publish_project(project_a.clone()).await.unwrap();
 
-    let incoming_calls_b = client_b.user_store.incoming_calls();
-    let user_b_joined = room_a.invite(client_b.user_id().unwrap());
+    let mut incoming_calls_b = client_b
+        .user_store
+        .read_with(cx_b, |user, _| user.incoming_calls());
+    let user_b_joined = room_a.update(cx_a, |room, cx| {
+        room.invite(client_b.user_id().unwrap(), cx)
+    });
     let call_b = incoming_calls_b.next().await.unwrap();
-    let room_b = Room::join(call_b.room_id, client_b.clone()).await.unwrap();
+    let room_b = cx_b
+        .update(|cx| Room::join(call_b.room_id, client_b.clone(), cx))
+        .await
+        .unwrap();
     user_b_joined.await.unwrap();
 }
 

crates/collab/src/rpc.rs 🔗

@@ -151,6 +151,7 @@ impl Server {
 
         server
             .add_request_handler(Server::ping)
+            .add_request_handler(Server::create_room)
             .add_request_handler(Server::register_project)
             .add_request_handler(Server::unregister_project)
             .add_request_handler(Server::join_project)
@@ -593,6 +594,16 @@ impl Server {
         Ok(())
     }
 
+    async fn create_room(
+        self: Arc<Server>,
+        request: TypedEnvelope<proto::CreateRoom>,
+        response: Response<proto::CreateRoom>,
+    ) -> Result<()> {
+        let room_id = self.store().await.create_room(request.sender_id)?;
+        response.send(proto::CreateRoomResponse { id: room_id })?;
+        Ok(())
+    }
+
     async fn register_project(
         self: Arc<Server>,
         request: TypedEnvelope<proto::RegisterProject>,

crates/collab/src/rpc/store.rs 🔗

@@ -6,6 +6,7 @@ use serde::Serialize;
 use std::{mem, path::PathBuf, str, time::Duration};
 use time::OffsetDateTime;
 use tracing::instrument;
+use util::post_inc;
 
 pub type RoomId = u64;
 
@@ -13,7 +14,8 @@ pub type RoomId = u64;
 pub struct Store {
     connections: BTreeMap<ConnectionId, ConnectionState>,
     connections_by_user_id: BTreeMap<UserId, HashSet<ConnectionId>>,
-    rooms: BTreeMap<RoomId, Room>,
+    next_room_id: RoomId,
+    rooms: BTreeMap<RoomId, proto::Room>,
     projects: BTreeMap<ProjectId, Project>,
     #[serde(skip)]
     channels: BTreeMap<ChannelId, Channel>,
@@ -23,22 +25,12 @@ pub struct Store {
 struct ConnectionState {
     user_id: UserId,
     admin: bool,
+    rooms: BTreeSet<RoomId>,
     projects: BTreeSet<ProjectId>,
     requested_projects: HashSet<ProjectId>,
     channels: HashSet<ChannelId>,
 }
 
-#[derive(Serialize)]
-struct Room {
-    participants: HashMap<ConnectionId, Participant>,
-}
-
-#[derive(Serialize)]
-struct Participant {
-    user_id: UserId,
-    shared_projects: HashSet<ProjectId>,
-}
-
 #[derive(Serialize)]
 pub struct Project {
     pub online: bool,
@@ -148,6 +140,7 @@ impl Store {
             ConnectionState {
                 user_id,
                 admin,
+                rooms: Default::default(),
                 projects: Default::default(),
                 requested_projects: Default::default(),
                 channels: Default::default(),
@@ -335,6 +328,29 @@ impl Store {
         metadata
     }
 
+    pub fn create_room(&mut self, creator_connection_id: ConnectionId) -> Result<RoomId> {
+        let connection = self
+            .connections
+            .get_mut(&creator_connection_id)
+            .ok_or_else(|| anyhow!("no such connection"))?;
+        let mut room = proto::Room::default();
+        room.participants.push(proto::Participant {
+            user_id: connection.user_id.to_proto(),
+            peer_id: creator_connection_id.0,
+            project_ids: Default::default(),
+            location: Some(proto::ParticipantLocation {
+                variant: Some(proto::participant_location::Variant::External(
+                    proto::participant_location::External {},
+                )),
+            }),
+        });
+
+        let room_id = post_inc(&mut self.next_room_id);
+        self.rooms.insert(room_id, room);
+        connection.rooms.insert(room_id);
+        Ok(room_id)
+    }
+
     pub fn register_project(
         &mut self,
         host_connection_id: ConnectionId,

crates/room/Cargo.toml 🔗

@@ -10,6 +10,7 @@ doctest = false
 [features]
 test-support = [
     "client/test-support",
+    "collections/test-support",
     "gpui/test-support",
     "project/test-support",
 ]
@@ -17,10 +18,12 @@ test-support = [
 [dependencies]
 anyhow = "1.0.38"
 client = { path = "../client" }
+collections = { path = "../collections" }
 gpui = { path = "../gpui" }
 project = { path = "../project" }
 
 [dev-dependencies]
 client = { path = "../client", features = ["test-support"] }
+collections = { path = "../collections", features = ["test-support"] }
 gpui = { path = "../gpui", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }

crates/room/src/participant.rs 🔗

@@ -1,19 +1,31 @@
-use client::User;
+use anyhow::{anyhow, Result};
+use client::proto;
 use gpui::ModelHandle;
 use project::Project;
 
-pub enum Location {
-    Project { project_id: usize },
+pub enum ParticipantLocation {
+    Project { project_id: u64 },
     External,
 }
 
+impl ParticipantLocation {
+    pub fn from_proto(location: Option<proto::ParticipantLocation>) -> Result<Self> {
+        match location.and_then(|l| l.variant) {
+            Some(proto::participant_location::Variant::Project(project)) => Ok(Self::Project {
+                project_id: project.id,
+            }),
+            Some(proto::participant_location::Variant::External(_)) => Ok(Self::External),
+            None => Err(anyhow!("participant location was not provided")),
+        }
+    }
+}
+
 pub struct LocalParticipant {
-    user: User,
-    projects: Vec<ModelHandle<Project>>,
+    pub projects: Vec<ModelHandle<Project>>,
 }
 
 pub struct RemoteParticipant {
-    user: User,
-    projects: Vec<ModelHandle<Project>>,
-    location: Location,
+    pub user_id: u64,
+    pub projects: Vec<ModelHandle<Project>>,
+    pub location: ParticipantLocation,
 }

crates/room/src/room.rs 🔗

@@ -1,11 +1,12 @@
 mod participant;
 
-use anyhow::Result;
-use client::{Client, PeerId};
-use gpui::{Entity, ModelHandle};
-use participant::{LocalParticipant, RemoteParticipant};
+use anyhow::{anyhow, Result};
+use client::{proto, Client, PeerId};
+use collections::HashMap;
+use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task};
+use participant::{LocalParticipant, ParticipantLocation, RemoteParticipant};
 use project::Project;
-use std::{collections::HashMap, sync::Arc};
+use std::sync::Arc;
 
 pub enum Event {
     PeerChangedActiveProject,
@@ -23,15 +24,56 @@ impl Entity for Room {
 }
 
 impl Room {
-    pub async fn create(client: Arc<Client>) -> Result<u64> {
-        todo!()
+    pub fn create(
+        client: Arc<Client>,
+        cx: &mut MutableAppContext,
+    ) -> Task<Result<ModelHandle<Self>>> {
+        cx.spawn(|mut cx| async move {
+            let room = client.request(proto::CreateRoom {}).await?;
+            Ok(cx.add_model(|cx| Self::new(room.id, client, cx)))
+        })
     }
 
-    pub async fn join(id: u64, client: Arc<Client>) -> Result<Self> {
-        todo!()
+    pub fn join(
+        id: u64,
+        client: Arc<Client>,
+        cx: &mut MutableAppContext,
+    ) -> Task<Result<ModelHandle<Self>>> {
+        cx.spawn(|mut cx| async move {
+            let response = client.request(proto::JoinRoom { id }).await?;
+            let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
+            let room = cx.add_model(|cx| Self::new(id, client, cx));
+            room.update(&mut cx, |room, cx| room.refresh(room_proto, cx))?;
+            Ok(room)
+        })
+    }
+
+    fn new(id: u64, client: Arc<Client>, _: &mut ModelContext<Self>) -> Self {
+        Self {
+            id,
+            local_participant: LocalParticipant {
+                projects: Default::default(),
+            },
+            remote_participants: Default::default(),
+            client,
+        }
+    }
+
+    fn refresh(&mut self, room: proto::Room, cx: &mut ModelContext<Self>) -> Result<()> {
+        for participant in room.participants {
+            self.remote_participants.insert(
+                PeerId(participant.peer_id),
+                RemoteParticipant {
+                    user_id: participant.user_id,
+                    projects: Default::default(), // TODO: populate projects
+                    location: ParticipantLocation::from_proto(participant.location)?,
+                },
+            );
+        }
+        Ok(())
     }
 
-    pub async fn invite(&mut self, user_id: u64) -> Result<()> {
+    pub fn invite(&mut self, user_id: u64, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
         todo!()
     }
 

crates/rpc/proto/zed.proto 🔗

@@ -10,104 +10,112 @@ message Envelope {
         Error error = 5;
         Ping ping = 6;
         Test test = 7;
-
-        RegisterProject register_project = 8;
-        RegisterProjectResponse register_project_response = 9;
-        UnregisterProject unregister_project = 10;
-        RequestJoinProject request_join_project = 11;
-        RespondToJoinProjectRequest respond_to_join_project_request = 12;
-        JoinProjectRequestCancelled join_project_request_cancelled = 13;
-        JoinProject join_project = 14;
-        JoinProjectResponse join_project_response = 15;
-        LeaveProject leave_project = 16;
-        AddProjectCollaborator add_project_collaborator = 17;
-        RemoveProjectCollaborator remove_project_collaborator = 18;
-        ProjectUnshared project_unshared = 19;
-
-        GetDefinition get_definition = 20;
-        GetDefinitionResponse get_definition_response = 21;
-        GetTypeDefinition get_type_definition = 22;
-        GetTypeDefinitionResponse get_type_definition_response = 23;
-        GetReferences get_references = 24;
-        GetReferencesResponse get_references_response = 25;
-        GetDocumentHighlights get_document_highlights = 26;
-        GetDocumentHighlightsResponse get_document_highlights_response = 27;
-        GetProjectSymbols get_project_symbols = 28;
-        GetProjectSymbolsResponse get_project_symbols_response = 29;
-        OpenBufferForSymbol open_buffer_for_symbol = 30;
-        OpenBufferForSymbolResponse open_buffer_for_symbol_response = 31;
-
-        UpdateProject update_project = 32;
-        RegisterProjectActivity register_project_activity = 33;
-        UpdateWorktree update_worktree = 34;
-        UpdateWorktreeExtensions update_worktree_extensions = 35;
-
-        CreateProjectEntry create_project_entry = 36;
-        RenameProjectEntry rename_project_entry = 37;
-        CopyProjectEntry copy_project_entry = 38;
-        DeleteProjectEntry delete_project_entry = 39;
-        ProjectEntryResponse project_entry_response = 40;
-
-        UpdateDiagnosticSummary update_diagnostic_summary = 41;
-        StartLanguageServer start_language_server = 42;
-        UpdateLanguageServer update_language_server = 43;
-
-        OpenBufferById open_buffer_by_id = 44;
-        OpenBufferByPath open_buffer_by_path = 45;
-        OpenBufferResponse open_buffer_response = 46;
-        CreateBufferForPeer create_buffer_for_peer = 47;
-        UpdateBuffer update_buffer = 48;
-        UpdateBufferFile update_buffer_file = 49;
-        SaveBuffer save_buffer = 50;
-        BufferSaved buffer_saved = 51;
-        BufferReloaded buffer_reloaded = 52;
-        ReloadBuffers reload_buffers = 53;
-        ReloadBuffersResponse reload_buffers_response = 54;
-        FormatBuffers format_buffers = 55;
-        FormatBuffersResponse format_buffers_response = 56;
-        GetCompletions get_completions = 57;
-        GetCompletionsResponse get_completions_response = 58;
-        ApplyCompletionAdditionalEdits apply_completion_additional_edits = 59;
-        ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 60;
-        GetCodeActions get_code_actions = 61;
-        GetCodeActionsResponse get_code_actions_response = 62;
-        GetHover get_hover = 63;
-        GetHoverResponse get_hover_response = 64;
-        ApplyCodeAction apply_code_action = 65;
-        ApplyCodeActionResponse apply_code_action_response = 66;
-        PrepareRename prepare_rename = 67;
-        PrepareRenameResponse prepare_rename_response = 68;
-        PerformRename perform_rename = 69;
-        PerformRenameResponse perform_rename_response = 70;
-        SearchProject search_project = 71;
-        SearchProjectResponse search_project_response = 72;
-
-        GetChannels get_channels = 73;
-        GetChannelsResponse get_channels_response = 74;
-        JoinChannel join_channel = 75;
-        JoinChannelResponse join_channel_response = 76;
-        LeaveChannel leave_channel = 77;
-        SendChannelMessage send_channel_message = 78;
-        SendChannelMessageResponse send_channel_message_response = 79;
-        ChannelMessageSent channel_message_sent = 80;
-        GetChannelMessages get_channel_messages = 81;
-        GetChannelMessagesResponse get_channel_messages_response = 82;
-
-        UpdateContacts update_contacts = 83;
-        UpdateInviteInfo update_invite_info = 84;
-        ShowContacts show_contacts = 85;
-
-        GetUsers get_users = 86;
-        FuzzySearchUsers fuzzy_search_users = 87;
-        UsersResponse users_response = 88;
-        RequestContact request_contact = 89;
-        RespondToContactRequest respond_to_contact_request = 90;
-        RemoveContact remove_contact = 91;
-
-        Follow follow = 92;
-        FollowResponse follow_response = 93;
-        UpdateFollowers update_followers = 94;
-        Unfollow unfollow = 95;
+        
+        CreateRoom create_room = 8;
+        CreateRoomResponse create_room_response = 9;
+        JoinRoom join_room = 10;
+        JoinRoomResponse join_room_response = 11;
+        Call call = 12;
+        CallResponse call_response = 13;
+        RoomUpdated room_updated = 14;
+
+        RegisterProject register_project = 15;
+        RegisterProjectResponse register_project_response = 16;
+        UnregisterProject unregister_project = 17;
+        RequestJoinProject request_join_project = 18;
+        RespondToJoinProjectRequest respond_to_join_project_request = 19;
+        JoinProjectRequestCancelled join_project_request_cancelled = 20;
+        JoinProject join_project = 21;
+        JoinProjectResponse join_project_response = 22;
+        LeaveProject leave_project = 23;
+        AddProjectCollaborator add_project_collaborator = 24;
+        RemoveProjectCollaborator remove_project_collaborator = 25;
+        ProjectUnshared project_unshared = 26;
+
+        GetDefinition get_definition = 27;
+        GetDefinitionResponse get_definition_response = 28;
+        GetTypeDefinition get_type_definition = 29;
+        GetTypeDefinitionResponse get_type_definition_response = 30;
+        GetReferences get_references = 31;
+        GetReferencesResponse get_references_response = 32;
+        GetDocumentHighlights get_document_highlights = 33;
+        GetDocumentHighlightsResponse get_document_highlights_response = 34;
+        GetProjectSymbols get_project_symbols = 35;
+        GetProjectSymbolsResponse get_project_symbols_response = 36;
+        OpenBufferForSymbol open_buffer_for_symbol = 37;
+        OpenBufferForSymbolResponse open_buffer_for_symbol_response = 38;
+
+        UpdateProject update_project = 39;
+        RegisterProjectActivity register_project_activity = 40;
+        UpdateWorktree update_worktree = 41;
+        UpdateWorktreeExtensions update_worktree_extensions = 42;
+
+        CreateProjectEntry create_project_entry = 43;
+        RenameProjectEntry rename_project_entry = 44;
+        CopyProjectEntry copy_project_entry = 45;
+        DeleteProjectEntry delete_project_entry = 46;
+        ProjectEntryResponse project_entry_response = 47;
+
+        UpdateDiagnosticSummary update_diagnostic_summary = 48;
+        StartLanguageServer start_language_server = 49;
+        UpdateLanguageServer update_language_server = 50;
+
+        OpenBufferById open_buffer_by_id = 51;
+        OpenBufferByPath open_buffer_by_path = 52;
+        OpenBufferResponse open_buffer_response = 53;
+        CreateBufferForPeer create_buffer_for_peer = 54;
+        UpdateBuffer update_buffer = 55;
+        UpdateBufferFile update_buffer_file = 56;
+        SaveBuffer save_buffer = 57;
+        BufferSaved buffer_saved = 58;
+        BufferReloaded buffer_reloaded = 59;
+        ReloadBuffers reload_buffers = 60;
+        ReloadBuffersResponse reload_buffers_response = 61;
+        FormatBuffers format_buffers = 62;
+        FormatBuffersResponse format_buffers_response = 63;
+        GetCompletions get_completions = 64;
+        GetCompletionsResponse get_completions_response = 65;
+        ApplyCompletionAdditionalEdits apply_completion_additional_edits = 66;
+        ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 67;
+        GetCodeActions get_code_actions = 68;
+        GetCodeActionsResponse get_code_actions_response = 69;
+        GetHover get_hover = 70;
+        GetHoverResponse get_hover_response = 71;
+        ApplyCodeAction apply_code_action = 72;
+        ApplyCodeActionResponse apply_code_action_response = 73;
+        PrepareRename prepare_rename = 74;
+        PrepareRenameResponse prepare_rename_response = 75;
+        PerformRename perform_rename = 76;
+        PerformRenameResponse perform_rename_response = 77;
+        SearchProject search_project = 78;
+        SearchProjectResponse search_project_response = 79;
+
+        GetChannels get_channels = 80;
+        GetChannelsResponse get_channels_response = 81;
+        JoinChannel join_channel = 82;
+        JoinChannelResponse join_channel_response = 83;
+        LeaveChannel leave_channel = 84;
+        SendChannelMessage send_channel_message = 85;
+        SendChannelMessageResponse send_channel_message_response = 86;
+        ChannelMessageSent channel_message_sent = 87;
+        GetChannelMessages get_channel_messages = 88;
+        GetChannelMessagesResponse get_channel_messages_response = 89;
+
+        UpdateContacts update_contacts = 90;
+        UpdateInviteInfo update_invite_info = 91;
+        ShowContacts show_contacts = 92;
+
+        GetUsers get_users = 93;
+        FuzzySearchUsers fuzzy_search_users = 94;
+        UsersResponse users_response = 95;
+        RequestContact request_contact = 96;
+        RespondToContactRequest respond_to_contact_request = 97;
+        RemoveContact remove_contact = 98;
+
+        Follow follow = 99;
+        FollowResponse follow_response = 100;
+        UpdateFollowers update_followers = 101;
+        Unfollow unfollow = 102;
     }
 }
 
@@ -125,6 +133,52 @@ message Test {
     uint64 id = 1;
 }
 
+message CreateRoom {}
+
+message CreateRoomResponse {
+    uint64 id = 1;
+}
+
+message JoinRoom {
+    uint64 id = 1;
+}
+
+message JoinRoomResponse {
+    Room room = 1;
+}
+
+message Room {
+    repeated Participant participants = 1;
+}
+
+message Participant {
+    uint64 user_id = 1;
+    uint32 peer_id = 2;
+    repeated uint64 project_ids = 3;
+    ParticipantLocation location = 4;
+}
+
+message ParticipantLocation {
+    oneof variant {
+        Project project = 1;
+        External external = 2;
+    }
+    
+    message Project {
+        uint64 id = 1;
+    }
+    
+    message External {}
+}
+
+message Call {}
+
+message CallResponse {}
+
+message RoomUpdated {
+    Room room = 1;
+}
+
 message RegisterProject {
     bool online = 1;
 }

crates/rpc/src/proto.rs 🔗

@@ -83,11 +83,12 @@ messages!(
     (ApplyCompletionAdditionalEditsResponse, Background),
     (BufferReloaded, Foreground),
     (BufferSaved, Foreground),
-    (RemoveContact, Foreground),
     (ChannelMessageSent, Foreground),
     (CopyProjectEntry, Foreground),
     (CreateBufferForPeer, Foreground),
     (CreateProjectEntry, Foreground),
+    (CreateRoom, Foreground),
+    (CreateRoomResponse, Foreground),
     (DeleteProjectEntry, Foreground),
     (Error, Foreground),
     (Follow, Foreground),
@@ -122,6 +123,8 @@ messages!(
     (JoinProject, Foreground),
     (JoinProjectResponse, Foreground),
     (JoinProjectRequestCancelled, Foreground),
+    (JoinRoom, Foreground),
+    (JoinRoomResponse, Foreground),
     (LeaveChannel, Foreground),
     (LeaveProject, Foreground),
     (OpenBufferById, Background),
@@ -136,6 +139,7 @@ messages!(
     (ProjectEntryResponse, Foreground),
     (ProjectUnshared, Foreground),
     (RegisterProjectResponse, Foreground),
+    (RemoveContact, Foreground),
     (Ping, Foreground),
     (RegisterProject, Foreground),
     (RegisterProjectActivity, Foreground),
@@ -177,6 +181,7 @@ request_messages!(
     ),
     (CopyProjectEntry, ProjectEntryResponse),
     (CreateProjectEntry, ProjectEntryResponse),
+    (CreateRoom, CreateRoomResponse),
     (DeleteProjectEntry, ProjectEntryResponse),
     (Follow, FollowResponse),
     (FormatBuffers, FormatBuffersResponse),
@@ -194,6 +199,7 @@ request_messages!(
     (GetUsers, UsersResponse),
     (JoinChannel, JoinChannelResponse),
     (JoinProject, JoinProjectResponse),
+    (JoinRoom, JoinRoomResponse),
     (OpenBufferById, OpenBufferResponse),
     (OpenBufferByPath, OpenBufferResponse),
     (OpenBufferForSymbol, OpenBufferForSymbolResponse),