Без опису

execution_context.go 27KB

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