diff --git a/plugin/src/ApiContext.lua b/plugin/src/ApiContext.lua index 98890d4f6..ccdc35066 100644 --- a/plugin/src/ApiContext.lua +++ b/plugin/src/ApiContext.lua @@ -85,7 +85,7 @@ end local ApiContext = {} ApiContext.__index = ApiContext -function ApiContext.new(baseUrl) +function ApiContext.new(baseUrl,clientId) assert(type(baseUrl) == "string", "baseUrl must be a string") local self = { @@ -94,6 +94,7 @@ function ApiContext.new(baseUrl) __messageCursor = -1, __connected = true, __activeRequests = {}, + __clientId = clientId } return setmetatable(self, ApiContext) @@ -189,6 +190,7 @@ function ApiContext:write(patch) local body = { sessionId = self.__sessionId, + clientId = self.__clientId, removed = patch.removed, updated = updated, added = added, diff --git a/plugin/src/App/init.lua b/plugin/src/App/init.lua index 8c7054497..2ebb8b613 100644 --- a/plugin/src/App/init.lua +++ b/plugin/src/App/init.lua @@ -1,5 +1,6 @@ local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") +local HttpSevice = game:GetService("HttpService") local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin @@ -211,12 +212,14 @@ function App:startSession() local baseUrl = if string.find(host, "^https?://") then string.format("%s:%s", host, port) else string.format("http://%s:%s", host, port) - local apiContext = ApiContext.new(baseUrl) + local clientId = HttpSevice:GenerateGUID(false) + local apiContext = ApiContext.new(baseUrl, clientId) local serveSession = ServeSession.new({ apiContext = apiContext, openScriptsExternally = sessionOptions.openScriptsExternally, twoWaySync = sessionOptions.twoWaySync, + clientId = clientId }) serveSession:onPatchApplied(function(patch, _unapplied) diff --git a/plugin/src/ServeSession.lua b/plugin/src/ServeSession.lua index f77c8c742..25844a32a 100644 --- a/plugin/src/ServeSession.lua +++ b/plugin/src/ServeSession.lua @@ -52,6 +52,7 @@ local validateServeOptions = t.strictInterface({ apiContext = t.table, openScriptsExternally = t.boolean, twoWaySync = t.boolean, + clientId = t.string }) function ServeSession.new(options) @@ -99,6 +100,7 @@ function ServeSession.new(options) __statusChangedCallback = nil, __patchAppliedCallback = nil, __connections = connections, + __clientId = options.clientId } setmetatable(self, ServeSession) @@ -288,6 +290,10 @@ function ServeSession:__mainSyncLoop() Log.trace("Serve session {} retrieved {} messages", tostring(self), #messages) for _, message in ipairs(messages) do + if message.clientId == self.__clientId then + continue + end + local unappliedPatch = self.__reconciler:applyPatch(message) if not PatchSet.isEmpty(unappliedPatch) then diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__add_folder_subscribe.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__add_folder_subscribe.snap index 09d4512c4..000ba66b2 100644 --- a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__add_folder_subscribe.snap +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__add_folder_subscribe.snap @@ -14,6 +14,8 @@ messages: Name: my-new-folder Parent: id-2 Properties: {} + clientId: ~ removed: [] updated: [] sessionId: id-1 + diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__edit_init_subscribe.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__edit_init_subscribe.snap index cef27f2c2..d7ac7ab78 100644 --- a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__edit_init_subscribe.snap +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__edit_init_subscribe.snap @@ -1,11 +1,11 @@ --- source: tests/tests/serve.rs expression: "subscribe_response.intern_and_redact(&mut redactions, ())" - --- messageCursor: 1 messages: - added: {} + clientId: ~ removed: [] updated: - changedClassName: ~ diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__empty_json_model_subscribe.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__empty_json_model_subscribe.snap index bb747b86e..7d2d593f9 100644 --- a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__empty_json_model_subscribe.snap +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__empty_json_model_subscribe.snap @@ -1,7 +1,6 @@ --- source: tests/tests/serve.rs expression: "subscribe_response.intern_and_redact(&mut redactions, ())" - --- messageCursor: 1 messages: @@ -15,6 +14,7 @@ messages: Name: test Parent: id-2 Properties: {} + clientId: ~ removed: [] updated: [] sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__move_folder_of_stuff_subscribe.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__move_folder_of_stuff_subscribe.snap index 40afc8882..8ac4a2aa9 100644 --- a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__move_folder_of_stuff_subscribe.snap +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__move_folder_of_stuff_subscribe.snap @@ -1,7 +1,6 @@ --- source: tests/tests/serve.rs expression: "subscribe_response.intern_and_redact(&mut redactions, ())" - --- messageCursor: 1 messages: @@ -135,6 +134,7 @@ messages: Properties: Value: String: "File #5" + clientId: ~ removed: [] updated: [] sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__remove_file_subscribe.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__remove_file_subscribe.snap index e70d17f99..5cafe3415 100644 --- a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__remove_file_subscribe.snap +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__remove_file_subscribe.snap @@ -5,7 +5,9 @@ expression: "subscribe_response.intern_and_redact(&mut redactions, ())" messageCursor: 1 messages: - added: {} + clientId: ~ removed: - id-3 updated: [] sessionId: id-1 + diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__scripts_subscribe.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__scripts_subscribe.snap index 19bea59eb..5a8c3dce7 100644 --- a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__scripts_subscribe.snap +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__scripts_subscribe.snap @@ -1,11 +1,11 @@ --- source: tests/tests/serve.rs expression: "subscribe_response.intern_and_redact(&mut redactions, ())" - --- messageCursor: 1 messages: - added: {} + clientId: ~ removed: [] updated: - changedClassName: ~ diff --git a/src/snapshot/patch.rs b/src/snapshot/patch.rs index 4413db595..f7bacde64 100644 --- a/src/snapshot/patch.rs +++ b/src/snapshot/patch.rs @@ -14,6 +14,7 @@ use super::{InstanceMetadata, InstanceSnapshot}; /// conflict! #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] pub struct PatchSet { + pub client_id: Option, pub removed_instances: Vec, pub added_instances: Vec, pub updated_instances: Vec, @@ -22,6 +23,7 @@ pub struct PatchSet { impl PatchSet { pub fn new() -> Self { PatchSet { + client_id: None, removed_instances: Vec::new(), added_instances: Vec::new(), updated_instances: Vec::new(), @@ -63,14 +65,16 @@ pub struct PatchUpdate { // current values in all fields. #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct AppliedPatchSet { + pub client_id: Option, pub removed: Vec, pub added: Vec, pub updated: Vec, } impl AppliedPatchSet { - pub fn new() -> Self { + pub fn new(client_id: Option) -> Self { AppliedPatchSet { + client_id: client_id, removed: Vec::new(), added: Vec::new(), updated: Vec::new(), diff --git a/src/snapshot/patch_apply.rs b/src/snapshot/patch_apply.rs index 439452d82..13c3e9cfb 100644 --- a/src/snapshot/patch_apply.rs +++ b/src/snapshot/patch_apply.rs @@ -17,7 +17,7 @@ use super::{ /// tree in sync with Rojo's. #[profiling::function] pub fn apply_patch_set(tree: &mut RojoTree, patch_set: PatchSet) -> AppliedPatchSet { - let mut context = PatchApplyContext::default(); + let mut context = PatchApplyContext::new(patch_set.client_id); { profiling::scope!("removals"); @@ -76,6 +76,16 @@ struct PatchApplyContext { applied_patch_set: AppliedPatchSet, } +impl PatchApplyContext { + fn new(client_id: Option) -> Self { + Self { + snapshot_id_to_instance_id: HashMap::new(), + has_refs_to_rewrite: HashSet::new(), + applied_patch_set: AppliedPatchSet::new(client_id), + } + } +} + /// Finalize this patch application, consuming the context, applying any /// deferred property updates, and returning the finally applied patch set. /// diff --git a/src/snapshot/patch_compute.rs b/src/snapshot/patch_compute.rs index 23e2f3024..49f932d9f 100644 --- a/src/snapshot/patch_compute.rs +++ b/src/snapshot/patch_compute.rs @@ -260,6 +260,7 @@ mod test { let patch_set = compute_patch_set(Some(snapshot), &tree, root_id); let expected_patch_set = PatchSet { + client_id: None, updated_instances: vec![PatchUpdate { id: root_id, changed_name: None, @@ -310,6 +311,7 @@ mod test { let patch_set = compute_patch_set(Some(snapshot), &tree, root_id); let expected_patch_set = PatchSet { + client_id: None, added_instances: vec![PatchAdd { parent_id: root_id, instance: InstanceSnapshot { diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__add_property-2.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__add_property-2.snap index 4fc4f9ab3..f4e9e9b2d 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__add_property-2.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__add_property-2.snap @@ -1,8 +1,8 @@ --- source: src/snapshot/tests/apply.rs expression: applied_patch_value - --- +client_id: ~ removed: [] added: [] updated: diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__remove_property_appied_patch.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__remove_property_appied_patch.snap index 640a62c08..0670c6e4e 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__remove_property_appied_patch.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__remove_property_appied_patch.snap @@ -2,6 +2,7 @@ source: src/snapshot/tests/apply.rs expression: applied_patch_value --- +client_id: ~ removed: [] added: [] updated: @@ -11,3 +12,4 @@ updated: changed_properties: Foo: ~ changed_metadata: ~ + diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__set_name_and_class_name-2.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__set_name_and_class_name-2.snap index 25a101317..956c9a7ff 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__set_name_and_class_name-2.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__apply__set_name_and_class_name-2.snap @@ -2,6 +2,7 @@ source: src/snapshot/tests/apply.rs expression: applied_patch_value --- +client_id: ~ removed: [] added: [] updated: @@ -10,3 +11,4 @@ updated: changed_class_name: Folder changed_properties: {} changed_metadata: ~ + diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__add_child.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__add_child.snap index 97bd639ec..4d6505d32 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__add_child.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__add_child.snap @@ -2,6 +2,7 @@ source: src/snapshot/tests/compute.rs expression: patch_value --- +client_id: ~ removed_instances: [] added_instances: - parent_id: id-1 @@ -16,3 +17,4 @@ added_instances: properties: {} children: [] updated_instances: [] + diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_child.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_child.snap index 16295d078..60efb80c0 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_child.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_child.snap @@ -2,7 +2,9 @@ source: src/snapshot/tests/compute.rs expression: patch_value --- +client_id: ~ removed_instances: - id-2 added_instances: [] updated_instances: [] + diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_property.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_property.snap index 44e810981..d2d2c3088 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_property.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__remove_property.snap @@ -2,6 +2,7 @@ source: src/snapshot/tests/compute.rs expression: patch_value --- +client_id: ~ removed_instances: [] added_instances: [] updated_instances: @@ -11,3 +12,4 @@ updated_instances: changed_properties: Foo: ~ changed_metadata: ~ + diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_name_and_class_name.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_name_and_class_name.snap index f949a9013..bdb1f4e99 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_name_and_class_name.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_name_and_class_name.snap @@ -2,6 +2,7 @@ source: src/snapshot/tests/compute.rs expression: patch_value --- +client_id: ~ removed_instances: [] added_instances: [] updated_instances: @@ -10,3 +11,4 @@ updated_instances: changed_class_name: Folder changed_properties: {} changed_metadata: ~ + diff --git a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_property.snap b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_property.snap index fbaae3b95..65ee6d677 100644 --- a/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_property.snap +++ b/src/snapshot/tests/snapshots/librojo__snapshot__tests__compute__set_property.snap @@ -1,8 +1,8 @@ --- source: src/snapshot/tests/compute.rs expression: patch_value - --- +client_id: ~ removed_instances: [] added_instances: [] updated_instances: diff --git a/src/web/api.rs b/src/web/api.rs index 8f500116d..74c9b6e99 100644 --- a/src/web/api.rs +++ b/src/web/api.rs @@ -152,6 +152,7 @@ impl ApiService { tree_mutation_sender .send(PatchSet { + client_id: request.client_id, removed_instances: Vec::new(), added_instances: Vec::new(), updated_instances, diff --git a/src/web/interface.rs b/src/web/interface.rs index 28005057c..a732aed03 100644 --- a/src/web/interface.rs +++ b/src/web/interface.rs @@ -27,6 +27,7 @@ pub const PROTOCOL_VERSION: u64 = 4; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubscribeMessage<'a> { + pub client_id: Option, pub removed: Vec, pub added: HashMap>, pub updated: Vec, @@ -72,6 +73,7 @@ impl<'a> SubscribeMessage<'a> { .collect(); Self { + client_id: patch.client_id, removed, added, updated, @@ -179,6 +181,7 @@ pub struct ReadResponse<'a> { #[serde(rename_all = "camelCase")] pub struct WriteRequest { pub session_id: SessionId, + pub client_id: Option, pub removed: Vec, #[serde(default)]