No Description

execution_context.go 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. package funmow
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "time"
  7. )
  8. type ExecutionContext struct {
  9. actor Object
  10. inbound chan PlayerEvent
  11. outbound chan PlayerEvent
  12. eventDistributor *EventDistributor
  13. db *DB
  14. factory *ObjectFactory
  15. forceContext bool
  16. }
  17. func NewForceContext(e *EventDistributor, w *DB, outbound chan PlayerEvent) *ExecutionContext {
  18. c := new(ExecutionContext)
  19. c.outbound = outbound
  20. c.eventDistributor = e
  21. c.db = w
  22. c.factory = NewObjectFactory(w)
  23. c.forceContext = true
  24. return c
  25. }
  26. func NewExecutionContext(actorID DBRef, e *EventDistributor, w *DB, outbound chan PlayerEvent) *ExecutionContext {
  27. c := new(ExecutionContext)
  28. actor, found := w.Fetch(actorID)
  29. if !found {
  30. return nil
  31. }
  32. c.actor = actor
  33. c.outbound = outbound
  34. c.eventDistributor = e
  35. c.db = w
  36. c.factory = NewObjectFactory(w)
  37. return c
  38. }
  39. func (c *ExecutionContext) StartInboundChannel() chan PlayerEvent {
  40. c.inbound = make(chan PlayerEvent)
  41. inboundBuffer := make(chan PlayerEvent)
  42. c.outbound = c.outbound
  43. go func() {
  44. // An inbound event buffer to protect the event distributor from long
  45. // running tasks. The alternative is to run each inbound event in a
  46. // separate goroutine but that has potential problems (ie, two emits
  47. // arrive in order; the first one requires an extra DB lookup to sort
  48. // out context. Now the second one ends up sending its output event
  49. // before the first, and the client sees things backwards.
  50. queue := make([]*PlayerEvent, 0)
  51. running := true
  52. for running {
  53. if len(queue) == 0 {
  54. select {
  55. case newEvent, ok := <-c.inbound:
  56. if !ok {
  57. running = false
  58. break
  59. }
  60. queue = append(queue, &newEvent)
  61. }
  62. } else {
  63. event := queue[0]
  64. select {
  65. case newEvent, ok := <-c.inbound:
  66. if !ok {
  67. running = false
  68. break
  69. }
  70. queue = append(queue, &newEvent)
  71. case inboundBuffer <- *event:
  72. queue = queue[1:]
  73. }
  74. }
  75. }
  76. // closure of c.inbound is the signal from the event distributor that we need to die.
  77. // we then close inboundBuffer to tell the event goroutine to die
  78. close(inboundBuffer)
  79. }()
  80. go func() {
  81. for {
  82. event, ok := <-inboundBuffer
  83. if !ok {
  84. break
  85. }
  86. c.HandleEvent(event)
  87. }
  88. // mark player as offline
  89. if c.actor.Type == "player" {
  90. c.actor.SetFlag("online", "false")
  91. c.actor.Commit()
  92. }
  93. // and finally we tell the event distributor to delete us.
  94. c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, messageType: EventTypeTeardownComplete}
  95. }()
  96. return c.inbound
  97. }
  98. func (c *ExecutionContext) HandleEvent(m PlayerEvent) {
  99. inside, _ := c.db.GetParent(c.actor.ID)
  100. switch m.messageType {
  101. case EventTypeLogin:
  102. if c.actor.Type == "player" {
  103. c.actor.SetFlag("online", "true")
  104. c.actor.Commit()
  105. inside, _ := c.db.GetParent(c.actor.ID)
  106. c.system("Yay! %s has connected! Oh boy!", c.actor.Name)
  107. c.oemit(inside, "You hear a rustling as %s awakens from a slumber.", c.actor.Name)
  108. }
  109. case EventTypeEmit:
  110. fallthrough
  111. case EventTypeOEmit:
  112. if m.dst == inside {
  113. c.output("%s", m.message)
  114. }
  115. case EventTypePEmit:
  116. c.output("%s", m.message)
  117. case EventTypeWall:
  118. speaker, found := c.db.Fetch(m.src)
  119. if !found {
  120. break
  121. }
  122. c.output("In the distance, you hear %s bellow out: \"%s\"", speaker.Name, m.message)
  123. case EventTypeSystem:
  124. c.output(m.message)
  125. case EventTypePage:
  126. speaker, found := c.db.Fetch(m.src)
  127. if !found {
  128. break
  129. }
  130. c.output("%s pages you: \"%s\"", speaker.Name, m.message)
  131. case EventTypeSay:
  132. if m.dst == inside {
  133. speaker, found := c.db.Fetch(m.src)
  134. if !found {
  135. break
  136. }
  137. c.output("%s says \"%s\"", speaker.Name, m.message)
  138. }
  139. case EventTypePose:
  140. if m.dst == inside {
  141. if m.src == c.actor.ID {
  142. c.output("%s %s", c.actor.Name, m.message)
  143. } else {
  144. speaker, found := c.db.Fetch(m.src)
  145. if !found {
  146. break
  147. }
  148. c.output("%s %s", speaker.Name, m.message)
  149. }
  150. }
  151. case EventTypeForce:
  152. actor, found := c.db.Fetch(m.dst)
  153. if !found {
  154. break
  155. }
  156. c.actor = actor
  157. c.evaluateCommand(m)
  158. case EventTypeCommand:
  159. c.evaluateCommand(m)
  160. }
  161. }
  162. func (c *ExecutionContext) evaluateCommand(m PlayerEvent) {
  163. message := m.message
  164. c.actor.Refresh()
  165. switch {
  166. case message == "l":
  167. c.lookCmd("")
  168. case message == "look":
  169. c.lookCmd("")
  170. case strings.HasPrefix(message, "l "): // look at
  171. c.lookCmd(strings.TrimPrefix(message, "l "))
  172. case strings.HasPrefix(message, "look "): // look at
  173. c.lookCmd(strings.TrimPrefix(message, "look "))
  174. case strings.HasPrefix(message, "ex "):
  175. c.examineCmd(strings.TrimPrefix(message, "ex "))
  176. case strings.HasPrefix(message, "examine "):
  177. c.examineCmd(strings.TrimPrefix(message, "examine "))
  178. case strings.HasPrefix(message, "\""):
  179. c.sayCmd(strings.TrimPrefix(message, "\""))
  180. case strings.HasPrefix(message, "say "):
  181. c.sayCmd(strings.TrimPrefix(message, "say "))
  182. case strings.HasPrefix(message, ":"):
  183. c.poseCmd(strings.TrimPrefix(message, ":"))
  184. case strings.HasPrefix(message, "pose "):
  185. c.poseCmd(strings.TrimPrefix(message, "pose "))
  186. case message == "i":
  187. c.inventoryCmd()
  188. case message == "inventory":
  189. c.inventoryCmd()
  190. case strings.HasPrefix(message, "get "):
  191. c.getCmd(strings.TrimPrefix(message, "get "))
  192. case strings.HasPrefix(message, "drop "):
  193. c.dropCmd(strings.TrimPrefix(message, "drop "))
  194. case strings.HasPrefix(message, "enter "):
  195. c.enterCmd(strings.TrimPrefix(message, "enter "))
  196. case message == "leave":
  197. c.leaveCmd()
  198. case message == "quit":
  199. c.quitCmd(m.connectionID)
  200. case message == "WHO":
  201. c.whoCmd()
  202. case message == "HOWLONG":
  203. time.Sleep(5 * time.Second)
  204. case strings.HasPrefix(message, "@create "):
  205. c.createCmd(strings.TrimPrefix(message, "@create "))
  206. case strings.HasPrefix(message, "@dig "):
  207. c.digCmd(message)
  208. case strings.HasPrefix(message, "@open "):
  209. c.openCmd(message)
  210. case strings.HasPrefix(message, "@name "):
  211. c.nameCmd(message)
  212. case strings.HasPrefix(message, "@desc "):
  213. c.descCmd(message)
  214. case strings.HasPrefix(message, "@tel "):
  215. c.telCmd(strings.TrimPrefix(message, "@tel "))
  216. case strings.HasPrefix(message, "@dump "):
  217. c.dumpCmd(strings.TrimPrefix(message, "@dump "))
  218. case strings.HasPrefix(message, "@destroy "):
  219. c.destroyCmd(strings.TrimPrefix(message, "@destroy "))
  220. case strings.HasPrefix(message, "@force "):
  221. c.forceCmd(message)
  222. case strings.HasPrefix(message, "@wizard "):
  223. c.playerFlagCmd(message, "wizard")
  224. default:
  225. if !c.goCmd(message) {
  226. c.output("What?\n")
  227. }
  228. }
  229. }
  230. func (c *ExecutionContext) lookCmd(at string) {
  231. roomID, found := c.db.GetParent(c.actor.ID)
  232. room, found := c.db.Fetch(roomID)
  233. if !found {
  234. c.output("True Limbo (#NaN)\nThere's nothing to see here. You are inside an object that doesn't exist.")
  235. return
  236. }
  237. if len(at) > 0 {
  238. object, matchType := room.MatchLinkNames(at, c.actor.ID).ExactlyOne()
  239. switch matchType {
  240. case MatchOne:
  241. c.output("%s\n%s", object.DetailedName(), object.Description)
  242. case MatchNone:
  243. c.output("I don't see that here.")
  244. case MatchMany:
  245. c.output("I don't now which one you're trying to look at.")
  246. }
  247. } else {
  248. c.output("%s\n%s", room.DetailedName(), room.Description)
  249. lookLinks := func(linkType string, pretty string) {
  250. linknames := room.GetLinkNames(linkType, DBRefList{c.actor.ID})
  251. if len(linknames) > 0 {
  252. c.output("%s\n %s", pretty, strings.Join(linknames, "\n "))
  253. }
  254. }
  255. lookLinks("thing", "Things:")
  256. lookLinks("player", "Players:")
  257. exits := room.GetLinkNames("exit", nil)
  258. if len(exits) > 0 {
  259. c.output("Exits:")
  260. exitList := make([]string, 0)
  261. for _, e := range exits {
  262. aliases := strings.Split(e, ";")
  263. exitList = append(exitList, aliases[0])
  264. }
  265. c.output(strings.Join(exitList, " "))
  266. }
  267. }
  268. }
  269. func (c *ExecutionContext) objectName(id DBRef) string {
  270. o, found := c.db.Fetch(id)
  271. if found {
  272. return o.DetailedName()
  273. } else {
  274. return fmt.Sprintf("MISSING OBJECT (#%d)", id)
  275. }
  276. }
  277. func (c *ExecutionContext) examineCmd(at string) {
  278. roomID, found := c.db.GetParent(c.actor.ID)
  279. room, found := c.db.Fetch(roomID)
  280. if !found {
  281. return
  282. }
  283. object, matchType := room.MatchLinkNames(at, c.actor.ID).ExactlyOne()
  284. switch matchType {
  285. case MatchOne:
  286. objectParentID, _ := c.db.GetParent(object.ID)
  287. c.output("%s", object.DetailedName())
  288. c.output("ID: %d", object.ID)
  289. c.output("Type: %s", object.Type)
  290. c.output("@name: %s", object.Name)
  291. c.output("@desc: %s", object.Description)
  292. c.output("Inside: %s", c.objectName(objectParentID))
  293. c.output("Next: %s", c.objectName(object.Next))
  294. c.output("Owner: %s", c.objectName(object.Owner))
  295. inventory := object.GetLinkNames("*", nil)
  296. if len(inventory) > 0 {
  297. c.output("Contents:\n %s", strings.Join(inventory, "\n "))
  298. }
  299. case MatchNone:
  300. c.output("I don't see that here.")
  301. case MatchMany:
  302. c.output("I don't know which one you're trying to examine.")
  303. }
  304. }
  305. func (c *ExecutionContext) forceCmd(input string) {
  306. r, _ := regexp.Compile(`^@force\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  307. params := r.FindStringSubmatch(input)
  308. if params == nil {
  309. return
  310. }
  311. objectName, command := params[1], params[2]
  312. roomID, found := c.db.GetParent(c.actor.ID)
  313. room, found := c.db.Fetch(roomID)
  314. if !found {
  315. return
  316. }
  317. objectID, err := NewDBRefFromHashRef(objectName)
  318. wizard := c.actor.GetFlag("wizard","false") == "true"
  319. if err == nil {
  320. object, found := c.db.Fetch(objectID)
  321. if !found {
  322. c.output("I can't force what doesn't exist.")
  323. }
  324. if object.Type != "thing" && object.Type != "player" {
  325. c.output("Some things just can't be forced.")
  326. return
  327. }
  328. if object.Type == "player" && !wizard {
  329. c.output("It's not nice to force others to do your bidding.")
  330. return
  331. }
  332. if object.Owner != c.actor.ID && !wizard {
  333. c.output("It's not nice to touch other people's things.")
  334. return
  335. }
  336. c.outbound <- PlayerEvent{src: c.actor.ID, dst: object.ID, message: command, messageType: EventTypeForce}
  337. } else {
  338. object, matchType := room.MatchLinkNames(objectName, c.actor.ID).ExactlyOne()
  339. switch matchType {
  340. case MatchOne:
  341. if object.Type != "thing" && object.Type != "player" {
  342. c.output("Some things just can't be forced.")
  343. return
  344. }
  345. if object.Type == "player" && !wizard {
  346. c.output("It's not nice to force others to do your bidding.")
  347. return
  348. }
  349. if object.Owner != c.actor.ID && !wizard {
  350. c.output("It's not nice to touch other people's things.")
  351. return
  352. }
  353. c.outbound <- PlayerEvent{src: c.actor.ID, dst: object.ID, message: command, messageType: EventTypeForce}
  354. case MatchNone:
  355. c.output("I don't see that here.")
  356. case MatchMany:
  357. c.output("I don't know which one you're trying to examine.")
  358. }
  359. }
  360. }
  361. func (c *ExecutionContext) sayCmd(message string) {
  362. inside, _ := c.db.GetParent(c.actor.ID)
  363. c.output("You say \"%s\"", message)
  364. c.outbound <- PlayerEvent{src: c.actor.ID, dst: inside, message: message, messageType: EventTypeSay}
  365. }
  366. func (c *ExecutionContext) poseCmd(message string) {
  367. inside, _ := c.db.GetParent(c.actor.ID)
  368. c.outbound <- PlayerEvent{src: c.actor.ID, dst: inside, message: message, messageType: EventTypePose}
  369. }
  370. func (c *ExecutionContext) inventoryCmd() {
  371. inventory := c.actor.GetLinkNames("*", nil)
  372. if len(inventory) > 0 {
  373. c.output("Inventory:\n %s", strings.Join(inventory, "\n "))
  374. } else {
  375. c.output("You're not carrying anything.")
  376. }
  377. }
  378. func (c *ExecutionContext) getCmd(message string) {
  379. roomID, found := c.db.GetParent(c.actor.ID)
  380. room, found := c.db.Fetch(roomID)
  381. if !found {
  382. return
  383. }
  384. object, matchType := room.MatchLinkNames(message, c.actor.ID).ExactlyOne()
  385. switch matchType {
  386. case MatchOne:
  387. if object.ID == c.actor.ID {
  388. c.output("You can't pick yourself up.")
  389. return
  390. }
  391. if object.Type == "room" {
  392. c.output("You can't carry a whole room, silly!")
  393. return
  394. }
  395. err := c.actor.Contains(&object)
  396. if err != nil {
  397. return
  398. }
  399. //c.actor.Refresh()
  400. c.output("You pick up %s.", object.Name)
  401. c.pemit(object.ID, "%s picked you up.", c.actor.Name)
  402. c.oemit(room.ID, "%s picks up %s.", c.actor.Name, object.Name)
  403. case MatchNone:
  404. c.output("I don't see that here.")
  405. case MatchMany:
  406. c.output("I don't know which one.")
  407. }
  408. }
  409. func (c *ExecutionContext) dropCmd(message string) {
  410. roomID, found := c.db.GetParent(c.actor.ID)
  411. room, found := c.db.Fetch(roomID)
  412. if !found {
  413. return
  414. }
  415. object, matchType := c.actor.MatchLinkNames(message, c.actor.ID).ExactlyOne()
  416. switch matchType {
  417. case MatchOne:
  418. err := room.Contains(&object)
  419. if err != nil {
  420. return
  421. }
  422. inside, _ := c.db.GetParent(c.actor.ID)
  423. c.output("You drop %s.", object.Name)
  424. c.pemit(object.ID, "%s drops you.", c.actor.Name)
  425. c.oemit(inside, "%s drops %s.", c.actor.Name, object.Name)
  426. case MatchNone:
  427. c.output("You're not carrying that.")
  428. case MatchMany:
  429. c.output("I don't now which one.")
  430. }
  431. }
  432. func (c *ExecutionContext) enterCmd(message string) {
  433. roomID, found := c.db.GetParent(c.actor.ID)
  434. room, found := c.db.Fetch(roomID)
  435. if !found {
  436. return
  437. }
  438. object, matchType := room.MatchLinkNames(message, c.actor.ID).ExactlyOne()
  439. switch matchType {
  440. case MatchOne:
  441. if object.Type == "player" {
  442. c.output("Maybe you should seek consent prior to entering another player.")
  443. return
  444. }
  445. if object.Type == "exit" {
  446. c.output("This s not how exits are meant to be used.")
  447. return
  448. }
  449. if object.ID == c.actor.ID {
  450. c.output("Entering yourself would be a bad idea.")
  451. return
  452. }
  453. err := object.Contains(&c.actor)
  454. if err != nil {
  455. return
  456. }
  457. c.output("You climb into %s.", object.Name)
  458. c.oemit(room.ID, "%s climbs into %s.", c.actor.Name, object.Name)
  459. c.oemit(object.ID, "%s squeezes into %s with you.", c.actor.Name, object.Name)
  460. case MatchNone:
  461. c.output("I don't see that here.")
  462. case MatchMany:
  463. c.output("I don't now which one.")
  464. }
  465. }
  466. func (c *ExecutionContext) leaveCmd() {
  467. inside, _ := c.db.GetParent(c.actor.ID)
  468. object, found := c.db.Fetch(inside)
  469. if !found {
  470. return
  471. }
  472. objectInside, _ := c.db.GetParent(inside)
  473. if objectInside == 0 { // probably trying to 'leave' a room
  474. c.output("You can't leave here.")
  475. return
  476. }
  477. room, found := c.db.Fetch(objectInside)
  478. if !found {
  479. return
  480. }
  481. err := room.Contains(&c.actor)
  482. if err != nil {
  483. return
  484. }
  485. //c.actor.Refresh()
  486. c.output("You climb out of %s.", object.Name)
  487. c.oemit(object.ID, "%s climbs out of %s.", c.actor.Name, object.Name)
  488. c.oemit(room.ID, "%s climbs out of %s.", c.actor.Name, object.Name)
  489. }
  490. func (c *ExecutionContext) quitCmd(connectionID int) {
  491. c.output("So long, it's been good to know yah.")
  492. c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, messageType: EventTypeQuit, connectionID: connectionID}
  493. }
  494. func (c *ExecutionContext) whoCmd() {
  495. // onlinePlayers := c.eventDistributor.OnlinePlayers()
  496. c.output("Currently Online:\n")
  497. //for _, ref := range onlinePlayers {
  498. // c.output("%s\n", c.db.GetName(ref))
  499. //}
  500. }
  501. func (c *ExecutionContext) createCmd(message string) {
  502. roomID, found := c.db.GetParent(c.actor.ID)
  503. room, found := c.db.Fetch(roomID)
  504. if !found {
  505. return
  506. }
  507. o := c.factory.NewThing()
  508. o.Name = strings.TrimSpace(message)
  509. o.Owner = c.actor.ID
  510. o.Commit()
  511. err := room.Contains(&o)
  512. if err != nil {
  513. return
  514. }
  515. c.oemit(room.ID, "A %s appears out of the ether.", o.Name)
  516. c.output("%s Created.", o.DetailedName())
  517. }
  518. func (c *ExecutionContext) openCmd(input string) {
  519. // @open <in1;in2;in3;etc>=#<room>,<out1;out2;out3;etc>
  520. r, _ := regexp.Compile(`^@open\pZ+([^=]*[^=\pZ]+)\pZ*=#([0-9]+)(?:\pZ*,\pZ*([^,]*[^,\pZ]+)\pZ*)?`)
  521. params := r.FindStringSubmatch(input)
  522. if params == nil {
  523. return
  524. }
  525. inExit, roomIDStr, outExit := params[1], params[2], params[3]
  526. if len(inExit) == 0 || len(roomIDStr) == 0 {
  527. c.output("Bad command or file name.")
  528. return
  529. }
  530. targetID, _ := NewDBRefFromString(roomIDStr) // this will never fail, the regexp guarantees that
  531. targetRoom, found := c.db.Fetch(targetID)
  532. if !found {
  533. c.output("Target not found.")
  534. return
  535. }
  536. roomID, found := c.db.GetParent(c.actor.ID)
  537. room, found := c.db.Fetch(roomID)
  538. if !found {
  539. return
  540. }
  541. toExit := c.factory.NewExit(inExit, targetRoom.ID, c.actor.ID)
  542. toExit.Commit()
  543. err := room.Contains(&toExit)
  544. if err != nil {
  545. return
  546. }
  547. c.output("%s Created.", toExit.DetailedName())
  548. if len(outExit) > 0 {
  549. fromExit := c.factory.NewExit(outExit, room.ID, c.actor.ID)
  550. fromExit.Commit()
  551. err = targetRoom.Contains(&fromExit)
  552. if err != nil {
  553. return
  554. }
  555. c.output("%s Created.", fromExit.DetailedName())
  556. }
  557. }
  558. func (c *ExecutionContext) digCmd(input string) {
  559. // @dig <Room name>=<in1;in2;in3;etc>,<out1;out2;out3;etc>
  560. //@dig foo bar = <F>oo;foo;f,<B>ack;back;b
  561. r, _ := regexp.Compile(`^@dig\pZ+([^=]*[^=\pZ]+)(\pZ*=\pZ*(?:([^,]*[^,\pZ]+)\pZ*)?(?:\pZ*,\pZ*([^,]*[^,\pZ]+)\pZ*)?)?`)
  562. params := r.FindStringSubmatch(input)
  563. if params == nil {
  564. return
  565. }
  566. roomName, inExit, outExit := params[1], params[2], params[3]
  567. if len(roomName) == 0 {
  568. c.output("Rooms can't not have names.")
  569. return
  570. }
  571. newRoom := c.factory.NewRoom()
  572. newRoom.Name = roomName
  573. newRoom.Owner = c.actor.ID
  574. newRoom.Commit()
  575. c.output("%s Created.", newRoom.DetailedName())
  576. if len(inExit) > 0 || len(outExit) > 0 {
  577. roomID, found := c.db.GetParent(c.actor.ID)
  578. room, found := c.db.Fetch(roomID)
  579. if !found {
  580. return
  581. }
  582. if len(inExit) > 0 {
  583. toExit := c.factory.NewExit(inExit, newRoom.ID, c.actor.ID)
  584. toExit.Commit()
  585. err := room.Contains(&toExit)
  586. if err != nil {
  587. return
  588. }
  589. c.output("%s Created.", toExit.DetailedName())
  590. }
  591. if len(outExit) > 0 {
  592. fromExit := c.factory.NewExit(outExit, room.ID, c.actor.ID)
  593. fromExit.Commit()
  594. err := newRoom.Contains(&fromExit)
  595. if err != nil {
  596. return
  597. }
  598. c.output("%s Created.", fromExit.DetailedName())
  599. }
  600. }
  601. }
  602. func (c *ExecutionContext) nameCmd(input string) {
  603. r, _ := regexp.Compile(`^@name\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  604. params := r.FindStringSubmatch(input)
  605. if params == nil {
  606. return
  607. }
  608. objectName, name := params[1], params[2]
  609. roomID, found := c.db.GetParent(c.actor.ID)
  610. room, found := c.db.Fetch(roomID)
  611. if !found {
  612. return
  613. }
  614. candidate, matchType := room.MatchLinkNames(objectName, c.actor.ID).ExactlyOne()
  615. switch matchType {
  616. case MatchOne:
  617. candidate.Name = name
  618. candidate.Commit()
  619. c.output("Name set.")
  620. case MatchNone:
  621. c.output("I don't see that here.")
  622. case MatchMany:
  623. c.output("I don't now which one.")
  624. }
  625. }
  626. func (c *ExecutionContext) playerFlagCmd(input string, flag string) {
  627. r, _ := regexp.Compile(`^@`+flag+`\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  628. params := r.FindStringSubmatch(input)
  629. if params == nil {
  630. return
  631. }
  632. playerName, value := params[1], params[2]
  633. playerMeta,found := c.db.GetPlayer(playerName)
  634. if found {
  635. player, found := c.db.Fetch(playerMeta.ID)
  636. if !found {
  637. return
  638. }
  639. player.SetFlag(flag, value)
  640. player.Commit()
  641. c.output("It is so.")
  642. } else {
  643. c.output("I don't know who that is.")
  644. }
  645. }
  646. func (c *ExecutionContext) descCmd(input string) {
  647. r, _ := regexp.Compile(`^@desc\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  648. params := r.FindStringSubmatch(input)
  649. if params == nil {
  650. return
  651. }
  652. objectName, description := params[1], params[2]
  653. roomID, found := c.db.GetParent(c.actor.ID)
  654. room, found := c.db.Fetch(roomID)
  655. if !found {
  656. return
  657. }
  658. var editObject *Object
  659. switch objectName {
  660. case "here":
  661. editObject = &room
  662. case "me":
  663. editObject = &c.actor
  664. default:
  665. candidate, matchType := room.MatchLinkNames(objectName, c.actor.ID).ExactlyOne()
  666. switch matchType {
  667. case MatchOne:
  668. editObject = &candidate
  669. case MatchNone:
  670. c.output("I don't see that here.")
  671. return
  672. case MatchMany:
  673. c.output("I don't now which one.")
  674. return
  675. }
  676. }
  677. editObject.Description = description
  678. editObject.Commit()
  679. //c.actor.Refresh()
  680. c.output("Description set.")
  681. }
  682. func (c *ExecutionContext) telCmd(destStr string) {
  683. dest, err := NewDBRefFromHashRef(destStr)
  684. if err != nil {
  685. c.output("That doesn't look like a DBRef.")
  686. return
  687. }
  688. newRoom, found := c.db.Fetch(dest)
  689. if !found {
  690. c.output("That doesn't exist.")
  691. return
  692. }
  693. if newRoom.Type == "exit" {
  694. c.output("As fun is it sounds, you're not going to teleport into an exit.")
  695. return
  696. }
  697. if newRoom.Type == "player" {
  698. c.output("Teleporting into other players is very impolite.")
  699. return
  700. }
  701. if newRoom.ID == c.actor.ID {
  702. c.output("Teleporting to yourself is generally considered dangerous.")
  703. return
  704. }
  705. c.output("You feel an intense wooshing sensation.")
  706. err = newRoom.Contains(&c.actor)
  707. if err != nil {
  708. return
  709. }
  710. c.actor.Refresh()
  711. c.lookCmd("")
  712. }
  713. func (c *ExecutionContext) dumpCmd(refStr string) {
  714. ref, err := NewDBRefFromHashRef(refStr)
  715. if err != nil {
  716. c.output("That doesn't look like a DBRef.")
  717. return
  718. }
  719. obj, found := c.db.Fetch(ref)
  720. if !found {
  721. c.output("That doesn't exist.")
  722. return
  723. }
  724. c.output("%s", c.db.DumpObject(obj.ID))
  725. }
  726. func (c *ExecutionContext) destroyCmd(message string) {
  727. roomID, found := c.db.GetParent(c.actor.ID)
  728. room, found := c.db.Fetch(roomID)
  729. if !found {
  730. return
  731. }
  732. object, matchType := room.MatchLinkNames(message, c.actor.ID).ExactlyOne()
  733. switch matchType {
  734. case MatchOne:
  735. if object.ID == c.actor.ID {
  736. c.output("There are alternatives to suicide.")
  737. return
  738. }
  739. if object.Type == "player" {
  740. c.output("I didn't think homicide was your thing.")
  741. return
  742. }
  743. name := object.DetailedName()
  744. err := object.Delete()
  745. if err == nil {
  746. //c.actor.Refresh()
  747. c.output("%s vanishes into thin air.", name)
  748. }
  749. case MatchNone:
  750. c.output("I don't see that here.")
  751. case MatchMany:
  752. c.output("I don't know which one.")
  753. }
  754. }
  755. func (c *ExecutionContext) goCmd(dir string) bool {
  756. roomID, found := c.db.GetParent(c.actor.ID)
  757. room, found := c.db.Fetch(roomID)
  758. if !found {
  759. return false
  760. }
  761. exit, matchType := room.MatchExitNames(dir).ExactlyOne()
  762. switch matchType {
  763. case MatchOne:
  764. if exit.Next.Valid() {
  765. newRoom, found := c.db.Fetch(exit.Next)
  766. if !found {
  767. c.output("That exit appears to be broken!")
  768. return true
  769. }
  770. err := newRoom.Contains(&c.actor)
  771. if err != nil {
  772. return false
  773. }
  774. //c.actor.Refresh()
  775. c.output("You head towards %s.", newRoom.Name)
  776. c.oemit(room.ID, "%s leaves the room.", c.actor.Name)
  777. c.oemit(newRoom.ID, "%s enters the room.", c.actor.Name)
  778. return true
  779. }
  780. case MatchNone:
  781. return false
  782. case MatchMany:
  783. c.output("Ambiguous exit names are ambiguous.")
  784. return true
  785. }
  786. return false
  787. }
  788. func (c *ExecutionContext) wall(format string, a ...interface{}) {
  789. message := fmt.Sprintf(format, a...)
  790. c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeWall}
  791. }
  792. func (c *ExecutionContext) system(format string, a ...interface{}) {
  793. message := fmt.Sprintf(format, a...)
  794. c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeSystem}
  795. }
  796. func (c *ExecutionContext) oemit(audience DBRef, format string, a ...interface{}) {
  797. message := fmt.Sprintf(format, a...)
  798. c.outbound <- PlayerEvent{src: c.actor.ID, dst: audience, message: message, messageType: EventTypeOEmit}
  799. }
  800. func (c *ExecutionContext) output(format string, a ...interface{}) {
  801. message := fmt.Sprintf(format, a...)
  802. if !c.forceContext {
  803. c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, message: message, messageType: EventTypeOutput}
  804. }
  805. }
  806. func (c *ExecutionContext) pemit(target DBRef, format string, a ...interface{}) {
  807. message := fmt.Sprintf(format, a...)
  808. c.outbound <- PlayerEvent{src: c.actor.ID, dst: target, message: message, messageType: EventTypePEmit}
  809. }
  810. func (c *ExecutionContext) emit(audience DBRef, format string, a ...interface{}) {
  811. message := fmt.Sprintf(format, a...)
  812. c.outbound <- PlayerEvent{src: c.actor.ID, dst: audience, message: message, messageType: EventTypeEmit}
  813. }