libnmdc: optional synchronous-based API instead of channel-based API

This commit is contained in:
mappu 2016-04-03 18:52:19 +12:00
parent e3a92da5f6
commit ba378a8245

View File

@ -79,7 +79,13 @@ type HubConnectionOptions struct {
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()
}