2014-08-27 01:55:40 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
function chatEvent(nym, status, utterance, stamp) {
|
|
|
|
return ["chatEvent", nym, status, utterance, stamp || +(new Date())];
|
|
|
|
}
|
|
|
|
|
2014-10-11 02:28:35 +00:00
|
|
|
function halfConnection(fromNym, toNym, sdp, ice) {
|
|
|
|
return ["route", fromNym, toNym, sdp, ice];
|
2014-08-27 01:55:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function remoteValue(x, metaLevel) {
|
|
|
|
return ["broker", metaLevel || 0, x];
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-08-26 01:42:35 +00:00
|
|
|
var G;
|
|
|
|
$(document).ready(function () {
|
|
|
|
var World = Minimart.World;
|
|
|
|
var Actor = Minimart.Actor;
|
|
|
|
var sub = Minimart.sub;
|
|
|
|
var pub = Minimart.pub;
|
|
|
|
var __ = Minimart.__;
|
|
|
|
var _$ = Minimart._$;
|
|
|
|
|
|
|
|
var rtcConfig = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]};
|
|
|
|
|
|
|
|
G = new Minimart.Ground(function () {
|
|
|
|
var wsconn = new Minimart.WebSocket.WebSocketConnection("broker", "ws://server.minimart.leastfixedpoint.com:8000/", true);
|
|
|
|
World.spawn(wsconn);
|
|
|
|
|
2014-08-27 01:55:40 +00:00
|
|
|
var localNym = "user-" + Math.floor(Math.random() * 1048576);
|
|
|
|
console.log("localNym", localNym);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Advertise our presence.
|
|
|
|
//
|
2014-08-26 01:42:35 +00:00
|
|
|
|
2014-08-27 01:55:40 +00:00
|
|
|
World.spawn(new Actor(function () {
|
2014-08-26 01:42:35 +00:00
|
|
|
Actor.advertise(
|
2014-08-27 01:55:40 +00:00
|
|
|
function () { return remoteValue(chatEvent(localNym, "", __, __)) });
|
2014-08-26 01:42:35 +00:00
|
|
|
}));
|
2014-08-27 01:55:40 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// DemandMatcher waits for people to appear, and when they do, offers an SDP to them.
|
|
|
|
//
|
|
|
|
|
|
|
|
var dm = new Minimart.DemandMatcher(remoteValue(chatEvent(_$, __, __, __)), 0, {
|
2014-10-11 02:28:35 +00:00
|
|
|
supplyProjection: remoteValue(halfConnection(__, _$, __, __))
|
2014-08-27 01:55:40 +00:00
|
|
|
});
|
|
|
|
dm.onDemandIncrease = function (captures) {
|
|
|
|
spawnRoute(captures[0]);
|
|
|
|
};
|
|
|
|
World.spawn(dm);
|
|
|
|
|
|
|
|
//
|
|
|
|
// spawnRoute spawns an actor responsible for managing an
|
|
|
|
// RTCPeerConnection to a specific remote peer.
|
|
|
|
//
|
|
|
|
|
|
|
|
function spawnRoute(remoteNym) {
|
|
|
|
var RTCPeerConnection = (window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection);
|
|
|
|
var RTCSessionDescription = (window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription);
|
2014-10-11 02:28:35 +00:00
|
|
|
var RTCIceCandidate = (window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate);
|
|
|
|
|
|
|
|
console.log("spawnRoute", remoteNym);
|
2014-08-27 01:55:40 +00:00
|
|
|
|
|
|
|
World.spawn(new Actor(function () {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
self.peerSeen = false;
|
|
|
|
|
|
|
|
if (localNym !== remoteNym) {
|
|
|
|
self.pc = new RTCPeerConnection(rtcConfig);
|
|
|
|
self.ch = self.pc.createDataChannel('x'); // TODO -- label
|
2014-10-11 02:28:35 +00:00
|
|
|
self.localSdp = null;
|
|
|
|
self.localIce = [];
|
|
|
|
self.remoteSdp = null;
|
|
|
|
self.remoteIce = [];
|
|
|
|
|
|
|
|
// WebRTC likes to pretend UDP is like a phone call.
|
|
|
|
self.isOriginator = localNym < remoteNym;
|
2014-08-27 01:55:40 +00:00
|
|
|
|
|
|
|
var errback = World.wrap(function (err) { // TODO - make a standard World utility?
|
|
|
|
throw err;
|
|
|
|
});
|
|
|
|
|
2014-10-11 02:28:35 +00:00
|
|
|
function flushLocalIce() {
|
|
|
|
if (!self.remoteSdp) { return; }
|
|
|
|
while (self.localIce.length) {
|
|
|
|
World.send(remoteValue(halfConnection(localNym,
|
|
|
|
remoteNym,
|
|
|
|
self.localSdp,
|
|
|
|
self.localIce.shift())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.pc.onicecandidate = World.wrap(function (e) {
|
|
|
|
if (e && e.candidate) {
|
|
|
|
var ice = [e.candidate.candidate, e.candidate.sdpMLineIndex, e.candidate.sdpMid];
|
|
|
|
console.log("LOCAL CANDIDATE", JSON.stringify(ice));
|
|
|
|
self.localIce.push(ice);
|
|
|
|
flushLocalIce();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (self.isOriginator) {
|
2014-08-27 01:55:40 +00:00
|
|
|
self.pc.createOffer(function (offer) {
|
2014-10-11 02:28:35 +00:00
|
|
|
self.pc.setLocalDescription(new RTCSessionDescription(offer), function () {
|
|
|
|
self.localSdp = self.pc.localDescription.sdp;
|
|
|
|
self.updateRoutes();
|
|
|
|
}, errback);
|
2014-08-27 01:55:40 +00:00
|
|
|
}, errback);
|
2014-10-11 02:28:35 +00:00
|
|
|
}
|
2014-08-27 01:55:40 +00:00
|
|
|
|
|
|
|
Actor.advertise(
|
2014-10-11 02:28:35 +00:00
|
|
|
function () { return remoteValue(halfConnection(localNym,
|
|
|
|
remoteNym,
|
|
|
|
self.localSdp,
|
|
|
|
__)) },
|
|
|
|
{ when: function () { return self.local.sdp } });
|
|
|
|
|
|
|
|
Actor.subscribe(
|
|
|
|
function () { return remoteValue(halfConnection(remoteNym,
|
|
|
|
localNym,
|
|
|
|
__,
|
|
|
|
_$("ice"))) },
|
|
|
|
function (ice) {
|
|
|
|
console.log("REMOTE CANDIDATE", JSON.stringify(ice));
|
|
|
|
var candidate = new RTCIceCandidate({ candidate: ice[0],
|
|
|
|
sdpMLineIndex: ice[1],
|
|
|
|
sdpMid: ice[2] });
|
|
|
|
self.pc.addIceCandidate(candidate);
|
|
|
|
});
|
2014-08-27 01:55:40 +00:00
|
|
|
|
|
|
|
Actor.observeAdvertisers(
|
2014-10-11 02:28:35 +00:00
|
|
|
function () { return remoteValue(halfConnection(remoteNym,
|
|
|
|
localNym,
|
|
|
|
_$("sdp"),
|
|
|
|
__)) },
|
|
|
|
{ singleton: "remote" },
|
2014-08-27 01:55:40 +00:00
|
|
|
function () {
|
2014-10-11 02:28:35 +00:00
|
|
|
if (self.local.sdp && self.remote) {
|
|
|
|
var remoteSessionDescription = new RTCSessionDescription({
|
2014-08-27 01:55:40 +00:00
|
|
|
type: "offer",
|
2014-10-11 02:28:35 +00:00
|
|
|
sdp: self.remote.sdp
|
|
|
|
});
|
|
|
|
// console.log("Local SDP is", self.localSdp);
|
|
|
|
// console.log("Remote SDP is", params);
|
2014-08-27 01:55:40 +00:00
|
|
|
self.pc.setRemoteDescription(new RTCSessionDescription(params), function () {}, errback);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Actor.observeAdvertisers(
|
|
|
|
function () { return remoteValue(chatEvent(remoteNym, __, __, __)) },
|
|
|
|
{ presence: "peerPresent" },
|
|
|
|
function () {
|
|
|
|
self.peerSeen = self.peerSeen || self.peerPresent;
|
|
|
|
if (self.peerSeen && !self.peerPresent) {
|
|
|
|
console.log("Peer went missing", remoteNym);
|
|
|
|
World.exit();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// World.spawn(new Actor(function () {
|
|
|
|
// var self = this;
|
|
|
|
// self.pc = new (window.mozRTCPeerConnection || window.webkitRTCPeerConnection)(rtcConfig);
|
|
|
|
// x = self.pc;
|
|
|
|
// self.ch = self.pc.createDataChannel('x'); // TODO -- label
|
|
|
|
|
|
|
|
// self.pc.onicecandidate = World.wrap(function (e) {
|
|
|
|
// self.offerSdp = self.pc.localDescription.sdp;
|
|
|
|
// console.log("onicecandidate", self.offerSdp);
|
|
|
|
// self.updateRoutes();
|
|
|
|
// });
|
|
|
|
|
|
|
|
// Actor.observeAdvertisers(
|
|
|
|
// function () { return ["broker_state", _$("state")] },
|
|
|
|
// { name: "broker_states",
|
|
|
|
// set: function (o) { return o.state; } },
|
|
|
|
// function () {
|
|
|
|
// if (self.broker_states[0] === "connected" && !self.pc.localDescription) {
|
|
|
|
// var errback = World.wrap(function (err) {
|
|
|
|
// throw new Error(err);
|
|
|
|
// });
|
|
|
|
// self.pc.createOffer(function (offer) {
|
|
|
|
// self.pc.setLocalDescription(offer, function () {}, errback);
|
|
|
|
// }, errback);
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
|
|
|
|
// Actor.advertise(
|
|
|
|
// function () { return ["broker", 0, ["offerSdp", self.offerSdp]] },
|
|
|
|
// { when: function () { return self.offerSdp } });
|
|
|
|
|
|
|
|
// Actor.observeAdvertisers(
|
|
|
|
// function () { return ["broker", 0, ["offerSdp", _$("offerSdp")]] },
|
|
|
|
// { name: "offers",
|
|
|
|
// set: function (o) { return o.offerSdp; } },
|
|
|
|
// function () {
|
|
|
|
// var elt = document.getElementById('output');
|
|
|
|
// elt.innerHTML = '';
|
|
|
|
// for (var i = 0; i < self.offers.length; i++) {
|
|
|
|
// // console.log(self.offers[i]);
|
|
|
|
// elt.appendChild(document.createTextNode('\n' + self.offers[i]));
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
// }));
|
2014-08-26 01:42:35 +00:00
|
|
|
});
|
|
|
|
G.startStepping();
|
|
|
|
});
|