No Description

client.go 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. package funmow
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "net"
  7. "regexp"
  8. "strings"
  9. "unicode"
  10. )
  11. type Client struct {
  12. conn net.Conn
  13. connectionID int
  14. player Object
  15. authenticated bool
  16. connected bool
  17. reader *bufio.Reader
  18. commandChan chan string
  19. inbound chan PlayerEvent
  20. outbound EventChanSet
  21. eventDistributor *EventDistributor
  22. db *DB
  23. factory *ObjectFactory
  24. }
  25. func NewClient(conn net.Conn, connectionID int, e *EventDistributor, w *DB) *Client {
  26. c := new(Client)
  27. c.conn = conn
  28. c.connectionID = connectionID
  29. c.reader = bufio.NewReader(conn)
  30. c.authenticated = false
  31. c.connected = true
  32. c.commandChan = make(chan string)
  33. c.inbound = make(chan PlayerEvent)
  34. c.eventDistributor = e
  35. c.db = w
  36. c.factory = NewObjectFactory(w)
  37. return c
  38. }
  39. func (c *Client) Run() {
  40. log.Print("Received connection from ", c.conn.RemoteAddr())
  41. go func() {
  42. for c.connected {
  43. message, err := c.reader.ReadString('\n')
  44. if err != nil {
  45. c.connected = false
  46. return
  47. }
  48. message = strings.TrimSpace(message)
  49. if len(message) > 0 {
  50. c.commandChan <- message
  51. }
  52. }
  53. }()
  54. c.splash()
  55. for c.connected {
  56. select {
  57. case m := <-c.inbound:
  58. inside, _ := c.db.GetParent(c.player.ID)
  59. switch m.messageType {
  60. case EventTypeEmit:
  61. fallthrough
  62. case EventTypeOEmit:
  63. if m.audience == inside {
  64. c.write("%s\n", m.message)
  65. }
  66. case EventTypePEmit:
  67. c.write("%s\n", m.message)
  68. case EventTypeWall:
  69. speaker, found := c.db.Fetch(m.src)
  70. if !found {
  71. break
  72. }
  73. c.write("In the distance, you hear %s bellow out \"%s\"\n", speaker.Name, m.message)
  74. case EventTypePage:
  75. speaker, found := c.db.Fetch(m.src)
  76. if !found {
  77. break
  78. }
  79. c.write("%s pages you: \"%s\"\n", speaker.Name, m.message)
  80. case EventTypeSay:
  81. if m.audience == inside {
  82. if m.src == c.player.ID {
  83. c.write("You say \"%s\"\n", m.message)
  84. } else {
  85. speaker, found := c.db.Fetch(m.src)
  86. if !found {
  87. break
  88. }
  89. c.write("%s says \"%s\"\n", speaker.Name, m.message)
  90. }
  91. }
  92. case EventTypePose:
  93. if m.audience == inside {
  94. if m.src == c.player.ID {
  95. c.write("%s %s\n", c.player.Name, m.message)
  96. } else {
  97. speaker, found := c.db.Fetch(m.src)
  98. if !found {
  99. break
  100. }
  101. c.write("%s %s\n", speaker.Name, m.message)
  102. }
  103. }
  104. }
  105. case cmd := <-c.commandChan:
  106. if c.authenticated {
  107. c.handlePlayPhase(cmd)
  108. } else {
  109. c.handleLoginPhase(cmd)
  110. }
  111. }
  112. }
  113. if c.authenticated {
  114. c.outbound.shutdownChan <- c.connectionID
  115. }
  116. c.conn.Close()
  117. log.Print("Lost connection from ", c.conn.RemoteAddr())
  118. }
  119. func (c *Client) splash() {
  120. c.write("Welcome to...\n")
  121. c.write(" ___ __ __ ___ __ __\n")
  122. c.write(" | __| _ _ _ _ | \\/ | / _ \\ \\ \\ / /\n")
  123. c.write(" | _| | || | | ' \\ | |\\/| | | (_) | \\ \\/\\/ /\n")
  124. c.write(" |_| \\_,_| |_||_| |_| |_| \\___/ \\_/\\_/\n\n")
  125. c.write("use connect <username> <password> to login.\n")
  126. }
  127. func (c *Client) handleLoginPhase(message string) {
  128. fields := strings.FieldsFunc(message, func(c rune) bool {
  129. return unicode.IsSpace(c)
  130. })
  131. switch fields[0] {
  132. case "help":
  133. c.write("Commands:\n\tcon[nect] <username>\n\tquit\n")
  134. case "con":
  135. fallthrough
  136. case "connect":
  137. if len(fields) == 3 {
  138. pID, err := c.db.GetPlayerID(fields[1])
  139. if err != nil {
  140. c.write("Bad username or password.\n")
  141. break
  142. }
  143. player, found := c.db.Fetch(pID)
  144. if !found {
  145. c.write("You appear to be having an existential crisis.\n")
  146. c.connected = false
  147. break
  148. }
  149. c.authenticated = true
  150. c.player = player
  151. // when a player authenticates, the ipc channels get turned on
  152. subReq := EventSubscribeRequest{
  153. connectionID: c.connectionID,
  154. playerID: c.player.ID,
  155. inbound: c.inbound,
  156. chanSet: make(chan EventChanSet),
  157. }
  158. c.outbound = c.eventDistributor.Subscribe(subReq)
  159. c.write("Welcome back, %s!\n", fields[1])
  160. c.lookCmd("")
  161. } else {
  162. c.write("What?\n")
  163. }
  164. case "quit":
  165. c.quitCmd()
  166. default:
  167. c.write("What?\n")
  168. }
  169. }
  170. func (c *Client) handlePlayPhase(message string) {
  171. switch {
  172. case message == "l":
  173. c.lookCmd("")
  174. case message == "look":
  175. c.lookCmd("")
  176. case strings.HasPrefix(message, "l "): // look at
  177. c.lookCmd(strings.TrimPrefix(message, "l "))
  178. case strings.HasPrefix(message, "look "): // look at
  179. c.lookCmd(strings.TrimPrefix(message, "look "))
  180. case strings.HasPrefix(message, "ex "):
  181. c.examineCmd(strings.TrimPrefix(message, "ex "))
  182. case strings.HasPrefix(message, "examine "):
  183. c.examineCmd(strings.TrimPrefix(message, "examine "))
  184. case strings.HasPrefix(message, "\""):
  185. c.sayCmd(strings.TrimPrefix(message, "\""))
  186. case strings.HasPrefix(message, "say "):
  187. c.sayCmd(strings.TrimPrefix(message, "say "))
  188. case strings.HasPrefix(message, ":"):
  189. c.poseCmd(strings.TrimPrefix(message, ":"))
  190. case strings.HasPrefix(message, "pose "):
  191. c.poseCmd(strings.TrimPrefix(message, "pose "))
  192. case message == "i":
  193. c.inventoryCmd()
  194. case message == "inventory":
  195. c.inventoryCmd()
  196. case strings.HasPrefix(message, "get "):
  197. c.getCmd(strings.TrimPrefix(message, "get "))
  198. case strings.HasPrefix(message, "drop "):
  199. c.dropCmd(strings.TrimPrefix(message, "drop "))
  200. case strings.HasPrefix(message, "enter "):
  201. c.enterCmd(strings.TrimPrefix(message, "enter "))
  202. case message == "leave":
  203. c.leaveCmd()
  204. case message == "quit":
  205. c.quitCmd()
  206. case message == "WHO":
  207. c.whoCmd()
  208. case strings.HasPrefix(message, "@create "):
  209. c.createCmd(strings.TrimPrefix(message, "@create "))
  210. case strings.HasPrefix(message, "@dig "):
  211. c.digCmd(message)
  212. case strings.HasPrefix(message, "@open "):
  213. c.openCmd(message)
  214. case strings.HasPrefix(message, "@name "):
  215. c.nameCmd(message)
  216. case strings.HasPrefix(message, "@desc "):
  217. c.descCmd(message)
  218. case strings.HasPrefix(message, "@tel "):
  219. c.telCmd(strings.TrimPrefix(message, "@tel "))
  220. case strings.HasPrefix(message, "@dump "):
  221. c.dumpCmd(strings.TrimPrefix(message, "@dump "))
  222. case strings.HasPrefix(message, "@destroy "):
  223. c.destroyCmd(strings.TrimPrefix(message, "@destroy "))
  224. default:
  225. if !c.goCmd(message) {
  226. c.write("What?\n")
  227. }
  228. }
  229. }
  230. func (c *Client) lookCmd(at string) {
  231. roomID, found := c.db.GetParent(c.player.ID)
  232. room, found := c.db.Fetch(roomID)
  233. if !found {
  234. c.write("True Limbo (#NaN)\nThere's nothing to see here. You are inside an object that doesn't exist.\n")
  235. return
  236. }
  237. if len(at) > 0 {
  238. object, matchType := room.MatchLinkNames(at, c.player.ID, false).ExactlyOne()
  239. switch matchType {
  240. case MatchOne:
  241. c.write("%s\n%s\n", object.DetailedName(), object.Description)
  242. case MatchNone:
  243. c.write("I don't see that here.\n")
  244. case MatchMany:
  245. c.write("I don't now which one you're trying to look at.\n")
  246. }
  247. } else {
  248. c.write("%s\n%s\n", room.DetailedName(), room.Description)
  249. c.lookLinks(&room, "thing", "Things:")
  250. c.lookLinks(&room, "player", "Players:")
  251. exits := room.GetLinkNames("exit", nil)
  252. if len(exits) > 0 {
  253. c.write("Exits:\n")
  254. for _, e := range exits {
  255. aliases := strings.Split(e, ";")
  256. c.write("%s ", aliases[0])
  257. }
  258. c.write("\n")
  259. }
  260. }
  261. }
  262. func (c *Client) objectName(id DBRef) string {
  263. o, found := c.db.Fetch(id)
  264. if found {
  265. return o.DetailedName()
  266. } else {
  267. return fmt.Sprintf("MISSING OBJECT (#%d)", id)
  268. }
  269. }
  270. func (c *Client) examineCmd(at string) {
  271. roomID, found := c.db.GetParent(c.player.ID)
  272. room, found := c.db.Fetch(roomID)
  273. if !found {
  274. return
  275. }
  276. object, matchType := room.MatchLinkNames(at, c.player.ID, false).ExactlyOne()
  277. switch matchType {
  278. case MatchOne:
  279. objectParentID, _ := c.db.GetParent(object.ID)
  280. c.write("%s\n", object.DetailedName())
  281. c.write("ID: %d\n", object.ID)
  282. c.write("Type: %s\n", object.Type)
  283. c.write("@name: %s\n", object.Name)
  284. c.write("@desc: %s\n", object.Description)
  285. c.write("Inside: %s\n", c.objectName(objectParentID))
  286. c.write("Next: %s\n", c.objectName(object.Next))
  287. c.write("Owner: %s\n", c.objectName(object.Owner))
  288. inventory := object.GetLinkNames("*", nil)
  289. if len(inventory) > 0 {
  290. c.write("Contents:\n %s\n", strings.Join(inventory, "\n "))
  291. }
  292. case MatchNone:
  293. c.write("I don't see that here.\n")
  294. case MatchMany:
  295. c.write("I don't know which one you're trying to examine.\n")
  296. }
  297. }
  298. func (c *Client) lookLinks(o *Object, linkType string, pretty string) {
  299. linknames := o.GetLinkNames(linkType, DBRefList{c.player.ID})
  300. if len(linknames) > 0 {
  301. c.write("%s\n %s\n", pretty, strings.Join(linknames, "\n "))
  302. }
  303. }
  304. func (c *Client) sayCmd(message string) {
  305. inside, _ := c.db.GetParent(c.player.ID)
  306. c.outbound.messageChan <- PlayerEvent{audience: inside, src: c.player.ID, message: message, messageType: EventTypeSay}
  307. }
  308. func (c *Client) poseCmd(message string) {
  309. inside, _ := c.db.GetParent(c.player.ID)
  310. c.outbound.messageChan <- PlayerEvent{audience: inside, src: c.player.ID, dst: c.player.ID, message: message, messageType: EventTypePose}
  311. }
  312. func (c *Client) inventoryCmd() {
  313. inventory := c.player.GetLinkNames("*", nil)
  314. if len(inventory) > 0 {
  315. c.write("Inventory:\n %s\n", strings.Join(inventory, "\n "))
  316. } else {
  317. c.write("You're not carrying anything.\n")
  318. }
  319. }
  320. func (c *Client) getCmd(message string) {
  321. roomID, found := c.db.GetParent(c.player.ID)
  322. room, found := c.db.Fetch(roomID)
  323. if !found {
  324. return
  325. }
  326. object, matchType := room.MatchLinkNames(message, c.player.ID, true).ExactlyOne()
  327. switch matchType {
  328. case MatchOne:
  329. err := c.player.Contains(&object)
  330. if err != nil {
  331. return
  332. }
  333. c.player.Refresh()
  334. c.write("You pick up %s.\n", object.Name)
  335. c.oemit(room.ID, "%s picks up %s.", c.player.Name, object.Name)
  336. case MatchNone:
  337. c.write("I don't see that here.\n")
  338. case MatchMany:
  339. c.write("I don't know which one.\n")
  340. }
  341. }
  342. func (c *Client) dropCmd(message string) {
  343. roomID, found := c.db.GetParent(c.player.ID)
  344. room, found := c.db.Fetch(roomID)
  345. if !found {
  346. return
  347. }
  348. object, matchType := c.player.MatchLinkNames(message, c.player.ID, false).ExactlyOne()
  349. switch matchType {
  350. case MatchOne:
  351. err := room.Contains(&object)
  352. if err != nil {
  353. return
  354. }
  355. inside, _ := c.db.GetParent(c.player.ID)
  356. c.player.Refresh()
  357. c.write("You drop %s.\n", object.Name)
  358. c.oemit(inside, "%s drops %s.", c.player.Name, object.Name)
  359. case MatchNone:
  360. c.write("You're not carrying that.\n")
  361. case MatchMany:
  362. c.write("I don't now which one.\n")
  363. }
  364. }
  365. func (c *Client) enterCmd(message string) {
  366. roomID, found := c.db.GetParent(c.player.ID)
  367. room, found := c.db.Fetch(roomID)
  368. if !found {
  369. return
  370. }
  371. object, matchType := room.MatchLinkNames(message, c.player.ID, true).ExactlyOne()
  372. switch matchType {
  373. case MatchOne:
  374. err := object.Contains(&c.player)
  375. if err != nil {
  376. return
  377. }
  378. c.player.Refresh()
  379. c.write("You climb into %s.\n", object.Name)
  380. c.oemit(room.ID, "%s climbs into %s.", c.player.Name, object.Name)
  381. c.oemit(object.ID, "%s squeezes into %s with you.", c.player.Name, object.Name)
  382. case MatchNone:
  383. c.write("I don't see that here.\n")
  384. case MatchMany:
  385. c.write("I don't now which one.\n")
  386. }
  387. }
  388. func (c *Client) leaveCmd() {
  389. inside, _ := c.db.GetParent(c.player.ID)
  390. object, found := c.db.Fetch(inside)
  391. if !found {
  392. return
  393. }
  394. objectInside, _ := c.db.GetParent(inside)
  395. if objectInside == 0 { // probably trying to 'leave' a room
  396. c.write("You can't leave here.\n")
  397. return
  398. }
  399. room, found := c.db.Fetch(objectInside)
  400. if !found {
  401. return
  402. }
  403. err := room.Contains(&c.player)
  404. if err != nil {
  405. return
  406. }
  407. c.player.Refresh()
  408. c.write("You climb out of %s.\n", object.Name)
  409. c.oemit(object.ID, "%s climbs out of %s.", c.player.Name, object.Name)
  410. c.oemit(room.ID, "%s climbs out of %s.", c.player.Name, object.Name)
  411. }
  412. func (c *Client) quitCmd() {
  413. c.write("So long, it's been good to know yah.\n")
  414. c.connected = false
  415. }
  416. func (c *Client) whoCmd() {
  417. // onlinePlayers := c.eventDistributor.OnlinePlayers()
  418. c.write("Currently Online:\n")
  419. //for _, ref := range onlinePlayers {
  420. // c.write("%s\n", c.db.GetName(ref))
  421. //}
  422. }
  423. func (c *Client) createCmd(message string) {
  424. roomID, found := c.db.GetParent(c.player.ID)
  425. room, found := c.db.Fetch(roomID)
  426. if !found {
  427. return
  428. }
  429. o := c.factory.NewThing()
  430. o.Name = strings.TrimSpace(message)
  431. o.Owner = c.player.ID
  432. o.Commit()
  433. err := room.Contains(&o)
  434. if err != nil {
  435. return
  436. }
  437. c.emit(room.ID, "A %s appears out of the ether.", o.Name)
  438. c.write("%s Created.\n", o.DetailedName())
  439. }
  440. func (c *Client) openCmd(input string) {
  441. // @open <in1;in2;in3;etc>=#<room>,<out1;out2;out3;etc>
  442. r, _ := regexp.Compile(`^@open\pZ+([^=]*[^=\pZ]+)\pZ*=#([0-9]+)(?:\pZ*,\pZ*([^,]*[^,\pZ]+)\pZ*)?`)
  443. params := r.FindStringSubmatch(input)
  444. if params == nil {
  445. return
  446. }
  447. inExit, roomIDStr, outExit := params[1], params[2], params[3]
  448. if len(inExit) == 0 || len(roomIDStr) == 0 {
  449. c.write("Bad command or file name.\n")
  450. return
  451. }
  452. targetID, _ := NewDBRefFromString(roomIDStr) // this will never fail, the regexp guarantees that
  453. targetRoom, found := c.db.Fetch(targetID)
  454. if !found {
  455. c.write("Target not found.\n")
  456. return
  457. }
  458. roomID, found := c.db.GetParent(c.player.ID)
  459. room, found := c.db.Fetch(roomID)
  460. if !found {
  461. return
  462. }
  463. toExit := c.factory.NewExit(inExit, targetRoom.ID, c.player.ID)
  464. toExit.Commit()
  465. err := room.Contains(&toExit)
  466. if err != nil {
  467. return
  468. }
  469. c.write("%s Created.\n", toExit.DetailedName())
  470. if len(outExit) > 0 {
  471. fromExit := c.factory.NewExit(outExit, room.ID, c.player.ID)
  472. fromExit.Commit()
  473. err = targetRoom.Contains(&fromExit)
  474. if err != nil {
  475. return
  476. }
  477. c.write("%s Created.\n", fromExit.DetailedName())
  478. }
  479. }
  480. func (c *Client) digCmd(input string) {
  481. // @dig <Room name>=<in1;in2;in3;etc>,<out1;out2;out3;etc>
  482. //@dig foo bar = <F>oo;foo;f,<B>ack;back;b
  483. r, _ := regexp.Compile(`^@dig\pZ+([^=]*[^=\pZ]+)(\pZ*=\pZ*(?:([^,]*[^,\pZ]+)\pZ*)?(?:\pZ*,\pZ*([^,]*[^,\pZ]+)\pZ*)?)?`)
  484. params := r.FindStringSubmatch(input)
  485. if params == nil {
  486. return
  487. }
  488. roomName, inExit, outExit := params[1], params[2], params[3]
  489. if len(roomName) == 0 {
  490. c.write("Rooms can't not have names.\n")
  491. return
  492. }
  493. newRoom := c.factory.NewRoom()
  494. newRoom.Name = roomName
  495. newRoom.Owner = c.player.ID
  496. newRoom.Commit()
  497. c.write("%s Created.\n", newRoom.DetailedName())
  498. if len(inExit) > 0 || len(outExit) > 0 {
  499. roomID, found := c.db.GetParent(c.player.ID)
  500. room, found := c.db.Fetch(roomID)
  501. if !found {
  502. return
  503. }
  504. if len(inExit) > 0 {
  505. toExit := c.factory.NewExit(inExit, newRoom.ID, c.player.ID)
  506. toExit.Commit()
  507. err := room.Contains(&toExit)
  508. if err != nil {
  509. return
  510. }
  511. c.write("%s Created.\n", toExit.DetailedName())
  512. }
  513. if len(outExit) > 0 {
  514. fromExit := c.factory.NewExit(outExit, room.ID, c.player.ID)
  515. fromExit.Commit()
  516. err := newRoom.Contains(&fromExit)
  517. if err != nil {
  518. return
  519. }
  520. c.write("%s Created.\n", fromExit.DetailedName())
  521. }
  522. }
  523. }
  524. func (c *Client) nameCmd(input string) {
  525. r, _ := regexp.Compile(`^@name\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  526. params := r.FindStringSubmatch(input)
  527. if params == nil {
  528. return
  529. }
  530. objectName, name := params[1], params[2]
  531. roomID, found := c.db.GetParent(c.player.ID)
  532. room, found := c.db.Fetch(roomID)
  533. if !found {
  534. return
  535. }
  536. candidate, matchType := room.MatchLinkNames(objectName, c.player.ID, false).ExactlyOne()
  537. switch matchType {
  538. case MatchOne:
  539. candidate.Name = name
  540. candidate.Commit()
  541. c.write("Name set.\n")
  542. c.player.Refresh()
  543. case MatchNone:
  544. c.write("I don't see that here.\n")
  545. case MatchMany:
  546. c.write("I don't now which one.\n")
  547. }
  548. }
  549. func (c *Client) descCmd(input string) {
  550. r, _ := regexp.Compile(`^@desc\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  551. params := r.FindStringSubmatch(input)
  552. if params == nil {
  553. return
  554. }
  555. objectName, description := params[1], params[2]
  556. roomID, found := c.db.GetParent(c.player.ID)
  557. room, found := c.db.Fetch(roomID)
  558. if !found {
  559. return
  560. }
  561. var editObject *Object
  562. switch objectName {
  563. case "here":
  564. editObject = &room
  565. case "me":
  566. editObject = &c.player
  567. default:
  568. candidate, matchType := room.MatchLinkNames(objectName, c.player.ID, false).ExactlyOne()
  569. switch matchType {
  570. case MatchOne:
  571. editObject = &candidate
  572. case MatchNone:
  573. c.write("I don't see that here.\n")
  574. return
  575. case MatchMany:
  576. c.write("I don't now which one.\n")
  577. return
  578. }
  579. }
  580. editObject.Description = description
  581. editObject.Commit()
  582. c.player.Refresh()
  583. c.write("Description set.\n")
  584. }
  585. func (c *Client) telCmd(destStr string) {
  586. dest, err := NewDBRefFromHashRef(destStr)
  587. if err != nil {
  588. c.write("That doesn't look like a DBRef.\n")
  589. return
  590. }
  591. newRoom, found := c.db.Fetch(dest)
  592. if !found {
  593. c.write("That doesn't exist.\n")
  594. return
  595. }
  596. c.write("You feel an intense wooshing sensation.\n")
  597. err = newRoom.Contains(&c.player)
  598. if err != nil {
  599. return
  600. }
  601. c.player.Refresh()
  602. c.lookCmd("")
  603. }
  604. func (c *Client) dumpCmd(refStr string) {
  605. ref, err := NewDBRefFromHashRef(refStr)
  606. if err != nil {
  607. c.write("That doesn't look like a DBRef.\n")
  608. return
  609. }
  610. obj, found := c.db.Fetch(ref)
  611. if !found {
  612. c.write("That doesn't exist.\n")
  613. return
  614. }
  615. c.write("%s\n", c.db.DumpObject(obj.ID))
  616. }
  617. func (c *Client) destroyCmd(message string) {
  618. roomID, found := c.db.GetParent(c.player.ID)
  619. room, found := c.db.Fetch(roomID)
  620. if !found {
  621. return
  622. }
  623. object, matchType := room.MatchLinkNames(message, c.player.ID, true).ExactlyOne()
  624. switch matchType {
  625. case MatchOne:
  626. name := object.DetailedName()
  627. err := object.Delete()
  628. if err == nil {
  629. c.player.Refresh()
  630. c.write("%s vanishes into thin air.\n", name)
  631. }
  632. case MatchNone:
  633. c.write("I don't see that here.\n")
  634. case MatchMany:
  635. c.write("I don't know which one.\n")
  636. }
  637. }
  638. func (c *Client) goCmd(dir string) bool {
  639. roomID, found := c.db.GetParent(c.player.ID)
  640. room, found := c.db.Fetch(roomID)
  641. if !found {
  642. return false
  643. }
  644. exit, matchType := room.MatchExitNames(dir).ExactlyOne()
  645. switch matchType {
  646. case MatchOne:
  647. if exit.Next.Valid() {
  648. newRoom, found := c.db.Fetch(exit.Next)
  649. if !found {
  650. c.write("That exit appears to be broken!\n")
  651. return true
  652. }
  653. err := newRoom.Contains(&c.player)
  654. if err != nil {
  655. return false
  656. }
  657. c.player.Refresh()
  658. c.write("You head towards %s.\n", newRoom.Name)
  659. c.oemit(room.ID, "%s leaves the room.", c.player.Name)
  660. c.oemit(newRoom.ID, "%s enters the room.", c.player.Name)
  661. return true
  662. }
  663. case MatchNone:
  664. return false
  665. case MatchMany:
  666. c.write("Ambiguous exit names are ambiguous.\n")
  667. return true
  668. }
  669. return false
  670. }
  671. func (c *Client) oemit(audience DBRef, format string, a ...interface{}) {
  672. message := fmt.Sprintf(format, a...)
  673. c.outbound.messageChan <- PlayerEvent{audience: audience, src: c.player.ID, dst: c.player.ID, message: message, messageType: EventTypeOEmit}
  674. }
  675. func (c *Client) write(format string, a ...interface{}) {
  676. fmt.Fprintf(c.conn, format, a...)
  677. }
  678. func (c *Client) pemit(audience DBRef, format string, a ...interface{}) {
  679. message := fmt.Sprintf(format, a...)
  680. c.outbound.messageChan <- PlayerEvent{audience: audience, src: c.player.ID, dst: c.player.ID, message: message, messageType: EventTypePEmit}
  681. }
  682. func (c *Client) emit(audience DBRef, format string, a ...interface{}) {
  683. message := fmt.Sprintf(format, a...)
  684. c.outbound.messageChan <- PlayerEvent{audience: audience, src: c.player.ID, dst: c.player.ID, message: message, messageType: EventTypeEmit}
  685. }