ask.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package server
  2. /* This file contains dialog helpers for the client. */
  3. // import "github.com/beoran/woe/monolog"
  4. import t "github.com/beoran/woe/telnet"
  5. import "github.com/beoran/woe/telnet"
  6. import "github.com/beoran/woe/world"
  7. import "github.com/beoran/woe/monolog"
  8. import "bytes"
  9. import "regexp"
  10. // import "fmt"
  11. import "strconv"
  12. // import "strings"
  13. const NEW_CHARACTER_PRICE = 4
  14. // Switches to "password" mode.
  15. func (me * Client) PasswordMode() telnet.Event {
  16. // The server sends "IAC WILL ECHO", meaning "I, the server, will do any
  17. // echoing from now on." The client should acknowledge this with an IAC DO
  18. // ECHO, and then stop putting echoed text in the input buffer.
  19. // It should also do whatever is appropriate for password entry to the input
  20. // box thing - for example, it might * it out. Text entered in server-echoes
  21. // mode should also not be placed any command history.
  22. // don't use the Q state machne for echos
  23. me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WILL, t.TELNET_TELOPT_ECHO)
  24. tev, _, _:= me.TryReadEvent(100)
  25. if tev != nil && !telnet.IsEventType(tev, t.TELNET_DO_EVENT) {
  26. return tev
  27. }
  28. return nil
  29. }
  30. // Switches to "normal, or non-password mode.
  31. func (me * Client) NormalMode() telnet.Event {
  32. // When the server wants the client to start local echoing again, it s}s
  33. // "IAC WONT ECHO" - the client must respond to this with "IAC DONT ECHO".
  34. // Again don't use Q state machine.
  35. me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WONT, t.TELNET_TELOPT_ECHO)
  36. tev, _, _ := me.TryReadEvent(100)
  37. if tev != nil && !telnet.IsEventType(tev, t.TELNET_DONT_EVENT) {
  38. return tev
  39. }
  40. return nil
  41. }
  42. func (me * Client) Printf(format string, args ...interface{}) {
  43. me.telnet.TelnetPrintf(format, args...)
  44. }
  45. func (me * Client) ColorTest() {
  46. me.Printf("\033[1mBold\033[0m\r\n")
  47. me.Printf("\033[3mItalic\033[0m\r\n")
  48. me.Printf("\033[4mUnderline\033[0m\r\n")
  49. for fg := 30; fg < 38; fg++ {
  50. me.Printf("\033[%dmForeground Color %d\033[0m\r\n", fg, fg)
  51. me.Printf("\033[1;%dmBold Foreground Color %d\033[0m\r\n", fg, fg)
  52. }
  53. for bg := 40; bg < 48; bg++ {
  54. me.Printf("\033[%dmBackground Color %d\033[0m\r\n", bg, bg)
  55. me.Printf("\033[1;%dmBold Background Color %d\033[0m\r\n", bg, bg)
  56. }
  57. }
  58. // Blockingly reads a single command from the client
  59. func (me * Client) ReadCommand() (something []byte) {
  60. something = nil
  61. for something == nil {
  62. something, _, _ = me.TryRead(-1)
  63. if something != nil {
  64. something = bytes.TrimRight(something, "\r\n")
  65. return something
  66. }
  67. }
  68. return nil
  69. }
  70. func (me * Client) AskSomething(prompt string, re string, nomatch_prompt string, noecho bool) (something []byte) {
  71. something = nil
  72. if noecho {
  73. me.PasswordMode()
  74. }
  75. for something == nil || len(something) == 0 {
  76. me.Printf("%s:", prompt)
  77. something, _, _ = me.TryRead(-1)
  78. if something != nil {
  79. something = bytes.TrimRight(something, "\r\n")
  80. if len(re) > 0 {
  81. ok, _ := regexp.Match(re, something)
  82. if !ok {
  83. me.Printf("\n%s\n", nomatch_prompt)
  84. something = nil
  85. }
  86. }
  87. }
  88. }
  89. if noecho {
  90. me.NormalMode()
  91. me.Printf("\n")
  92. }
  93. return something
  94. }
  95. func (me * Client) AskYesNo(prompt string) bool {
  96. res := me.AskSomething(prompt + " (y/n)","[ynYN]", "Please answer y or n.", false)
  97. if res[0] == 'Y'|| res[0] == 'y' {
  98. return true
  99. } else {
  100. return false
  101. }
  102. }
  103. func (me * Client) AskEntityListOnce(heading string, prompt string, noecho bool, elist world.EntitylikeSlice) (result world.Entitylike) {
  104. list := elist.FilterPrivilege(me.account.Privilege)
  105. me.Printf("\n%s\n\n",heading)
  106. for i, v := range(list) {
  107. e := v.AsEntity()
  108. me.Printf("[%d] %s: %s\n", i+1, e.Name, e.Short)
  109. }
  110. me.Printf("\n")
  111. aid := me.AskSomething(prompt, "", "", false);
  112. iresp, err := strconv.Atoi(string(aid))
  113. if err != nil { /* Try name. */
  114. e := list.FindName(string(aid))
  115. if e != nil {
  116. return e
  117. } else {
  118. me.Printf("Name not found in list. Please choose a number or name from the list above.\n")
  119. }
  120. } else if (iresp>0) && (iresp<=len(list)) { /* In range. */
  121. return list[iresp-1]
  122. } else {
  123. me.Printf("Please choose a number or name from the list above.\n")
  124. }
  125. return nil
  126. }
  127. func (me * Client) AskEntityList(heading string, prompt string, noecho bool, list world.EntitylikeSlice) (result world.Entitylike) {
  128. for {
  129. result = me.AskEntityListOnce(heading, prompt, noecho, list)
  130. if result != nil {
  131. e := result.AsEntity()
  132. me.Printf("\n%s: %s\n\n%s\n\n", e.Name, e.Short, e.Long)
  133. if noecho || me.AskYesNo("Confirm?") {
  134. return result
  135. }
  136. }
  137. }
  138. }
  139. const LOGIN_RE = "^[A-Za-z]+$"
  140. func (me * Client) AskLogin() []byte {
  141. return me.AskSomething("Login", LOGIN_RE, "Login must consist of a letter followed by letters or numbers.", false)
  142. }
  143. const EMAIL_RE = "@"
  144. func (me * Client) AskEmail() []byte {
  145. return me.AskSomething("E-mail", EMAIL_RE, "Email must have at least an @ in there somewhere.", false)
  146. }
  147. func (me * Client) AskCharacterName() []byte {
  148. return me.AskSomething("Character Name", LOGIN_RE, "Character name consisst of letters only.", false)
  149. }
  150. func (me * Client) AskPassword() []byte {
  151. return me.AskSomething("Password", "", "", true)
  152. }
  153. func (me * Client) AskRepeatPassword() []byte {
  154. return me.AskSomething("Repeat Password", "", "", true)
  155. }
  156. func (me * Client) HandleCommand() {
  157. command := me.ReadCommand()
  158. me.ProcessCommand(command)
  159. }
  160. func (me * Client) ExistingAccountDialog() bool {
  161. pass := me.AskPassword()
  162. for pass == nil {
  163. me.Printf("Password may not be empty!\n")
  164. pass = me.AskPassword()
  165. }
  166. if !me.account.Challenge(string(pass)) {
  167. me.Printf("Password not correct!\n")
  168. me.Printf("Disconnecting!\n")
  169. return false
  170. }
  171. return true
  172. }
  173. func (me * Client) NewAccountDialog(login string) bool {
  174. for me.account == nil {
  175. me.Printf("\nWelcome, %s! Creating new account...\n", login)
  176. pass1 := me.AskPassword()
  177. if pass1 == nil {
  178. return false
  179. }
  180. pass2 := me.AskRepeatPassword()
  181. if pass1 == nil {
  182. return false
  183. }
  184. if string(pass1) != string(pass2) {
  185. me.Printf("\nPasswords do not match! Please try again!\n")
  186. continue
  187. }
  188. email := me.AskEmail()
  189. if email == nil { return false }
  190. me.account = world.NewAccount(login, string(pass1), string(email), 7)
  191. err := me.account.Save(me.server.DataPath())
  192. if err != nil {
  193. monolog.Error("Could not save account %s: %v", login, err)
  194. me.Printf("\nFailed to save your account!\nPlease contact a WOE administrator!\n")
  195. return false
  196. }
  197. monolog.Info("Created new account %s", login)
  198. me.Printf("\nSaved your account.\n")
  199. return true
  200. }
  201. return false
  202. }
  203. func (me * Client) AccountDialog() bool {
  204. login := me.AskLogin()
  205. if login == nil { return false }
  206. var err error
  207. if me.server.World.GetAccount(string(login)) != nil {
  208. me.Printf("Account already logged in!\n")
  209. me.Printf("Disconnecting!\n")
  210. return false
  211. }
  212. me.account, err = me.server.World.LoadAccount(string(login))
  213. if err != nil {
  214. monolog.Warning("Could not load account %s: %v", login, err)
  215. }
  216. if me.account != nil {
  217. return me.ExistingAccountDialog()
  218. } else {
  219. return me.NewAccountDialog(string(login))
  220. }
  221. }
  222. func (me * Client) NewCharacterDialog() bool {
  223. me.Printf("New character:\n")
  224. charname := me.AskCharacterName()
  225. kin := me.AskEntityList("Please choose the kin of this character", "Kin of character? ", false, world.KinEntityList)
  226. me.Printf("%s %v\n", charname, kin)
  227. gender := me.AskEntityList("Please choose the gender of this character", "Gender? ", false, world.GenderList)
  228. me.Printf("%s %v\n", charname, gender)
  229. job := me.AskEntityList("Please choose the job of this character", "Job? ", false, world.JobEntityList)
  230. me.Printf("%s %v\n", charname, job)
  231. character := world.NewCharacter(me.account,
  232. string(charname), kin, gender, job)
  233. me.Printf("%s", character.Being.ToStatus());
  234. ok := me.AskYesNo("Is this character ok?")
  235. if (!ok) {
  236. me.Printf("Character creation canceled.\n")
  237. return true
  238. }
  239. me.account.AddCharacter(character)
  240. me.account.Points -= NEW_CHARACTER_PRICE
  241. me.account.Save(me.server.DataPath())
  242. character.Save(me.server.DataPath())
  243. me.Printf("Character %s saved.\n", character.Being.Name)
  244. return true
  245. }
  246. func (me * Client) CharacterDialog() bool {
  247. me.Printf("You have %d remaining points.\n", me.account.Points)
  248. for me.account.NumCharacters() < 1 {
  249. me.Printf("You have no characters yet!\n")
  250. if (me.account.Points > 0) {
  251. me.NewCharacterDialog();
  252. } else {
  253. me.Printf("Sorry, you have no points left to make new characters!\n")
  254. me.Printf("Please contact the staff of WOE if you think this is a mistake.\n")
  255. me.Printf("Disconnecting!\n")
  256. return false
  257. }
  258. }
  259. return true
  260. }