Aucune description

execution_context.go 28KB

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