Ingen beskrivning

execution_context.go 23KB

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