package funmow import ( "fmt" "regexp" "strings" "unicode" ) const ( MatchContext = 1 << iota MatchGlobalDBRef = 1 << iota MatchGlobalPlayer = 1 << iota MatchInventory = 1 << iota MatchRelative = 1 << iota MatchExitAliases = 1 << iota MatchExitType = 1 << iota MatchRoomType = 1 << iota MatchPlayerType = 1 << iota MatchThingType = 1 << iota MatchAnyType = MatchExitType | MatchRoomType | MatchPlayerType | MatchThingType MatchStuffTypes = MatchExitType | MatchRoomType | MatchThingType ) type ExecutionContext struct { actor Object context Object inbound chan PlayerEvent outbound chan PlayerEvent eventDistributor *EventDistributor db *DB factory *ObjectFactory forceContext bool } func NewForceContext(e *EventDistributor, w *DB, outbound chan PlayerEvent) *ExecutionContext { c := new(ExecutionContext) c.outbound = outbound c.eventDistributor = e c.db = w c.factory = NewObjectFactory(w) c.forceContext = true return c } func NewExecutionContext(actorID DBRef, e *EventDistributor, w *DB, outbound chan PlayerEvent) *ExecutionContext { c := new(ExecutionContext) actor, found := w.Fetch(actorID) if !found { return nil } c.actor = actor c.outbound = outbound c.eventDistributor = e c.db = w c.factory = NewObjectFactory(w) return c } func (c *ExecutionContext) StartInboundChannel() chan PlayerEvent { c.inbound = make(chan PlayerEvent) inboundBuffer := make(chan PlayerEvent) go func() { // An inbound event buffer to protect the event distributor from long // running tasks. The alternative is to run each inbound event in a // separate goroutine but that has potential problems (ie, two emits // arrive in order; the first one requires an extra DB lookup to sort // out context. Now the second one ends up sending its output event // before the first, and the client sees things backwards. queue := make([]*PlayerEvent, 0) running := true for running { if len(queue) == 0 { select { case newEvent, ok := <-c.inbound: if !ok { running = false break } queue = append(queue, &newEvent) } } else { event := queue[0] select { case newEvent, ok := <-c.inbound: if !ok { running = false break } queue = append(queue, &newEvent) case inboundBuffer <- *event: queue = queue[1:] } } } // closure of c.inbound is the signal from the event distributor that we need to die. // we then close inboundBuffer to tell the event goroutine to die close(inboundBuffer) }() go func() { for { event, ok := <-inboundBuffer if !ok { break } c.HandleEvent(event) } // mark player as offline if c.actor.Type == "player" { c.actor.SetFlag("online", false) c.actor.Commit() } // and finally we tell the event distributor to delete us. c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, messageType: EventTypeTeardownComplete} }() return c.inbound } func (c *ExecutionContext) HandleEvent(m PlayerEvent) { if m.messageType == EventTypeForce { found := false c.actor, found = c.db.Fetch(m.dst) contextID, found := c.db.GetParent(c.actor.ID) if found { c.context, _ = c.db.Fetch(contextID) } else { c.context, _ = c.db.Fetch(0) } if !found { return } c.evaluateCommand(m) return } c.actor.Refresh() contextID, found := c.db.GetParent(c.actor.ID) if found { c.context, _ = c.db.Fetch(contextID) } else { c.context, _ = c.db.Fetch(0) } switch m.messageType { case EventTypeLogin: if c.actor.Type == "player" { c.actor.SetFlag("online", true) c.actor.Commit() c.system("Yay! %s has connected! Oh boy!", c.actor.ColorName()) c.oemit(c.context.ID, "You hear a rustling as %s awakens from a slumber.", c.actor.ColorName()) } case EventTypeEmit: fallthrough case EventTypeOEmit: if m.dst == c.context.ID { c.output("%s", m.message) } case EventTypePEmit: c.output("%s", m.message) case EventTypeWall: speaker, found := c.db.Fetch(m.src) if !found { break } c.output("In the distance, you hear %s bellow out: \"%s\"", speaker.ColorName(), m.message) case EventTypeSystem: c.output(m.message) case EventTypePage: speaker, found := c.db.Fetch(m.src) if !found { break } c.output("%s pages: %s", speaker.ColorName(), m.message) case EventTypeSay: if m.dst == c.context.ID { speaker, found := c.db.Fetch(m.src) if !found { break } c.output("%s says \"%s\"", speaker.ColorName(), m.message) } case EventTypePose: if m.dst == c.context.ID { if m.src == c.actor.ID { c.output("%s %s", c.actor.ColorName(), m.message) } else { speaker, found := c.db.Fetch(m.src) if !found { break } c.output("%s %s", speaker.ColorName(), m.message) } } case EventTypeCommand: c.evaluateCommand(m) } } func (c *ExecutionContext) evaluateCommand(m PlayerEvent) { command := m.message switch { case command == "whoami": c.output(c.actor.Name) case command == "l": c.lookCmd("") case command == "look": c.lookCmd("") case strings.HasPrefix(command, "l "): // look at c.lookCmd(strings.TrimPrefix(command, "l ")) case strings.HasPrefix(command, "look "): // look at c.lookCmd(strings.TrimPrefix(command, "look ")) case strings.HasPrefix(command, "ex "): c.examineCmd(strings.TrimPrefix(command, "ex ")) case strings.HasPrefix(command, "examine "): c.examineCmd(strings.TrimPrefix(command, "examine ")) case strings.HasPrefix(command, "\""): c.sayCmd(strings.TrimPrefix(command, "\"")) case strings.HasPrefix(command, "say "): c.sayCmd(strings.TrimPrefix(command, "say ")) case strings.HasPrefix(command, ":"): c.poseCmd(strings.TrimPrefix(command, ":")) case strings.HasPrefix(command, "pose "): c.poseCmd(strings.TrimPrefix(command, "pose ")) case strings.HasPrefix(command, "p "): c.pageCmd(command) case strings.HasPrefix(command, "page "): c.pageCmd(command) case command == "i": c.inventoryCmd() case command == "inventory": c.inventoryCmd() case strings.HasPrefix(command, "get "): c.getCmd(strings.TrimPrefix(command, "get ")) case strings.HasPrefix(command, "drop "): c.dropCmd(strings.TrimPrefix(command, "drop ")) case strings.HasPrefix(command, "give "): c.giveCmd(command) case strings.HasPrefix(command, "put "): c.putCmd(command) case strings.HasPrefix(command, "enter "): c.enterCmd(strings.TrimPrefix(command, "enter ")) case command == "leave": c.leaveCmd() case command == "quit": c.quitCmd(m.connectionID) case command == "WHO": c.whoCmd() case strings.HasPrefix(command, "@create "): c.createCmd(strings.TrimPrefix(command, "@create ")) case strings.HasPrefix(command, "@dig "): c.digCmd(command) case strings.HasPrefix(command, "@open "): c.openCmd(command) case strings.HasPrefix(command, "@name "): c.nameCmd(command) case strings.HasPrefix(command, "@desc "): c.descCmd(command) case strings.HasPrefix(command, "@password "): c.passwordCmd(command) case strings.HasPrefix(command, "@set "): c.setCmd(command) case strings.HasPrefix(command, "@color "): c.colorCmd(command) case strings.HasPrefix(command, "@tel "): c.telCmd(strings.TrimPrefix(command, "@tel ")) case strings.HasPrefix(command, "@dump "): c.dumpCmd(strings.TrimPrefix(command, "@dump ")) case strings.HasPrefix(command, "@destroy "): c.destroyCmd(strings.TrimPrefix(command, "@destroy ")) case strings.HasPrefix(command, "@force "): c.forceCmd(command) default: if !c.goCmd(command) { c.output("What?\n") } } } func (c *ExecutionContext) lookCmd(target string) { if len(target) > 0 { object, found := c.MatchFirst(c.context.ID, target, MatchContext|MatchInventory|MatchRelative|MatchAnyType|MatchExitAliases) if !found { c.output("I don't see that here.") return } c.output("%s\n%s", object.DetailedName(), object.Description) } else { c.output("%s\n%s", c.context.DetailedName(), c.context.Description) contents := c.context.GetContents(c.actor.ID, false) if len(contents) > 0 { c.output("Contents:\n" + strings.Join(contents, "\n")) } exits := c.context.GetExits(true, false) if len(exits) > 0 { c.output("Obvious Exits:\n" + strings.Join(exits, " ")) } } } func (c *ExecutionContext) objectName(id DBRef) string { o, found := c.db.Fetch(id) if found { return o.DetailedName() } else { return fmt.Sprintf("MISSING OBJECT <#%d>", id) } } func (c *ExecutionContext) examineCmd(objectName string) { object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchInventory|MatchRelative|MatchAnyType|MatchExitAliases|MatchGlobalDBRef) if !found { c.output("I don't see that here.") return } containerID, _ := c.db.GetParent(object.ID) c.output("%s", object.DetailedName()) c.output("Type: %s", object.Type) c.output("@name: %s", object.Name) c.output("@desc: %s", object.Description) c.output("Next: %s", c.objectName(object.Next)) c.output("Owner: %s", c.objectName(object.Owner)) c.output("Location: %s", c.objectName(containerID)) contents := object.GetContents(0, true) if len(contents) > 0 { c.output("Contains:\n%s", strings.Join(contents, "\n")) } exits := object.GetExits(false, true) if len(exits) > 0 { c.output("Exits:\n%s", strings.Join(exits, "\n")) } flags := make([]string, 0, len(object.Flags)) for k, v := range object.Flags { if v { flags = append(flags, k) } } c.output("Flags: %s", strings.Join(flags, ", ")) properties := make([]string, 0, len(object.Properties)) for k, v := range object.Properties { properties = append(properties, fmt.Sprintf("@%s: %s", k, v)) } c.output("Properties:\n%s", strings.Join(properties, "\n")) } func (c *ExecutionContext) forceCmd(input string) { r, _ := regexp.Compile(`^@force\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } objectName, command := params[1], params[2] wizard := c.actor.GetFlag("wizard") object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchGlobalDBRef|MatchExitAliases|MatchInventory|MatchRelative|MatchStuffTypes) if !found { c.output("I don't see that here.") return } if object.Owner != c.actor.ID && !wizard { c.output("It's not nice to touch other people's things.") return } c.outbound <- PlayerEvent{src: c.actor.ID, dst: object.ID, message: command, messageType: EventTypeForce} } func (c *ExecutionContext) sayCmd(message string) { c.output(`You say "%s"`, message) c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.context.ID, message: message, messageType: EventTypeSay} } func (c *ExecutionContext) poseCmd(message string) { c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.context.ID, message: message, messageType: EventTypePose} } func (c *ExecutionContext) pageCmd(input string) { r, _ := regexp.Compile(`^p(?:age)*\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*[^\pZ]+)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } searchName, message := params[1], params[2] candidate, found := c.MatchPlayerName(searchName) if !found { c.output("I don't know who you're trying to page.") return } if !candidate.GetFlag("online") { c.output("That player appears to be offline.") return } c.output(`You paged %s with "%s".`, candidate.ColorName(), message) c.outbound <- PlayerEvent{src: c.actor.ID, dst: candidate.ID, message: message, messageType: EventTypePage} } func (c *ExecutionContext) inventoryCmd() { inventory := c.actor.GetContents(0, true) if len(inventory) > 0 { c.output("Inventory:\n %s", strings.Join(inventory, "\n ")) } else { c.output("You're not carrying anything.") } } func (c *ExecutionContext) getCmd(objectName string) { object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchGlobalDBRef|MatchAnyType) if !found { c.output("I don't see that here.") return } if object.Type == "exit" { c.output("You can't pick pick up exits.") return } if object.Type == "player" { c.output("That's really annoying. Don't be that person.") return } if object.ID == c.actor.ID { c.output("You can't pick yourself up.") return } err := c.actor.Contains(&object) if err != nil { return } //c.actor.Refresh() c.output("You pick up %s.", object.ColorName()) c.pemit(object.ID, "%s picked you up.", c.actor.ColorName()) c.oemit(c.context.ID, "%s picks up %s.", c.actor.ColorName(), object.ColorName()) } func (c *ExecutionContext) putCmd(input string) { c.xferCmd(input, "put", "into") } func (c *ExecutionContext) giveCmd(input string) { c.xferCmd(input, "give", "to") } func (c *ExecutionContext) xferCmd(input string, verb string, preposition string) { r, _ := regexp.Compile(`^` + verb + `\pZ+([^=]*[^=\pZ]{1})\pZ*` + preposition + `\pZ*(.*)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } objectName, receiverName := params[1], params[2] object, found := c.MatchFirst(c.actor.ID, objectName, MatchInventory|MatchThingType) if !found { c.output("You can't " + verb + " what you don't have.") return } receiver, found := c.MatchFirst(c.context.ID, receiverName, MatchContext|MatchThingType|MatchPlayerType) if !found { c.output("I cant find who or what you want to " + verb + " this " + preposition + ".") return } err := receiver.Contains(&object) if err != nil { return } c.output("You "+verb+" %s "+preposition+" %s.", object.ColorName(), receiver.ColorName()) c.pemit(object.ID, "%s "+verb+"s you "+preposition+" %s.", c.actor.ColorName(), receiver.ColorName()) if verb == "give" { c.pemit(receiver.ID, "%s gives you %s.", c.actor.ColorName(), object.ColorName()) } else { c.pemit(receiver.ID, "%s puts %s into you.", c.actor.ColorName(), object.ColorName()) } } func (c *ExecutionContext) MatchPlayerName(matchName string) (Object, bool) { playerMeta, foundMeta := c.db.GetPlayer(matchName) if foundMeta { o, found := c.db.Fetch(playerMeta.ID) if found { return o, true } } return Object{}, false } func (c *ExecutionContext) MatchFirst(context DBRef, matchName string, matchType int) (Object, bool) { wanted := map[string]bool{ "player": matchType&MatchPlayerType != 0, "exit": matchType&MatchExitType != 0, "room": matchType&MatchRoomType != 0, "thing": matchType&MatchThingType != 0, } if matchType&MatchRelative != 0 { if matchName == "here" { o, found := c.db.Fetch(context) if found && wanted[o.Type] { return o, true } } if matchName == "me" { if wanted[c.actor.Type] { return c.actor, true } } } ref, valid := NewDBRefFromHashRef(matchName) if valid { o, found := c.db.Fetch(ref) if found && wanted[o.Type] { container, _ := c.db.GetParent(o.ID) if container == context && matchType&MatchContext != 0 { return o, true } if container == c.actor.ID && matchType&MatchInventory != 0 { return o, true } if matchType&MatchGlobalDBRef != 0 { return o, true } } } if wanted["player"] && matchType&MatchGlobalPlayer != 0 { o, found := c.MatchPlayerName(matchName) if found { return o, true } } if matchType&MatchContext != 0 { for childID, _ := range c.db.GetChildren(context) { o, found := c.db.Fetch(childID) if found && wanted[o.Type] { if o.Type == "exit" && matchType&MatchExitAliases != 0 { aliases := strings.Split(o.Name, ";") for _, v := range aliases { if strings.EqualFold(v, matchName) { return o, true } } } lowerObjectName := strings.ToLower(o.Name) lowerMatchName := strings.ToLower(matchName) if strings.HasPrefix(lowerObjectName, lowerMatchName) { return o, true } } } } if matchType&MatchInventory != 0 { for childID, _ := range c.db.GetChildren(c.actor.ID) { o, found := c.db.Fetch(childID) if found && wanted[o.Type] { lowerObjectName := strings.ToLower(o.Name) lowerMatchName := strings.ToLower(matchName) if strings.HasPrefix(lowerObjectName, lowerMatchName) { return o, true } } } } return Object{}, false } func (c *ExecutionContext) dropCmd(objectName string) { object, found := c.MatchFirst(c.actor.ID, objectName, MatchInventory|MatchAnyType) if !found { c.output("You're not carrying that.") return } err := c.context.Contains(&object) if err != nil { return } c.output("You drop %s.", object.ColorName()) c.pemit(object.ID, "%s drops you.", c.actor.ColorName()) c.oemit(c.context.ID, "%s drops %s.", c.actor.ColorName(), object.ColorName()) } func (c *ExecutionContext) enterCmd(objectName string) { object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchThingType) if !found { c.output("I don't see that here.") return } if object.Type == "player" { c.output("Maybe you should seek consent prior to entering another player.") return } if object.Type == "exit" { c.output("This s not how exits are meant to be used.") return } if object.ID == c.actor.ID { c.output("Entering yourself would be a bad idea.") return } if !object.GetFlag("enter_ok") && !c.actor.GetFlag("wizard") { c.output("You're not allowed to do that.") return } err := object.Contains(&c.actor) if err != nil { return } c.output("You climb into %s.", object.ColorName()) c.oemit(c.context.ID, "%s climbs into %s.", c.actor.ColorName(), object.ColorName()) c.oemit(object.ID, "%s squeezes into %s with you.", c.actor.ColorName(), object.ColorName()) } func (c *ExecutionContext) leaveCmd() { outerObjectID, _ := c.db.GetParent(c.context.ID) if outerObjectID == 0 { // probably trying to 'leave' a room c.output("You can't leave here.") return } container, found := c.db.Fetch(outerObjectID) if !found { return } err := container.Contains(&c.actor) if err != nil { return } c.output("You climb out of %s.", c.context.ColorName()) c.oemit(c.context.ID, "%s climbs out of %s.", c.actor.ColorName(), c.context.ColorName()) c.oemit(container.ID, "%s climbs out of %s.", c.actor.ColorName(), c.context.ColorName()) } func (c *ExecutionContext) quitCmd(connectionID int) { c.output("So long, it's been good to know yah.") c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, messageType: EventTypeQuit, connectionID: connectionID} } func (c *ExecutionContext) whoCmd() { onlinePlayers := c.eventDistributor.OnlinePlayers() c.output("Currently Online:") for _, ref := range onlinePlayers { c.output("%s", c.objectName(ref)) } } func (c *ExecutionContext) createCmd(message string) { o := c.factory.NewThing() o.Name = strings.TrimSpace(message) o.Owner = c.actor.ID o.Commit() err := c.actor.Contains(&o) if err != nil { return } c.output("%s Created.", o.DetailedName()) } func (c *ExecutionContext) openCmd(input string) { // @open =#, r, _ := regexp.Compile(`^@open\pZ+([^=]*[^=\pZ]{1})\pZ*=#([0-9]+)(?:\pZ*,\pZ*([^,]*[^,\pZ]{1})\pZ*)?`) params := r.FindStringSubmatch(input) if params == nil { return } inExit, roomIDStr, outExit := params[1], params[2], params[3] if len(inExit) == 0 || len(roomIDStr) == 0 { c.output("Bad command or file name.") return } targetID, _ := NewDBRefFromString(roomIDStr) // this will never fail, the regexp guarantees that targetRoom, found := c.db.Fetch(targetID) if !found { c.output("Target not found.") return } toExit := c.factory.NewExit(inExit, targetRoom.ID, c.actor.ID) toExit.Commit() err := c.context.Contains(&toExit) if err != nil { return } c.output("%s Created.", toExit.DetailedName()) if len(outExit) > 0 { fromExit := c.factory.NewExit(outExit, c.context.ID, c.actor.ID) fromExit.Commit() err = targetRoom.Contains(&fromExit) if err != nil { return } c.output("%s Created.", fromExit.DetailedName()) } } func (c *ExecutionContext) digCmd(input string) { // @dig =, //@dig foo bar = oo;foo;f,ack;back;b r, _ := regexp.Compile(`^@dig\pZ+([^=]*[^=\pZ]{1})(?:\pZ*=\pZ*(?:([^,]*[^,\pZ]{1})\pZ*)?(?:\pZ*,\pZ*([^,]*[^,\pZ]{1})\pZ*)?)?`) params := r.FindStringSubmatch(input) if params == nil { return } roomName, inExit, outExit := params[1], params[2], params[3] if len(roomName) == 0 { c.output("Rooms can't not have names.") return } newRoom := c.factory.NewRoom() newRoom.Name = roomName newRoom.Owner = c.actor.ID newRoom.Commit() c.output("%s Created.", newRoom.DetailedName()) if len(inExit) > 0 || len(outExit) > 0 { if len(inExit) > 0 { toExit := c.factory.NewExit(inExit, newRoom.ID, c.actor.ID) toExit.Commit() err := c.context.Contains(&toExit) if err != nil { return } c.output("%s Created.", toExit.DetailedName()) } if len(outExit) > 0 { fromExit := c.factory.NewExit(outExit, c.context.ID, c.actor.ID) fromExit.Commit() err := newRoom.Contains(&fromExit) if err != nil { return } c.output("%s Created.", fromExit.DetailedName()) } } } func (c *ExecutionContext) passwordCmd(input string) { if c.actor.Type != "player" { c.output("Only players can have passwords.") return } r, _ := regexp.Compile(`^@password\pZ+([^\pZ]+)\pZ*=\pZ*([^\pZ]+)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } oldPassword, newPassword := params[1], params[2] if len(oldPassword) == 0 || len(newPassword) == 0 { c.output("Password can't be blank.") return } changed := c.db.SetPlayerPassword(c.actor.Name, oldPassword, newPassword) if changed { c.output("Password updated.") } else { c.output("Old password is wrong.") } } func (c *ExecutionContext) nameCmd(input string) { r, _ := regexp.Compile(`^@name\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } searchName, newName := params[1], params[2] candidate, found := c.MatchFirst(c.context.ID, searchName, MatchContext|MatchInventory|MatchRelative|MatchGlobalDBRef|MatchExitAliases|MatchAnyType) if !found { c.output("I don't see that here.") return } if candidate.Type == "player" { i := strings.IndexFunc(newName, func(c rune) bool { return unicode.IsSpace(c) }) if i != -1 { c.output("Player names can't have spaces.") return } if c.actor.ID != candidate.ID && !c.actor.GetFlag("wizard") { c.output("Only wizards can rename other players.") return } err := c.db.RenamePlayer(candidate.Name, newName) if err != nil { c.output("I can't do that. Something has gone wrong.") return } } candidate.Name = newName candidate.Commit() c.output("Name set.") } func (c *ExecutionContext) descCmd(input string) { r, _ := regexp.Compile(`^@desc\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } objectName, description := params[1], params[2] object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchInventory|MatchRelative|MatchGlobalDBRef|MatchExitAliases|MatchAnyType) if !found { c.output("I don't see that here.") return } object.Description = description object.Commit() c.output("Description set.") } func (c *ExecutionContext) setCmd(input string) { r, _ := regexp.Compile(`^@set\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(!)?(.*)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } objectName, value, flag := params[1], params[2] != "!", params[3] object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchInventory|MatchRelative|MatchGlobalDBRef|MatchExitAliases|MatchAnyType) if !found { c.output("I don't know what that is") return } if object.Owner != c.actor.ID && !c.actor.GetFlag("wizard") { c.output("You're not allowed to do that.") return } object.SetFlag(flag, value) object.Commit() c.output("It is so.") } func (c *ExecutionContext) colorCmd(input string) { r, _ := regexp.Compile(`^@color\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*([^\pZ]*)\pZ*$`) params := r.FindStringSubmatch(input) if params == nil { return } objectName, value := params[1], params[2] object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchInventory|MatchRelative|MatchGlobalDBRef|MatchExitAliases|MatchAnyType) if !found { c.output("I don't know what that is") return } if object.Owner != c.actor.ID && !c.actor.GetFlag("wizard") { c.output("You're not allowed to do that.") return } object.SetProp("color", value) object.Commit() c.output("It is so.") } func (c *ExecutionContext) telCmd(destStr string) { var dest Object var found bool dest, found = c.MatchFirst(c.context.ID, destStr, MatchGlobalDBRef|MatchRoomType) if !found { c.output("Invalid destination.") return } if dest.Owner != c.actor.ID && !dest.GetFlag("jump_ok") && !c.actor.GetFlag("wizard") { c.output("You're not allowed to do that.") return } c.output("You feel an intense wooshing sensation.") c.oemit(c.context.ID, "%s teleports out of the room.", c.actor.ColorName()) c.oemit(dest.ID, "%s teleports in to the room.", c.actor.ColorName()) err := dest.Contains(&c.actor) if err != nil { return } c.command("look") } func (c *ExecutionContext) dumpCmd(input string) { object, found := c.MatchFirst(c.context.ID, input, MatchContext|MatchInventory|MatchRelative|MatchGlobalDBRef|MatchGlobalPlayer|MatchExitAliases|MatchAnyType) if !found { c.output("I can't find that.") return } c.output("%s", c.db.DumpObject(object.ID)) } func (c *ExecutionContext) destroyCmd(target string) { object, found := c.MatchFirst(c.context.ID, target, MatchContext|MatchGlobalDBRef|MatchRelative|MatchExitAliases|MatchInventory|MatchStuffTypes) if !found { c.output("I don't see that here.") return } if object.ID == c.actor.ID { c.output("There are alternatives to suicide.") return } if object.Type == "player" { c.output("I didn't think homicide was your thing.") return } if object.Owner != c.actor.ID && !c.actor.GetFlag("wizard") { c.output("You're not allowed to do that.") return } name := object.DetailedName() err := object.Delete() if err == nil { c.output("%s vanishes into thin air.", name) } } func (c *ExecutionContext) goCmd(dir string) bool { exit, found := c.MatchFirst(c.context.ID, dir, MatchContext|MatchExitType|MatchExitAliases) if !found { return false } if !exit.Next.Valid() { return false } newRoom, found := c.db.Fetch(exit.Next) if !found { c.output("That exit appears to be broken!") return true } err := newRoom.Contains(&c.actor) if err != nil { return false } //c.actor.Refresh() c.output("You head towards %s.", newRoom.DetailedName()) c.oemit(c.context.ID, "%s leaves the room.", c.actor.ColorName()) c.oemit(newRoom.ID, "%s enters the room.", c.actor.ColorName()) c.command("look") return true } func (c *ExecutionContext) command(format string, a ...interface{}) { command := fmt.Sprintf(format, a...) c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, message: command, messageType: EventTypeCommand} } func (c *ExecutionContext) wall(format string, a ...interface{}) { message := fmt.Sprintf(format, a...) c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeWall} } func (c *ExecutionContext) system(format string, a ...interface{}) { message := fmt.Sprintf(format, a...) c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeSystem} } func (c *ExecutionContext) oemit(audience DBRef, format string, a ...interface{}) { message := fmt.Sprintf(format, a...) c.outbound <- PlayerEvent{src: c.actor.ID, dst: audience, message: message, messageType: EventTypeOEmit} } func (c *ExecutionContext) output(format string, a ...interface{}) { message := fmt.Sprintf(format, a...) if !c.forceContext { c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, message: message, messageType: EventTypeOutput} } } func (c *ExecutionContext) pemit(target DBRef, format string, a ...interface{}) { message := fmt.Sprintf(format, a...) c.outbound <- PlayerEvent{src: c.actor.ID, dst: target, message: message, messageType: EventTypePEmit} } func (c *ExecutionContext) emit(audience DBRef, format string, a ...interface{}) { message := fmt.Sprintf(format, a...) c.outbound <- PlayerEvent{src: c.actor.ID, dst: audience, message: message, messageType: EventTypeEmit} }