暫無描述

execution_context.go 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  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 { return }
  486. err := container.Contains(&c.actor)
  487. if err != nil { return }
  488. c.output("You climb out of %s.", c.context.Name)
  489. c.oemit(c.context.ID, "%s climbs out of %s.", c.actor.Name, c.context.Name)
  490. c.oemit(container.ID, "%s climbs out of %s.", c.actor.Name, c.context.Name)
  491. }
  492. func (c *ExecutionContext) quitCmd(connectionID int) {
  493. c.output("So long, it's been good to know yah.")
  494. c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, messageType: EventTypeQuit, connectionID: connectionID}
  495. }
  496. func (c *ExecutionContext) whoCmd() {
  497. // onlinePlayers := c.eventDistributor.OnlinePlayers()
  498. c.output("Currently Online:\n")
  499. //for _, ref := range onlinePlayers {
  500. // c.output("%s\n", c.db.GetName(ref))
  501. //}
  502. }
  503. func (c *ExecutionContext) createCmd(message string) {
  504. o := c.factory.NewThing()
  505. o.Name = strings.TrimSpace(message)
  506. o.Owner = c.actor.ID
  507. o.Commit()
  508. err := c.actor.Contains(&o)
  509. if err != nil {
  510. return
  511. }
  512. c.output("%s Created.", o.DetailedName())
  513. }
  514. func (c *ExecutionContext) openCmd(input string) {
  515. // @open <in1;in2;in3;etc>=#<room>,<out1;out2;out3;etc>
  516. r, _ := regexp.Compile(`^@open\pZ+([^=]*[^=\pZ]+)\pZ*=#([0-9]+)(?:\pZ*,\pZ*([^,]*[^,\pZ]+)\pZ*)?`)
  517. params := r.FindStringSubmatch(input)
  518. if params == nil {
  519. return
  520. }
  521. inExit, roomIDStr, outExit := params[1], params[2], params[3]
  522. if len(inExit) == 0 || len(roomIDStr) == 0 {
  523. c.output("Bad command or file name.")
  524. return
  525. }
  526. targetID, _ := NewDBRefFromString(roomIDStr) // this will never fail, the regexp guarantees that
  527. targetRoom, found := c.db.Fetch(targetID)
  528. if !found {
  529. c.output("Target not found.")
  530. return
  531. }
  532. toExit := c.factory.NewExit(inExit, targetRoom.ID, c.actor.ID)
  533. toExit.Commit()
  534. err := c.context.Contains(&toExit)
  535. if err != nil {
  536. return
  537. }
  538. c.output("%s Created.", toExit.DetailedName())
  539. if len(outExit) > 0 {
  540. fromExit := c.factory.NewExit(outExit, c.context.ID, c.actor.ID)
  541. fromExit.Commit()
  542. err = targetRoom.Contains(&fromExit)
  543. if err != nil {
  544. return
  545. }
  546. c.output("%s Created.", fromExit.DetailedName())
  547. }
  548. }
  549. func (c *ExecutionContext) digCmd(input string) {
  550. // @dig <Room name>=<in1;in2;in3;etc>,<out1;out2;out3;etc>
  551. //@dig foo bar = <F>oo;foo;f,<B>ack;back;b
  552. r, _ := regexp.Compile(`^@dig\pZ+([^=]*[^=\pZ]+)(\pZ*=\pZ*(?:([^,]*[^,\pZ]+)\pZ*)?(?:\pZ*,\pZ*([^,]*[^,\pZ]+)\pZ*)?)?`)
  553. params := r.FindStringSubmatch(input)
  554. if params == nil {
  555. return
  556. }
  557. roomName, inExit, outExit := params[1], params[2], params[3]
  558. if len(roomName) == 0 {
  559. c.output("Rooms can't not have names.")
  560. return
  561. }
  562. newRoom := c.factory.NewRoom()
  563. newRoom.Name = roomName
  564. newRoom.Owner = c.actor.ID
  565. newRoom.Commit()
  566. c.output("%s Created.", newRoom.DetailedName())
  567. if len(inExit) > 0 || len(outExit) > 0 {
  568. if len(inExit) > 0 {
  569. toExit := c.factory.NewExit(inExit, newRoom.ID, c.actor.ID)
  570. toExit.Commit()
  571. err := c.context.Contains(&toExit)
  572. if err != nil {
  573. return
  574. }
  575. c.output("%s Created.", toExit.DetailedName())
  576. }
  577. if len(outExit) > 0 {
  578. fromExit := c.factory.NewExit(outExit, c.context.ID, c.actor.ID)
  579. fromExit.Commit()
  580. err := newRoom.Contains(&fromExit)
  581. if err != nil {
  582. return
  583. }
  584. c.output("%s Created.", fromExit.DetailedName())
  585. }
  586. }
  587. }
  588. func (c *ExecutionContext) nameCmd(input string) {
  589. r, _ := regexp.Compile(`^@name\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  590. params := r.FindStringSubmatch(input)
  591. if params == nil {
  592. return
  593. }
  594. searchName, newName := params[1], params[2]
  595. candidate, found := c.MatchFirst(c.context.ID, searchName, true, false)
  596. if !found {
  597. c.output("I don't see that here.")
  598. return
  599. }
  600. if candidate.Type == "player" {
  601. i := strings.IndexFunc(newName, func(c rune) bool {
  602. return unicode.IsSpace(c)
  603. })
  604. if i != -1 {
  605. c.output("Player names can't have spaces.")
  606. return
  607. }
  608. if c.actor.ID != candidate.ID && !c.actor.GetFlag("wizard") {
  609. c.output("Only wizards can rename other players.")
  610. return
  611. }
  612. err := c.db.RenamePlayer(candidate.Name, newName)
  613. if err != nil {
  614. c.output("I can't do that. Something has gone wrong.")
  615. return
  616. }
  617. }
  618. candidate.Name = newName
  619. candidate.Commit()
  620. c.output("Name set.")
  621. }
  622. func (c *ExecutionContext) setCmd(input string) {
  623. r, _ := regexp.Compile(`^@set\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(!)?(.*)\pZ*$`)
  624. params := r.FindStringSubmatch(input)
  625. if params == nil {
  626. return
  627. }
  628. objectName, value, flag := params[1], params[2]!="!", params[3]
  629. object, found := c.MatchFirst(c.context.ID, objectName, true, true)
  630. if (!found) {
  631. c.output("I don't know what that is")
  632. return
  633. }
  634. object.SetFlag(flag, value)
  635. object.Commit()
  636. c.output("It is so.")
  637. }
  638. func (c *ExecutionContext) descCmd(input string) {
  639. r, _ := regexp.Compile(`^@desc\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
  640. params := r.FindStringSubmatch(input)
  641. if params == nil {
  642. return
  643. }
  644. objectName, description := params[1], params[2]
  645. object, found := c.MatchFirst(c.actor.ID, objectName, false, false)
  646. if !found {
  647. c.output("I don't see that here.")
  648. return
  649. }
  650. object.Description = description
  651. object.Commit()
  652. c.output("Description set.")
  653. }
  654. func (c *ExecutionContext) telCmd(destStr string) {
  655. var dest Object
  656. var found bool
  657. dest, found = c.MatchFirst(c.context.ID, destStr, true, false)
  658. if !found {
  659. c.output("Invalid destination.")
  660. return
  661. }
  662. if dest.Type == "exit" {
  663. c.output("As fun is it sounds, you're not going to teleport into an exit.")
  664. return
  665. }
  666. if dest.Type == "player" {
  667. c.output("Teleporting into players is very impolite.")
  668. return
  669. }
  670. if dest.Owner != c.actor.ID && !dest.GetFlag("jump_ok") {
  671. c.output("You're not allowed to do that.")
  672. }
  673. c.output("You feel an intense wooshing sensation.")
  674. c.oemit(c.context.ID, "%s teleports out of the room.", c.actor.Name)
  675. c.oemit(dest.ID, "%s teleports in to the room.", c.actor.Name)
  676. err := dest.Contains(&c.actor)
  677. if err != nil {
  678. return
  679. }
  680. c.actor.Refresh()
  681. c.lookCmd("")
  682. }
  683. func (c *ExecutionContext) dumpCmd(refStr string) {
  684. object, found := c.MatchFirst(c.context.ID, refStr, true, true)
  685. if !found {
  686. c.output("I can't find that.")
  687. return
  688. }
  689. c.output("%s", c.db.DumpObject(object.ID))
  690. }
  691. func (c *ExecutionContext) destroyCmd(target string) {
  692. object, found := c.MatchFirst(c.actor.ID, target, true, false)
  693. if (!found) {
  694. c.output("I don't see that here.")
  695. return
  696. }
  697. if object.ID == c.actor.ID {
  698. c.output("There are alternatives to suicide.")
  699. return
  700. }
  701. if object.Type == "player" {
  702. c.output("I didn't think homicide was your thing.")
  703. return
  704. }
  705. name := object.DetailedName()
  706. err := object.Delete()
  707. if err == nil {
  708. c.output("%s vanishes into thin air.", name)
  709. }
  710. }
  711. func (c *ExecutionContext) goCmd(dir string) bool {
  712. exit, matchType := c.context.MatchExitNames(dir).ExactlyOne()
  713. switch matchType {
  714. case MatchOne:
  715. if exit.Next.Valid() {
  716. newRoom, found := c.db.Fetch(exit.Next)
  717. if !found {
  718. c.output("That exit appears to be broken!")
  719. return true
  720. }
  721. err := newRoom.Contains(&c.actor)
  722. if err != nil {
  723. return false
  724. }
  725. //c.actor.Refresh()
  726. c.output("You head towards %s.", newRoom.Name)
  727. c.oemit(c.context.ID, "%s leaves the room.", c.actor.Name)
  728. c.oemit(newRoom.ID, "%s enters the room.", c.actor.Name)
  729. return true
  730. }
  731. case MatchNone:
  732. return false
  733. case MatchMany:
  734. c.output("Ambiguous exit names are ambiguous.")
  735. return true
  736. }
  737. return false
  738. }
  739. func (c *ExecutionContext) wall(format string, a ...interface{}) {
  740. message := fmt.Sprintf(format, a...)
  741. c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeWall}
  742. }
  743. func (c *ExecutionContext) system(format string, a ...interface{}) {
  744. message := fmt.Sprintf(format, a...)
  745. c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeSystem}
  746. }
  747. func (c *ExecutionContext) oemit(audience DBRef, format string, a ...interface{}) {
  748. message := fmt.Sprintf(format, a...)
  749. c.outbound <- PlayerEvent{src: c.actor.ID, dst: audience, message: message, messageType: EventTypeOEmit}
  750. }
  751. func (c *ExecutionContext) output(format string, a ...interface{}) {
  752. message := fmt.Sprintf(format, a...)
  753. if !c.forceContext {
  754. c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, message: message, messageType: EventTypeOutput}
  755. }
  756. }
  757. func (c *ExecutionContext) pemit(target DBRef, format string, a ...interface{}) {
  758. message := fmt.Sprintf(format, a...)
  759. c.outbound <- PlayerEvent{src: c.actor.ID, dst: target, message: message, messageType: EventTypePEmit}
  760. }
  761. func (c *ExecutionContext) emit(audience DBRef, format string, a ...interface{}) {
  762. message := fmt.Sprintf(format, a...)
  763. c.outbound <- PlayerEvent{src: c.actor.ID, dst: audience, message: message, messageType: EventTypeEmit}
  764. }