// BitTorrent Visualization // created by http://www.aphid.org/btsim/ // // code is in Processing Language // http://processing.org/ // // ported to current version of Processing // by Jeff Atwood // http://www.codinghorror.com/ float angle = 0; ArrayList peers = new ArrayList(); ArrayList tmp = new ArrayList(); ArrayList connections = new ArrayList(); float rot = -1; Torrent testTorrent = new Torrent(30); int initialSeeders = 2; int initialPeers = 8; void setup() { size(450,450); fill(255); fill(0); textAlign(CENTER); // establish initial seeds/peers for (int i = 0; i < initialSeeders; i++) { addSeeder(); } for (int i = 0; i < initialPeers; i++) { addPeer(); } } void addPeer() { int cc = peers.size(); Peer k = new Peer(random(0,1)); peers.add(k); } void addSeeder() { int cc = peers.size(); Peer p = new Peer(random(0,1)); peers.add(p); for (int i = 0; i < testTorrent.bits.size(); i++) { p.myBits.add(testTorrent.bits.get(i)); } p.needBits = new ArrayList(); } void removePeer() { int i = int(random(0,peers.size() - 1)); ((Peer)peers.get(i)).removing = 1; } void keyPressed() { if (key == '+') { addPeer(); } if (key == 's') { addSeeder(); } if (key == '-') { removePeer(); } } void draw() { background(0); // rotate peers and seeds (disabled by default) if (rot >= 0) { if (rot < 360) { rot += .2; } else { rot = rot - 360; } } for (int i = 0; i < connections.size(); i++) { Connection c = (Connection)connections.get(i); c.manageKibbles(); if ( c.kibbles.isEmpty() && c.stream == false ) { if (c.to.removing >= 1) { c.to.removing++; } if (c.from.removing >= 1) { c.from.removing++; } c.from.knex.remove(c.from.knex.indexOf(c.to)); c.to.myBits.add(c.theBit); if (c.to.needBits.indexOf(c.theBit) != -1) { c.to.needBits.remove(c.to.needBits.indexOf(c.theBit)); } if (c.to.knex.indexOf(c.theBit) != -1) { c.to.knex.remove(c.to.knex.indexOf(c.theBit)); } connections.remove(i); } } for (int i = 0; i < peers.size(); i++) { Peer p = (Peer)peers.get(i); p.moveSelf(); p.drawSelf(); p.reConfigure(i); if (p.removing > 1) { peers.remove(i); } } ArrayList tmp = shuffle(peers); for (int i = 0; i < tmp.size(); i++) { Peer cpeer = (Peer)tmp.get(i); if (cpeer.lastcheck < millis() - cpeer.pwait) { cpeer.findPeer(); cpeer.lastcheck = millis(); } } } class Peer { int index; float pwait = random(1,9)*1000; float sxpos; // x of peer float sypos; // y of peer float expos; // x of where peer should be float eypos; // y of where peer should be float cxpos; float cypos; float smovetime; float emovetime; float percent; ArrayList knex = new ArrayList(); ArrayList actBits = new ArrayList(); float chue; float ehue; float shue; color ccolor; int removing; int lastcheck; ArrayList myBits = new ArrayList(); ArrayList needBits = new ArrayList(); Peer(float pct) { int szv = peers.size(); if (szv == 0) { szv++; } pushMatrix(); translate(width / 2, height / 2); ellipseMode(CENTER); float angle = 3 ; rotate(radians(angle)); expos = screenX(0, 230); eypos = screenY(0, 230); sxpos = width / 2; sypos = height / 2; smovetime = millis(); emovetime = smovetime + 1250; percent = pct; colorMode(HSB); lastcheck = millis(); chue = 5; popMatrix(); setupBits(); } void setupBits() { for (int i = 0; i < testTorrent.bits.size(); i++) { if (!myBits.contains(testTorrent.bits.get(i))) { needBits.add(testTorrent.bits.get(i)); } } } void findPeer() { for (int i = 0; i < needBits.size(); i++) { needBits = shuffle(needBits); Bit b = (Bit)needBits.get(i); for (int o = 0; o < peers.size(); o++) { Peer p = (Peer)peers.get(o); if (p.myBits.contains(b) && !(p.removing > 0) && !(removing > 0) && !p.knex.contains((Peer)peers.get(index)) && p.index != index && !actBits.contains(b)) { bitRequest(p,b); } } } } void bitRequest(Peer k, Bit j) { if (k.knex.size() < 4) { Connection mz = new Connection(k, (Peer)peers.get(index), j); k.knex.add(peers.get(index)); actBits.add(j); connections.add(mz); } } void reConfigure(int i) { int k; pushMatrix(); translate(width / 2, height / 2); ellipseMode(CENTER); if (peers.size() == 0) { k = 1; } else { k = peers.size(); } index = i; float angle = ((360 / k) * i) + rot; rotate(radians(angle)); sxpos = cxpos; sypos = cypos; expos = screenX(0, 180); eypos = screenY(0, 180); smovetime= millis(); emovetime= smovetime + 3000; popMatrix(); shue = chue; ehue = (255 / peers.size() - 1) * i; ccolor = color(chue, 255, 255, 133); } void moveSelf() { if (millis() > emovetime) { cxpos = expos; cypos = eypos; chue = ehue; } else { float diff = (millis() - smovetime) / (emovetime - smovetime); cxpos = sxpos * (1 - diff) + expos * diff; cypos = sypos * (1 - diff) + eypos * diff; chue = shue * (1 - diff) + ehue * diff; } } void drawSelf() { fill(ccolor); stroke(myBits.size()); strokeWeight(1); ellipseMode(CENTER); ellipse(cxpos, cypos, 50, 50); fill(0); float w = testTorrent.bits.size() - 1; rect(cxpos - w/2, cypos - 5, w, 10); for(int i = 0; i < myBits.size(); i++) { Bit k = (Bit)myBits.get(i); colorMode(HSB); stroke(k.bitHue, 255, 255); line(cxpos - w/2 + (1 * k.id), cypos - 5, cxpos - w / 2 + (1 * k.id), cypos + 5); } } } class Kibble { float starttime; float endtime; float big; Kibble() { starttime = 0; endtime = 0; big = (random(0,4)); } } class Connection { int lastdraw; Peer from; Peer to; boolean stream; ArrayList kibbles = new ArrayList(); int deadkibbles; int speed; Bit theBit; Connection(Peer f, Peer t, Bit b) { theBit = (Bit)b; from = f; to = t; stream = true; lastdraw = millis(); deadkibbles = 0; speed = int(random(30,500)); } int getIdxTo() { return to.index; } int getIdxFrom() { return from.index; } void manageKibbles() { if ( from.removing >= 1 || to.removing >= 1 || deadkibbles > 125 ) { stream = false; } else { if (lastdraw < millis() - speed) { newKibble(); } } drawKibbles(); } void newKibble() { Kibble k = new Kibble(); k.starttime = millis(); k.endtime = k.starttime + 5000; kibbles.add(k); lastdraw = millis(); } void drawKibbles() { for (int i = 0; i < kibbles.size(); i++) { Kibble k = (Kibble)kibbles.get(i); if(millis() > k.endtime) { kibbles.remove(i); deadkibbles++; } else { float diff = (millis() - k.starttime) / (k.endtime - k.starttime); float xpos = from.cxpos * (1 - diff) + (to.cxpos) * diff; float ypos = from.cypos * (1 - diff) + (to.cypos) * diff; colorMode(HSB); fill(theBit.bitHue, 255, 255); stroke(theBit.bitHue, 255, 255); strokeWeight(k.big); ellipse(xpos, ypos, k.big, k.big); } } } } class Bit { int id; int bitHue; Bit(int i, int hu) { id = i; bitHue = hu; } } class Torrent { ArrayList bits = new ArrayList(); Torrent(int totbits) { for (int i=0; i < totbits; i ++) { int ll = (255 / totbits) * i; Bit k = new Bit(i, ll); bits.add(k); } } } ArrayList shuffle(ArrayList input) { ArrayList yin = new ArrayList(); for (int i = 0; i < input.size(); i++) { yin.add(input.get(i)); } ArrayList temp = new ArrayList(); while (yin.size() > 0) { int x = int(random(0, yin.size())); temp.add(yin.get(x)); yin.remove(x); } return(temp); }