diff --git a/plugins/analysis/webinterface/httpserver/index.go b/plugins/analysis/webinterface/httpserver/index.go
index 288ead40aeb2c5c75972cfd5ad7e3234dfecb255..e1da2e2623a7eee28cb34a150049b96d4910b51e 100644
--- a/plugins/analysis/webinterface/httpserver/index.go
+++ b/plugins/analysis/webinterface/httpserver/index.go
@@ -64,6 +64,8 @@ func index(w http.ResponseWriter, r *http.Request) {
       links: []
     };
 
+    var existingLinks = {};
+
     const elem = document.getElementById("3d-graph");
 
     const Graph = ForceGraph3D()(elem)
@@ -125,14 +127,19 @@ func index(w http.ResponseWriter, r *http.Request) {
     }
 
     function connectNodes(sourceNodeId, targetNodeId) {
-      data.links = [...data.links, { source: sourceNodeId, target: targetNodeId }];
+      if(existingLinks[sourceNodeId + targetNodeId] == undefined && existingLinks[targetNodeId + sourceNodeId] == undefined) {
+        data.links = [...data.links, { source: sourceNodeId, target: targetNodeId }];
 
-      updateGraph();
+        updateGraph();
+      }
     }
 
     function disconnectNodes(sourceNodeId, targetNodeId) {
       data.links = data.links.filter(l => !(l.source.id == sourceNodeId && l.target.id == targetNodeId) && !(l.source.id == targetNodeId && l.target.id == sourceNodeId));
 
+      delete existingLinks[sourceNodeId + targetNodeId];
+      delete existingLinks[targetNodeId + sourceNodeId];
+
       updateGraph();
     }
 
diff --git a/plugins/autopeering/protocol/error_handler.go b/plugins/autopeering/protocol/error_handler.go
index 734a2f15961fb96f5a1eadfaa1e2264dafccca48..9319aa8d0e788cccf2da6689f686610628219f06 100644
--- a/plugins/autopeering/protocol/error_handler.go
+++ b/plugins/autopeering/protocol/error_handler.go
@@ -1,12 +1,13 @@
 package protocol
 
 import (
+    "github.com/iotaledger/goshimmer/packages/events"
     "github.com/iotaledger/goshimmer/packages/node"
     "net"
 )
 
