diff --git a/src/libnmdc/libnmdc.go b/src/libnmdc/libnmdc.go index 999541d..bee3a6b 100644 --- a/src/libnmdc/libnmdc.go +++ b/src/libnmdc/libnmdc.go @@ -75,11 +75,17 @@ func init() { } type HubConnectionOptions struct { - Address HubAddress - SkipVerifyTLS bool // using a negative verb, because bools default to false - Self UserInfo - NickPassword string + Address HubAddress + SkipVerifyTLS bool // using a negative verb, because bools default to false + Self UserInfo + NickPassword string + + // Returning messages in async mode NumEventsToBuffer uint + OnEvent chan HubEvent + + // Returning messages in sync mode + OnEventSync func(HubEvent) } type HubConnection struct { @@ -92,7 +98,7 @@ type HubConnection struct { Users map[string]UserInfo // Streamed events - OnEvent chan HubEvent + processEvent func(HubEvent) // Private state conn net.Conn // this is an interface @@ -148,7 +154,7 @@ func (this *HubConnection) userJoined_NameOnly(nick string) { _, already_existed := this.Users[nick] if !already_existed { this.Users[nick] = *NewUserInfo(nick) - this.OnEvent <- HubEvent{EventType: EVENT_USER_JOINED, Nick: nick} + this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: nick}) } } @@ -156,7 +162,7 @@ func (this *HubConnection) userJoined_Full(uinf *UserInfo) { _, already_existed := this.Users[uinf.Nick] if !already_existed { this.Users[uinf.Nick] = *uinf - this.OnEvent <- HubEvent{EventType: EVENT_USER_JOINED, Nick: uinf.Nick} + this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: uinf.Nick}) } } @@ -205,14 +211,14 @@ func (this *HubConnection) processProtocolMessage(message string) { // ``````````` if rx_publicChat.MatchString(message) { pubchat_parts := rx_publicChat.FindStringSubmatch(message) - this.OnEvent <- HubEvent{EventType: EVENT_PUBLIC, Nick: pubchat_parts[1], Message: NMDCUnescape(pubchat_parts[2])} + this.processEvent(HubEvent{EventType: EVENT_PUBLIC, Nick: pubchat_parts[1], Message: NMDCUnescape(pubchat_parts[2])}) return } // System messages // ``````````````` if message[0] != '$' { - this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Nick: this.HubName, Message: NMDCUnescape(message)} + this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Nick: this.HubName, Message: NMDCUnescape(message)}) return } @@ -242,27 +248,27 @@ func (this *HubConnection) processProtocolMessage(message string) { case "$HubName": this.HubName = commandParts[1] - this.OnEvent <- HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: commandParts[1]} + this.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: commandParts[1]}) case "$ValidateDenide": // sic if len(this.Hco.NickPassword) > 0 { - this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."} + this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."}) } else { - this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Nick already in use."} + this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Nick already in use."}) } case "$HubIsFull": - this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Hub is full."} + this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Hub is full."}) case "$BadPass": - this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."} + this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."}) case "$GetPass": this.SayRaw("$MyPass " + NMDCEscape(this.Hco.NickPassword) + "|") case "$Quit": delete(this.Users, commandParts[1]) - this.OnEvent <- HubEvent{EventType: EVENT_USER_PART, Nick: commandParts[1]} + this.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: commandParts[1]}) case "$MyINFO": u := UserInfo{} @@ -270,7 +276,7 @@ func (this *HubConnection) processProtocolMessage(message string) { if err == nil { this.userJoined_Full(&u) } else { - this.OnEvent <- HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: err.Error()} + this.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: err.Error()}) } case "$NickList": @@ -286,20 +292,20 @@ func (this *HubConnection) processProtocolMessage(message string) { if rx_incomingTo.MatchString(commandParts[1]) { txparts := rx_incomingTo.FindStringSubmatch(commandParts[1]) if txparts[1] == this.Hco.Self.Nick && txparts[2] == txparts[3] { - this.OnEvent <- HubEvent{EventType: EVENT_PRIVATE, Nick: txparts[2], Message: txparts[4]} + this.processEvent(HubEvent{EventType: EVENT_PRIVATE, Nick: txparts[2], Message: txparts[4]}) valid = true } } if !valid { - this.OnEvent <- HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed private message '" + commandParts[1] + "'"} + this.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed private message '" + commandParts[1] + "'"}) } case "$UserIP": // Final message in PtokaX connection handshake - trigger connection callback. // This might not be the case for other hubsofts, though if this.State != CONNECTIONSTATE_CONNECTED { - this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED} + this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED}) this.State = CONNECTIONSTATE_CONNECTED } @@ -318,7 +324,7 @@ func (this *HubConnection) processProtocolMessage(message string) { case "$ConnectToMe": default: - this.OnEvent <- HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Unhandled protocol command '" + commandParts[0] + "'"} + this.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Unhandled protocol command '" + commandParts[0] + "'"}) } } @@ -350,7 +356,7 @@ func (this *HubConnection) worker() { } else { this.State = CONNECTIONSTATE_CONNECTING this.connValid = true - this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING} + this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING}) } } @@ -369,7 +375,7 @@ func (this *HubConnection) worker() { this.State = CONNECTIONSTATE_DISCONNECTED this.conn = nil this.connValid = false - this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_DISCONNECTED, Message: err.Error()} + this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_DISCONNECTED, Message: err.Error()}) time.Sleep(30 * time.Second) // Wait before reconnect continue @@ -393,26 +399,45 @@ func (this *HubConnection) worker() { } -// Connects to an NMDC server, and spawns a background goroutine to handle -// protocol messages. Client code should select on all the interface channels. -func (this *HubConnectionOptions) Connect() *HubConnection { - +func (this *HubConnectionOptions) prepareConnection() *HubConnection { if this.Self.ClientTag == "" { this.Self.ClientTag = "libnmdc.go" } - if this.NumEventsToBuffer < 1 { - this.NumEventsToBuffer = 1 - } hc := HubConnection{ Hco: this, HubName: "(unknown)", State: CONNECTIONSTATE_DISCONNECTED, Users: make(map[string]UserInfo), - OnEvent: make(chan HubEvent, this.NumEventsToBuffer), } - go hc.worker() - return &hc } + +// Connects to an NMDC server, and spawns a background goroutine to handle +// protocol messages. Client code should select on all the interface channels. +func (this *HubConnectionOptions) Connect() *HubConnection { + + if this.NumEventsToBuffer < 1 { + this.NumEventsToBuffer = 1 + } + if this.OnEvent == nil { + this.OnEvent = make(chan HubEvent, this.NumEventsToBuffer) + } + + hc := this.prepareConnection() + + hc.processEvent = func(ev HubEvent) { + this.OnEvent <- ev + } + + go hc.worker() + return hc +} + +// Connects to an NMDC server, and blocks forever to handle protocol messages. +// Client code should supply an event handling function. +func (this *HubConnectionOptions) ConnectSync() { + hc := this.prepareConnection() + hc.worker() +}