added support to use existing user ids! Fixes #23

This commit is contained in:
Kevin Jahns 2015-05-28 15:44:13 +02:00
parent a9c2ec6ba0
commit dc3c6a5d42
14 changed files with 39358 additions and 508 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -154,7 +154,7 @@ module.exports = {
this.send(user, { this.send(user, {
sync_step: "getHB", sync_step: "getHB",
send_again: "true", send_again: "true",
data: [] data: this.getStateVector()
}); });
if (!this.sent_hb_to_all_users) { if (!this.sent_hb_to_all_users) {
this.sent_hb_to_all_users = true; this.sent_hb_to_all_users = true;
@ -184,7 +184,7 @@ module.exports = {
this.send(user, { this.send(user, {
sync_step: "getHB", sync_step: "getHB",
send_again: "true", send_again: "true",
data: [] data: this.getStateVector()
}); });
hb = this.getHB([]).hb; hb = this.getHB([]).hb;
_hb = []; _hb = [];
@ -221,8 +221,14 @@ module.exports = {
return null; return null;
} }
}, },
whenReceivedStateVector: function(f) {
if (this.when_received_state_vector_listeners == null) {
this.when_received_state_vector_listeners = [];
}
return this.when_received_state_vector_listeners.push(f);
},
receiveMessage: function(sender, res) { receiveMessage: function(sender, res) {
var _hb, data, f, hb, i, j, len, len1, o, ref, results, sendApplyHB, send_again; var _hb, data, f, hb, i, j, k, len, len1, len2, o, ref, ref1, results, sendApplyHB, send_again;
if (res.sync_step == null) { if (res.sync_step == null) {
ref = this.receive_handlers; ref = this.receive_handlers;
results = []; results = [];
@ -236,6 +242,14 @@ module.exports = {
return; return;
} }
if (res.sync_step === "getHB") { if (res.sync_step === "getHB") {
if (this.when_received_state_vector_listeners != null) {
ref1 = this.when_received_state_vector_listeners;
for (j = 0, len1 = ref1.length; j < len1; j++) {
f = ref1[j];
f.call(this, res.data);
}
}
delete this.when_received_state_vector_listeners;
data = this.getHB(res.data); data = this.getHB(res.data);
hb = data.hb; hb = data.hb;
_hb = []; _hb = [];
@ -252,8 +266,8 @@ module.exports = {
}; };
})(this); })(this);
} }
for (j = 0, len1 = hb.length; j < len1; j++) { for (k = 0, len2 = hb.length; k < len2; k++) {
o = hb[j]; o = hb[k];
_hb.push(o); _hb.push(o);
if (_hb.length > 10) { if (_hb.length > 10) {
sendApplyHB({ sendApplyHB({
@ -271,10 +285,10 @@ module.exports = {
send_again = (function(_this) { send_again = (function(_this) {
return function(sv) { return function(sv) {
return function() { return function() {
var k, len2; var l, len3;
hb = _this.getHB(sv).hb; hb = _this.getHB(sv).hb;
for (k = 0, len2 = hb.length; k < len2; k++) { for (l = 0, len3 = hb.length; l < len3; l++) {
o = hb[k]; o = hb[l];
_hb.push(o); _hb.push(o);
if (_hb.length > 10) { if (_hb.length > 10) {
_this.send(sender, { _this.send(sender, {

View File

@ -16,30 +16,26 @@ HistoryBuffer = (function() {
setTimeout(this.emptyGarbage, this.garbageCollectTimeout); setTimeout(this.emptyGarbage, this.garbageCollectTimeout);
} }
HistoryBuffer.prototype.resetUserId = function(id) { HistoryBuffer.prototype.setUserId = function(user_id1, state_vector) {
var o, o_name, own; var base, buff, counter_diff, name, o, o_name, ref;
own = this.buffer[this.user_id]; this.user_id = user_id1;
if (own != null) { if ((base = this.buffer)[name = this.user_id] == null) {
for (o_name in own) { base[name] = [];
o = own[o_name];
if (o.uid.creator != null) {
o.uid.creator = id;
}
if (o.uid.alt != null) {
o.uid.alt.creator = id;
}
}
if (this.buffer[id] != null) {
throw new Error("You are re-assigning an old user id - this is not (yet) possible!");
}
this.buffer[id] = own;
delete this.buffer[this.user_id];
} }
if (this.operation_counter[this.user_id] != null) { buff = this.buffer[this.user_id];
this.operation_counter[id] = this.operation_counter[this.user_id]; counter_diff = state_vector[this.user_id] || 0;
delete this.operation_counter[this.user_id]; if (this.buffer._temp != null) {
ref = this.buffer._temp;
for (o_name in ref) {
o = ref[o_name];
o.uid.creator = this.user_id;
o.uid.op_number += counter_diff;
buff[o.uid.op_number] = o;
}
} }
return this.user_id = id; this.operation_counter[this.user_id] = (this.operation_counter._temp || 0) + counter_diff;
delete this.operation_counter._temp;
return delete this.buffer._temp;
}; };
HistoryBuffer.prototype.emptyGarbage = function() { HistoryBuffer.prototype.emptyGarbage = function() {
@ -243,15 +239,13 @@ HistoryBuffer = (function() {
if ((base = this.operation_counter)[name = o.uid.creator] == null) { if ((base = this.operation_counter)[name = o.uid.creator] == null) {
base[name] = 0; base[name] = 0;
} }
if (o.uid.creator !== this.getUserId()) { if (o.uid.op_number === this.operation_counter[o.uid.creator]) {
if (o.uid.op_number === this.operation_counter[o.uid.creator]) { this.operation_counter[o.uid.creator]++;
this.operation_counter[o.uid.creator]++;
}
while (this.buffer[o.uid.creator][this.operation_counter[o.uid.creator]] != null) {
this.operation_counter[o.uid.creator]++;
}
return void 0;
} }
while (this.buffer[o.uid.creator][this.operation_counter[o.uid.creator]] != null) {
this.operation_counter[o.uid.creator]++;
}
return void 0;
}; };
return HistoryBuffer; return HistoryBuffer;

View File

@ -10,15 +10,15 @@ adaptConnector = require("./ConnectorAdapter");
createY = function(connector) { createY = function(connector) {
var HB, ct, engine, model, ops, ops_manager, user_id; var HB, ct, engine, model, ops, ops_manager, user_id;
user_id = null;
if (connector.user_id != null) { if (connector.user_id != null) {
user_id = connector.user_id; user_id = connector.user_id;
} else { } else {
user_id = "_temp"; user_id = "_temp";
connector.on_user_id_set = function(id) { connector.when_received_state_vector_listeners = [
user_id = id; function(state_vector) {
return HB.resetUserId(id); return HB.setUserId(this.user_id, state_vector);
}; }
];
} }
HB = new HistoryBuffer(user_id); HB = new HistoryBuffer(user_id);
ops_manager = structured_ops_uninitialized(HB, this.constructor); ops_manager = structured_ops_uninitialized(HB, this.constructor);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

20152
build/test/selections-test.js Normal file

File diff suppressed because one or more lines are too long

18741
build/test/text-test.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -102,7 +102,7 @@ module.exports =
# #
# Execute a function _when_ we are connected. If not connected, wait until connected. # Execute a function _when_ we are connected. If not connected, wait until connected.
# @param f {Function} Will be executed on the PeerJs-Connector context. # @param f {Function} Will be executed on the Connector context.
# #
whenSynced: (args)-> whenSynced: (args)->
if args.constructor is Function if args.constructor is Function
@ -143,7 +143,7 @@ module.exports =
@send user, @send user,
sync_step: "getHB" sync_step: "getHB"
send_again: "true" send_again: "true"
data: [] # @getStateVector() data: @getStateVector()
if not @sent_hb_to_all_users if not @sent_hb_to_all_users
@sent_hb_to_all_users = true @sent_hb_to_all_users = true
@ -171,7 +171,7 @@ module.exports =
@send user, @send user,
sync_step: "getHB" sync_step: "getHB"
send_again: "true" send_again: "true"
data: [] data: @getStateVector()
hb = @getHB([]).hb hb = @getHB([]).hb
_hb = [] _hb = []
for o in hb for o in hb
@ -199,6 +199,12 @@ module.exports =
delete @compute_when_synced delete @compute_when_synced
null null
# executed when the a state_vector is received. listener will be called only once!
whenReceivedStateVector: (f)->
@when_received_state_vector_listeners ?= []
@when_received_state_vector_listeners.push f
# #
# You received a raw message, and you know that it is intended for to Yjs. Then call this function. # You received a raw message, and you know that it is intended for to Yjs. Then call this function.
# #
@ -210,6 +216,12 @@ module.exports =
if sender is @user_id if sender is @user_id
return return
if res.sync_step is "getHB" if res.sync_step is "getHB"
# call listeners
if @when_received_state_vector_listeners?
for f in @when_received_state_vector_listeners
f.call this, res.data
delete @when_received_state_vector_listeners
data = @getHB(res.data) data = @getHB(res.data)
hb = data.hb hb = data.hb
_hb = [] _hb = []

View File

@ -22,22 +22,32 @@ class HistoryBuffer
@reserved_identifier_counter = 0 @reserved_identifier_counter = 0
setTimeout @emptyGarbage, @garbageCollectTimeout setTimeout @emptyGarbage, @garbageCollectTimeout
resetUserId: (id)-> # At the beginning (when the user id was not assigned yet),
own = @buffer[@user_id] # the operations are added to buffer._temp. When you finally get your user id,
if own? # the operations are copies from buffer._temp to buffer[id]. Furthermore, when buffer[id] does already contain operations
for o_name,o of own # (because of a previous session), the uid.op_numbers of the operations have to be reassigned.
if o.uid.creator? # This is what this function does. It adds them to buffer[id],
o.uid.creator = id # and assigns them the correct uid.op_number and uid.creator
if o.uid.alt? setUserId: (@user_id, state_vector)->
o.uid.alt.creator = id @buffer[@user_id] ?= []
if @buffer[id]? buff = @buffer[@user_id]
throw new Error "You are re-assigning an old user id - this is not (yet) possible!"
@buffer[id] = own # we assumed that we started with counter = 0.
delete @buffer[@user_id] # when we receive tha state_vector, and actually have
if @operation_counter[@user_id]? # counter = 10. Then we have to add 10 to every op_counter
@operation_counter[id] = @operation_counter[@user_id] counter_diff = state_vector[@user_id] or 0
delete @operation_counter[@user_id]
@user_id = id if @buffer._temp?
for o_name,o of @buffer._temp
o.uid.creator = @user_id
o.uid.op_number += counter_diff
buff[o.uid.op_number] = o
@operation_counter[@user_id] = (@operation_counter._temp or 0) + counter_diff
delete @operation_counter._temp
delete @buffer._temp
emptyGarbage: ()=> emptyGarbage: ()=>
for o in @garbage for o in @garbage
@ -207,17 +217,11 @@ class HistoryBuffer
# #
addToCounter: (o)-> addToCounter: (o)->
@operation_counter[o.uid.creator] ?= 0 @operation_counter[o.uid.creator] ?= 0
if o.uid.creator isnt @getUserId() # TODO: check if operations are send in order
# TODO: check if operations are send in order if o.uid.op_number is @operation_counter[o.uid.creator]
if o.uid.op_number is @operation_counter[o.uid.creator] @operation_counter[o.uid.creator]++
@operation_counter[o.uid.creator]++ while @buffer[o.uid.creator][@operation_counter[o.uid.creator]]?
while @buffer[o.uid.creator][@operation_counter[o.uid.creator]]? @operation_counter[o.uid.creator]++
@operation_counter[o.uid.creator]++ undefined
undefined
#if @operation_counter[o.uid.creator] isnt (o.uid.op_number + 1)
#console.log (@operation_counter[o.uid.creator] - (o.uid.op_number + 1))
#console.log o
#throw new Error "You don't receive operations in the proper order. Try counting like this 0,1,2,3,4,.. ;)"
module.exports = HistoryBuffer module.exports = HistoryBuffer

View File

@ -6,14 +6,13 @@ Engine = require "./Engine"
adaptConnector = require "./ConnectorAdapter" adaptConnector = require "./ConnectorAdapter"
createY = (connector)-> createY = (connector)->
user_id = null
if connector.user_id? if connector.user_id?
user_id = connector.user_id # TODO: change to getUniqueId() user_id = connector.user_id # TODO: change to getUniqueId()
else else
user_id = "_temp" user_id = "_temp"
connector.on_user_id_set = (id)-> connector.when_received_state_vector_listeners = [(state_vector)->
user_id = id HB.setUserId this.user_id, state_vector
HB.resetUserId id ]
HB = new HistoryBuffer user_id HB = new HistoryBuffer user_id
ops_manager = structured_ops_uninitialized HB, this.constructor ops_manager = structured_ops_uninitialized HB, this.constructor
ops = ops_manager.operations ops = ops_manager.operations

4
y.js

File diff suppressed because one or more lines are too long