No Description

client.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package funmow
  2. import (
  3. "bufio"
  4. "fmt"
  5. "github.com/mgutz/ansi"
  6. "log"
  7. "net"
  8. "strings"
  9. "unicode"
  10. )
  11. type Client struct {
  12. conn net.Conn
  13. connectionID int
  14. connected bool
  15. playerID DBRef
  16. authenticated bool
  17. inbound chan PlayerEvent
  18. outbound chan PlayerEvent
  19. eventDistributor *EventDistributor
  20. quit chan bool
  21. db *DB
  22. factory *ObjectFactory
  23. }
  24. func NewClient(conn net.Conn, connectionID int, e *EventDistributor, w *DB) *Client {
  25. c := new(Client)
  26. c.conn = conn
  27. c.connectionID = connectionID
  28. c.connected = true
  29. c.authenticated = false
  30. c.inbound = make(chan PlayerEvent)
  31. c.eventDistributor = e
  32. c.db = w
  33. c.factory = NewObjectFactory(w)
  34. return c
  35. }
  36. func (c *Client) Run() {
  37. log.Print("Received connection from ", c.conn.RemoteAddr())
  38. commandChan := make(chan string)
  39. dieChan := make(chan bool)
  40. go func() {
  41. reader := bufio.NewReader(c.conn)
  42. for c.connected {
  43. message, err := reader.ReadString('\n')
  44. if err != nil {
  45. dieChan <- true
  46. break
  47. }
  48. message = strings.TrimSpace(message)
  49. if len(message) > 0 {
  50. commandChan <- message
  51. }
  52. }
  53. }()
  54. c.splash()
  55. for c.connected {
  56. select {
  57. case m := <-c.inbound:
  58. c.handleInboundEvent(m)
  59. case cmd := <-commandChan:
  60. if c.authenticated {
  61. c.sendCommand(cmd)
  62. } else {
  63. c.handleLoginPhase(cmd)
  64. }
  65. case <-dieChan:
  66. c.connected = false
  67. }
  68. }
  69. c.conn.Close()
  70. c.outbound <- PlayerEvent{src: c.playerID, dst: c.playerID, messageType: EventTypeTeardown, connectionID: c.connectionID}
  71. log.Print("Lost connection from ", c.conn.RemoteAddr())
  72. }
  73. func (c *Client) splash() {
  74. c.write(ansi.Color("Welcome to...", "red+b") + "\n")
  75. c.write(" ___ __ __ ___ __ __\n")
  76. c.write(" | __| _ _ _ _ | \\/ | / _ \\ \\ \\ / /\n")
  77. c.write(" | _| | || | | ' \\ | |\\/| | | (_) | \\ \\/\\/ /\n")
  78. c.write(" |_| \\_,_| |_||_| |_| |_| \\___/ \\_/\\_/\n\n")
  79. c.write("Commands:\n")
  80. c.write("To create a player: " + ansi.Color("create <username> <password>", "blue+b") + "\n")
  81. c.write("To connect: " + ansi.Color("connect <username> <password>", "blue+b") + "\n")
  82. }
  83. func (c *Client) handleLoginPhase(message string) {
  84. fields := strings.FieldsFunc(message, func(c rune) bool {
  85. return unicode.IsSpace(c)
  86. })
  87. switch fields[0] {
  88. case "help":
  89. c.write("Commands:\n\tcon[nect] <username>\n\tquit\n")
  90. case "con":
  91. fallthrough
  92. case "connect":
  93. if len(fields) == 3 {
  94. player, found := c.db.GetPlayer(fields[1])
  95. if !found || (found && player.Password != fields[2]) {
  96. c.write("Bad username or password.\n")
  97. break
  98. }
  99. c.playerID = player.ID
  100. c.authenticated = true
  101. // when a player authenticates, the ipc channels get turned on
  102. subReq := EventSubscribeRequest{
  103. connectionID: c.connectionID,
  104. playerID: c.playerID,
  105. inbound: c.inbound,
  106. outbound: make(chan chan PlayerEvent),
  107. }
  108. c.outbound = c.eventDistributor.Subscribe(subReq)
  109. c.sendCommand("look")
  110. } else {
  111. c.write("What?\n")
  112. }
  113. case "create":
  114. if len(fields) == 3 {
  115. _, found := c.db.GetPlayer(fields[1])
  116. if found {
  117. c.write("That name is already taken.\n")
  118. break
  119. }
  120. playerID, _ := c.db.CreatePlayer(strings.TrimSpace(fields[1]), strings.TrimSpace(fields[2]), nil)
  121. _, found = c.db.Fetch(playerID)
  122. if !found {
  123. c.write("I can't even.\n")
  124. break
  125. }
  126. c.write("You have been created. Now you can connect!\n")
  127. } else {
  128. c.write("What?\n")
  129. }
  130. case "quit":
  131. c.connected = false
  132. default:
  133. c.write("What?\n")
  134. }
  135. }
  136. func (c *Client) handleInboundEvent(m PlayerEvent) {
  137. switch m.messageType {
  138. case EventTypeOutput:
  139. c.write("%s\n", m.message)
  140. case EventTypeQuit:
  141. c.connected = false
  142. }
  143. }
  144. func (c *Client) write(format string, a ...interface{}) {
  145. fmt.Fprintf(c.conn, format, a...)
  146. }
  147. func (c *Client) sendCommand(message string) {
  148. c.outbound <- PlayerEvent{src: c.playerID, dst: c.playerID, message: message, messageType: EventTypeCommand, connectionID: c.connectionID}
  149. }