-func createErrorHandler(plugin *node.Plugin) func(ip net.IP, err error) {
-    return func(ip net.IP, err error) {
+func createErrorHandler(plugin *node.Plugin) *events.Closure {
+    return events.NewClosure(func(ip net.IP, err error) {
         plugin.LogDebug("error when communicating with " + ip.String() + ": " + err.Error())
-    }
+    })
 }
diff --git a/plugins/autopeering/protocol/incoming_drop_processor.go b/plugins/autopeering/protocol/incoming_drop_processor.go
index 2c40c45eb149c6b3d5c05c6308fe9ffa67afddd3..732ce97b1f84769b88a45101d1c8a7de0ff3646b 100644
--- a/plugins/autopeering/protocol/incoming_drop_processor.go
+++ b/plugins/autopeering/protocol/incoming_drop_processor.go
@@ -1,17 +1,18 @@
 package protocol
 
 import (
+    "github.com/iotaledger/goshimmer/packages/events"
     "github.com/iotaledger/goshimmer/packages/node"
     "github.com/iotaledger/goshimmer/plugins/autopeering/instances/acceptedneighbors"
     "github.com/iotaledger/goshimmer/plugins/autopeering/instances/chosenneighbors"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/drop"
 )
 
-func createIncomingDropProcessor(plugin *node.Plugin) func(drop *drop.Drop) {
-    return func(drop *drop.Drop) {
+func createIncomingDropProcessor(plugin *node.Plugin) *events.Closure {
+    return events.NewClosure(func(drop *drop.Drop) {
         plugin.LogDebug("received drop message from " + drop.Issuer.String())
 
         chosenneighbors.INSTANCE.Remove(drop.Issuer.Identity.StringIdentifier)
         acceptedneighbors.INSTANCE.Remove(drop.Issuer.Identity.StringIdentifier)
-    }
+    })
 }
diff --git a/plugins/autopeering/protocol/incoming_ping_processor.go b/plugins/autopeering/protocol/incoming_ping_processor.go
index f9beb33e33fa10e3b6e7793dedc9f5e5c5c49cd8..d0fa1a1f6036a20ed0783793688e625037f89f2a 100644
--- a/plugins/autopeering/protocol/incoming_ping_processor.go
+++ b/plugins/autopeering/protocol/incoming_ping_processor.go
@@ -1,18 +1,19 @@
 package protocol
 
 import (
+    "github.com/iotaledger/goshimmer/packages/events"
     "github.com/iotaledger/goshimmer/packages/node"
     "github.com/iotaledger/goshimmer/plugins/autopeering/instances/knownpeers"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/ping"
 )
 
-func createIncomingPingProcessor(plugin *node.Plugin) func(ping *ping.Ping) {
-    return func(ping *ping.Ping) {
+func createIncomingPingProcessor(plugin *node.Plugin) *events.Closure {
+    return events.NewClosure(func(ping *ping.Ping) {
         plugin.LogDebug("received ping from " + ping.Issuer.String())
 
         knownpeers.INSTANCE.AddOrUpdate(ping.Issuer)
         for _, neighbor := range ping.Neighbors {
             knownpeers.INSTANCE.AddOrUpdate(neighbor)
         }
-    }
+    })
 }
diff --git a/plugins/autopeering/protocol/incoming_request_processor.go b/plugins/autopeering/protocol/incoming_request_processor.go
index 53e27cb5b59e6bb1dd35f33537d1bdca72fe131c..61fa17d6863793715e0a964274de9884d8eb0d5c 100644
--- a/plugins/autopeering/protocol/incoming_request_processor.go
+++ b/plugins/autopeering/protocol/incoming_request_processor.go
@@ -1,6 +1,7 @@
 package protocol
 
 import (
+    "github.com/iotaledger/goshimmer/packages/events"
     "github.com/iotaledger/goshimmer/packages/node"
     "github.com/iotaledger/goshimmer/plugins/autopeering/instances/acceptedneighbors"
     "github.com/iotaledger/goshimmer/plugins/autopeering/instances/knownpeers"
@@ -12,10 +13,10 @@ import (
     "math/rand"
 )
 
-func createIncomingRequestProcessor(plugin *node.Plugin) func(req *request.Request) {
-    return func(req *request.Request) {
+func createIncomingRequestProcessor(plugin *node.Plugin) *events.Closure {
+    return events.NewClosure(func(req *request.Request) {
         go processIncomingRequest(plugin, req)
-    }
+    })
 }
 
 func processIncomingRequest(plugin *node.Plugin, req *request.Request) {
diff --git a/plugins/autopeering/protocol/incoming_response_processor.go b/plugins/autopeering/protocol/incoming_response_processor.go
index dd732bcb41a639c2f6b0de857f6d3e0b58d4056f..ee581478b018945f4636cdec10304c2a3f538bd7 100644
--- a/plugins/autopeering/protocol/incoming_response_processor.go
+++ b/plugins/autopeering/protocol/incoming_response_processor.go
@@ -1,16 +1,17 @@
 package protocol
 
 import (
+    "github.com/iotaledger/goshimmer/packages/events"
     "github.com/iotaledger/goshimmer/packages/node"
     "github.com/iotaledger/goshimmer/plugins/autopeering/instances/chosenneighbors"
     "github.com/iotaledger/goshimmer/plugins/autopeering/instances/knownpeers"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/response"
 )
 
-func createIncomingResponseProcessor(plugin *node.Plugin) func(peeringResponse *response.Response) {
-    return func(peeringResponse *response.Response) {
+func createIncomingResponseProcessor(plugin *node.Plugin) *events.Closure {
+    return events.NewClosure(func(peeringResponse *response.Response) {
         go processIncomingResponse(plugin, peeringResponse)
-    }
+    })
 }
 
 func processIncomingResponse(plugin *node.Plugin, peeringResponse *response.Response) {
diff --git a/plugins/autopeering/server/tcp/events.go b/plugins/autopeering/server/tcp/events.go
index d1d4376f1b746b804ee2e2d899344e8e79dbddd9..47e063e1a7c037b61ed2f510ea504e05d615ae6c 100644
--- a/plugins/autopeering/server/tcp/events.go
+++ b/plugins/autopeering/server/tcp/events.go
@@ -1,95 +1,26 @@
 package tcp
 
 import (
+    "github.com/iotaledger/goshimmer/packages/events"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/ping"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/request"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/response"
     "net"
-    "reflect"
 )
 
-var Events = &pluginEvents{
-    ReceivePing:     &pingEvent{make(map[uintptr]PingConsumer)},
-    ReceiveRequest:  &requestEvent{make(map[uintptr]RequestConsumer)},
-    ReceiveResponse: &responseEvent{make(map[uintptr]ResponseConsumer)},
-    Error:           &ipErrorEvent{make(map[uintptr]IPErrorConsumer)},
-}
-
-type pluginEvents struct {
-    ReceivePing     *pingEvent
-    ReceiveRequest  *requestEvent
-    ReceiveResponse *responseEvent
-    Error           *ipErrorEvent
-}
-
-type pingEvent struct {
-    callbacks map[uintptr]PingConsumer
-}
-
-func (this *pingEvent) Attach(callback PingConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *pingEvent) Detach(callback PingConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *pingEvent) Trigger(ping *ping.Ping) {
-    for _, callback := range this.callbacks {
-        callback(ping)
-    }
-}
-
-type requestEvent struct {
-    callbacks map[uintptr]RequestConsumer
-}
-
-func (this *requestEvent) Attach(callback RequestConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *requestEvent) Detach(callback RequestConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *requestEvent) Trigger(req *request.Request) {
-    for _, callback := range this.callbacks {
-        callback(req)
-    }
-}
-
-type responseEvent struct {
-    callbacks map[uintptr]ResponseConsumer
-}
-
-func (this *responseEvent) Attach(callback ResponseConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *responseEvent) Detach(callback ResponseConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *responseEvent) Trigger(peeringResponse *response.Response) {
-    for _, callback := range this.callbacks {
-        callback(peeringResponse)
-    }
-}
-
-type ipErrorEvent struct {
-    callbacks map[uintptr]IPErrorConsumer
-}
-
-func (this *ipErrorEvent) Attach(callback IPErrorConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *ipErrorEvent) Detach(callback IPErrorConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *ipErrorEvent) Trigger(ip net.IP, err error) {
-    for _, callback := range this.callbacks {
-        callback(ip, err)
-    }
-}
+var Events = struct {
+    ReceivePing     *events.Event
+    ReceiveRequest  *events.Event
+    ReceiveResponse *events.Event
+    Error           *events.Event
+}{
+    events.NewEvent(pingCaller),
+    events.NewEvent(requestCaller),
+    events.NewEvent(responseCaller),
+    events.NewEvent(errorCaller),
+}
+
+func pingCaller(handler interface{}, params ...interface{}) { handler.(func(*ping.Ping))(params[0].(*ping.Ping)) }
+func requestCaller(handler interface{}, params ...interface{}) { handler.(func(*request.Request))(params[0].(*request.Request)) }
+func responseCaller(handler interface{}, params ...interface{}) { handler.(func(*response.Response))(params[0].(*response.Response)) }
+func errorCaller(handler interface{}, params ...interface{}) { handler.(func(net.IP, error))(params[0].(net.IP), params[1].(error)) }
diff --git a/plugins/autopeering/server/tcp/server.go b/plugins/autopeering/server/tcp/server.go
index 0c25eaddfd6ad1e6bb6d654032768349455a9af4..6f0e3d8412d4584864e947da38ab3e345cd96445 100644
--- a/plugins/autopeering/server/tcp/server.go
+++ b/plugins/autopeering/server/tcp/server.go
@@ -100,8 +100,8 @@ func ProcessIncomingPacket(connectionState *byte, receiveBuffer *[]byte, conn *n
     }
 }
 
-func parsePackageHeader(data []byte) (ConnectionState, []byte, error) {
-    var connectionState ConnectionState
+func parsePackageHeader(data []byte) (byte, []byte, error) {
+    var connectionState byte
     var receiveBuffer []byte
 
     switch data[0] {
diff --git a/plugins/autopeering/server/tcp/types.go b/plugins/autopeering/server/tcp/types.go
deleted file mode 100644
index 871d557230537829e3e3a1a5afb63b74bb9d05e7..0000000000000000000000000000000000000000
--- a/plugins/autopeering/server/tcp/types.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package tcp
-
-import (
-    "github.com/iotaledger/goshimmer/plugins/autopeering/types/ping"
-    "github.com/iotaledger/goshimmer/plugins/autopeering/types/request"
-    "github.com/iotaledger/goshimmer/plugins/autopeering/types/response"
-    "net"
-)
-
-type PingConsumer = func(p *ping.Ping)
-
-type RequestConsumer = func(req *request.Request)
-
-type ResponseConsumer = func(peeringResponse *response.Response)
-
-type IPErrorConsumer = func(ip net.IP, err error)
-
-type ConnectionState = byte
diff --git a/plugins/autopeering/server/udp/events.go b/plugins/autopeering/server/udp/events.go
index 449a0265ba7b2548c8766e6503b0f26997174969..d69667c4e1b4062af4e92de56d91979a63f03b46 100644
--- a/plugins/autopeering/server/udp/events.go
+++ b/plugins/autopeering/server/udp/events.go
@@ -1,116 +1,30 @@
 package udp
 
 import (
+    "github.com/iotaledger/goshimmer/packages/events"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/drop"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/ping"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/request"
     "github.com/iotaledger/goshimmer/plugins/autopeering/types/response"
     "net"
-    "reflect"
 )
 
-var Events = &pluginEvents{
-    ReceiveDrop:     &dropEvent{make(map[uintptr]DropConsumer)},
-    ReceivePing:     &pingEvent{make(map[uintptr]PingConsumer)},
-    ReceiveRequest:  &requestEvent{make(map[uintptr]ConnectionPeeringRequestConsumer)},
-    ReceiveResponse: &responseEvent{make(map[uintptr]ConnectionPeeringResponseConsumer)},
-    Error:           &ipErrorEvent{make(map[uintptr]IPErrorConsumer)},
-}
-
-type pluginEvents struct {
-    ReceiveDrop     *dropEvent
-    ReceivePing     *pingEvent
-    ReceiveRequest  *requestEvent
-    ReceiveResponse *responseEvent
-    Error           *ipErrorEvent
-}
-
-type dropEvent struct {
-    callbacks map[uintptr]DropConsumer
-}
-
-func (this *dropEvent) Attach(callback DropConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *dropEvent) Detach(callback DropConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *dropEvent) Trigger(drop *drop.Drop) {
-    for _, callback := range this.callbacks {
-        callback(drop)
-    }
-}
-
-type pingEvent struct {
-    callbacks map[uintptr]PingConsumer
-}
-
-func (this *pingEvent) Attach(callback PingConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *pingEvent) Detach(callback PingConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *pingEvent) Trigger(ping *ping.Ping) {
-    for _, callback := range this.callbacks {
-        callback(ping)
-    }
-}
-
-type requestEvent struct {
-    callbacks map[uintptr]ConnectionPeeringRequestConsumer
-}
-
-func (this *requestEvent) Attach(callback ConnectionPeeringRequestConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *requestEvent) Detach(callback ConnectionPeeringRequestConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *requestEvent) Trigger(request *request.Request) {
-    for _, callback := range this.callbacks {
-        callback(request)
-    }
-}
-
-type responseEvent struct {
-    callbacks map[uintptr]ConnectionPeeringResponseConsumer
-}
-
-func (this *responseEvent) Attach(callback ConnectionPeeringResponseConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *responseEvent) Detach(callback ConnectionPeeringResponseConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *responseEvent) Trigger(peeringResponse *response.Response) {
-    for _, callback := range this.callbacks {
-        callback(peeringResponse)
-    }
-}
-
-type ipErrorEvent struct {
-    callbacks map[uintptr]IPErrorConsumer
-}
-
-func (this *ipErrorEvent) Attach(callback IPErrorConsumer) {
-    this.callbacks[reflect.ValueOf(callback).Pointer()] = callback
-}
-
-func (this *ipErrorEvent) Detach(callback IPErrorConsumer) {
-    delete(this.callbacks, reflect.ValueOf(callback).Pointer())
-}
-
-func (this *ipErrorEvent) Trigger(ip net.IP, err error) {
-    for _, callback := range this.callbacks {
-        callback(ip, err)
-    }
-}
+var Events = struct {
+    ReceiveDrop     *events.Event
+    ReceivePing     *events.Event
+    ReceiveRequest  *events.Event
+    ReceiveResponse *events.Event
+    Error           *events.Event
+}{
+    events.NewEvent(dropCaller),
+    events.NewEvent(pingCaller),
+    events.NewEvent(requestCaller),
+    events.NewEvent(responseCaller),
+    events.NewEvent(errorCaller),
+}
+
+func dropCaller(handler interface{}, params ...interface{}) { handler.(func(*drop.Drop))(params[0].(*drop.Drop)) }
+func pingCaller(handler interface{}, params ...interface{}) { handler.(func(*ping.Ping))(params[0].(*ping.Ping)) }
+func requestCaller(handler interface{}, params ...interface{}) { handler.(func(*request.Request))(params[0].(*request.Request)) }
+func responseCaller(handler interface{}, params ...interface{}) { handler.(func(*response.Response))(params[0].(*response.Response)) }
+func errorCaller(handler interface{}, params ...interface{}) { handler.(func(net.IP, error))(params[0].(net.IP), params[1].(error)) }
diff --git a/plugins/autopeering/server/udp/server.go b/plugins/autopeering/server/udp/server.go
index a6ed7c51fe6859cdd7fed9fe4e35b26ac48f41e8..72ed9cdb1b408bc360492e21f154035eaf45a844 100644
--- a/plugins/autopeering/server/udp/server.go
+++ b/plugins/autopeering/server/udp/server.go
@@ -19,9 +19,9 @@ import (
 var udpServer = udp.NewServer(int(math.Max(float64(request.MARSHALLED_TOTAL_SIZE), float64(response.MARSHALLED_TOTAL_SIZE))))
 
 func ConfigureServer(plugin *node.Plugin) {
-    Events.Error.Attach(func(ip net.IP, err error) {
+    Events.Error.Attach(events.NewClosure(func(ip net.IP, err error) {
         plugin.LogFailure(err.Error())
-    })
+    }))
 
     udpServer.Events.ReceiveData.Attach(events.NewClosure(processReceivedData))
     udpServer.Events.Error.Attach(events.NewClosure(func(err error) {
diff --git a/plugins/autopeering/server/udp/types.go b/plugins/autopeering/server/udp/types.go
deleted file mode 100644
index 2c18f4f81c864fbfcc0a4f8ab9377f51fd015786..0000000000000000000000000000000000000000
--- a/plugins/autopeering/server/udp/types.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package udp
-
-import (
-    "github.com/iotaledger/goshimmer/plugins/autopeering/types/drop"
-    "github.com/iotaledger/goshimmer/plugins/autopeering/types/ping"
-    "github.com/iotaledger/goshimmer/plugins/autopeering/types/request"
-    "github.com/iotaledger/goshimmer/plugins/autopeering/types/response"
-    "net"
-)
-
-type DropConsumer = func(d *drop.Drop)
-
-type PingConsumer = func(p *ping.Ping)
-
-type ConnectionPeeringRequestConsumer = func(request *request.Request)
-
-type ConnectionPeeringResponseConsumer = func(peeringResponse *response.Response)
-
-type IPErrorConsumer = func(ip net.IP, err error)
\ No newline at end of file