public override void Initialize() { Actor(); this.Response("who he is", (actor, npc, topic) => { Core.SendLocaleMessage(actor, "\"Name's Bjorn,\" <the0> gasps. \"You going to buy something, or..?\"", this); GlobalRules.ConsiderPerformRule("introduce self", this); return(PerformResult.Stop); }); Perform <MudObject, MudObject, MudObject>("topic response") .When((actor, npc, topic) => topic == null) .Do((actor, npc, topic) => { Core.SendLocaleMessage(actor, "\"Eh?\" <the0> asks.", this); return(PerformResult.Stop); }); Short = "Bjorn"; AddNoun("shopkeeper", "shop", "keeper", "keep"); AddNoun("bjorn", "b").When(a => GlobalRules.ConsiderValueRule <bool>("actor knows actor?", a, this)); this.Wear("overalls", ClothingLayer.Outer, ClothingBodyPart.Legs); this.Wear("simple white shirt", ClothingLayer.Outer, ClothingBodyPart.Torso); this.Wear("kufi", ClothingLayer.Outer, ClothingBodyPart.Head); Perform <MudObject, MudObject>("describe in locale").Do((actor, item) => { Core.SendMessage(actor, "The shopkeeper leans on the counter."); return(PerformResult.Continue); }); }
public override void Initialize() { Actor(); this.SetProperty("gender", Gender.Female); Long = "The most striking feature of this tree is not it's size - it's the face hacked crudely into the trunk. As you watch, it shifts, as if muttering to itself."; this.Response("who she is", (actor, npc, topic) => { Core.SendLocaleMessage(actor, "The breeze whistles between the trees. It almost sounds like words.... \"...Forest...\", the wind says.", this); GlobalRules.ConsiderPerformRule("introduce self", this); return(PerformResult.Stop); }); Perform <MudObject, MudObject, MudObject>("topic response") .When((actor, npc, topic) => topic == null) .Do((actor, npc, topic) => { Core.SendLocaleMessage(actor, "The wind whistles.", this); return(PerformResult.Stop); }); Short = "Mother Tree"; AddNoun("mother", "tree", "oak", "forest"); Perform <MudObject, MudObject>("describe in locale").Do((actor, item) => { Core.SendMessage(actor, "The wind whistles through the leaves."); return(PerformResult.Continue); }); }
/// <summary> /// Process commands in a single thread, until there are no more queued commands. /// Heartbeat is called between every command. /// </summary> public static void ProcessCommands() { if ((Core.Flags & StartupFlags.SingleThreaded) != StartupFlags.SingleThreaded) { throw new InvalidOperationException("ProcessCommands should only be called in single threaded mode."); } while (PendingCommands.Count > 0 && !ShuttingDown) { GlobalRules.ConsiderPerformRule("heartbeat"); Core.SendPendingMessages(); PendingCommand PendingCommand = null; try { PendingCommand = PendingCommands.FirstOrDefault(); if (PendingCommand != null) { PendingCommands.Remove(PendingCommand); } } catch (Exception e) { LogCommandError(e); PendingCommand = null; } if (PendingCommand != null) { NextCommand = PendingCommand; //Reset flags that the last command may have changed CommandTimeoutEnabled = true; SilentFlag = false; GlobalRules.LogRules(null); try { var handler = NextCommand.Actor.GetProperty <ClientCommandHandler>("command handler"); if (handler != null) { handler.HandleCommand(NextCommand); } } catch (Exception e) { LogCommandError(e); Core.DiscardPendingMessages(); } if (PendingCommand.ProcessingCompleteCallback != null) { PendingCommand.ProcessingCompleteCallback(); } } } }
private void UpdateGlobalRules() { var newRules = GetAllGlobalCSRules(); if (newRules?.Count > 0) { GlobalCSRules.Clear(); GlobalCSRules = newRules; GlobalRules.UpdateGlobalRules(); } }
/// <summary> /// Run update rule on all objects that have been marked. /// </summary> public static void UpdateMarkedObjects() { // Updating an object may mark further objects for update. Avoid an infinite loop. var startCount = MarkedObjects.Count; for (int i = 0; i < startCount; ++i) { GlobalRules.ConsiderPerformRule("update", MarkedObjects[i]); } MarkedObjects.RemoveRange(0, startCount); }
/// <summary> /// Process the Heartbeat. It is assumed that this function is called periodically by the command processing loop. /// When called, this function will invoke the "heartbeat" rulebook if enough time has passed since the last /// invokation. /// </summary> internal static void Heartbeat() { var now = DateTime.Now; var timeSinceLastBeat = now - TimeOfLastHeartbeat; if (timeSinceLastBeat.TotalMilliseconds >= SettingsObject.HeartbeatInterval) { Core.TimeOfDay += Core.SettingsObject.ClockAdvanceRate; TimeOfLastHeartbeat = now; CurrentHeartbeat += 1; var heartbeatObjects = new HashSet <MudObject>(); foreach (var client in Clients.ConnectedClients) { foreach (var visibleObject in Core.EnumerateVisibleTree(Core.FindLocale(client.Player))) { heartbeatObjects.Add(visibleObject); } } foreach (var heartbeatObject in heartbeatObjects) { HeartbeatSet.Add(heartbeatObject); heartbeatObject.CurrentHeartbeat = CurrentHeartbeat; } HeartbeatSet.RemoveWhere(o => o.CurrentHeartbeat < (CurrentHeartbeat - Core.SettingsObject.LiveHeartbeats)); foreach (var heartbeatObject in HeartbeatSet) { GlobalRules.ConsiderPerformRule("heartbeat", heartbeatObject); } //In case heartbeat rules emitted messages. Core.SendPendingMessages(); } for (var i = 0; i < ActiveTimers.Count;) { var timerFireTime = ActiveTimers[i].StartTime + ActiveTimers[i].Interval; if (timerFireTime <= now) { ActiveTimers[i].Action(); SendPendingMessages(); ActiveTimers.RemoveAt(i); } else { ++i; } } }
/// <summary> /// Actually carryout the command, following all of it's rules, including the before and after command rules. /// </summary> /// <param name="Command"></param> /// <param name="Match"></param> /// <param name="Actor"></param> /// <returns>The result of the command's procedural rules.</returns> private static PerformResult ExecuteCommand(CommandEntry Command, PossibleMatch Match, MudObject Actor) { var result = PerformResult.Stop; Match.Upsert("COMMAND", Command); if (GlobalRules.ConsiderMatchBasedPerformRule("before command", Match, Actor) == PerformResult.Continue) { result = Command.ProceduralRules.Consider(Match, Actor); GlobalRules.ConsiderMatchBasedPerformRule("after command", Match, Actor); } GlobalRules.ConsiderPerformRule("after every command", Actor); return(result); }
public void AddRule(BestPracticeRule rule, bool global = false) { rule.ID = GetUniqueId(rule.ID); if (global) { GlobalRules.Add(rule); } else { LocalRules.Add(rule); } DoCollectionChanged(NotifyCollectionChangedAction.Add, rule); }
/// <summary> /// Process the Heartbeat. It is assumed that this function is called periodically by the command processing loop. /// When called, this function will invoke the "heartbeat" rulebook if enough time has passed since the last /// invokation. /// </summary> internal static void Heartbeat() { var now = DateTime.Now; var timeSinceLastBeat = now - TimeOfLastHeartbeat; if (timeSinceLastBeat.TotalMilliseconds >= SettingsObject.HeartbeatInterval) { TimeOfLastHeartbeat = now; GlobalRules.ConsiderPerformRule("heartbeat"); //In case heartbeat rules emitted messages. Core.SendPendingMessages(); } }
public string GetUniqueId(string id) { int suffix = 1; var testId = id; while ( GlobalRules.Contains(testId) || LocalRules.Contains(testId) ) { suffix++; testId = id + "_" + suffix; } return(testId); }
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (var item in e.OldItems) { //if (!((IGlobalCSSheet)item).FixStyle && ((IGlobalCSSheet)item).Component != null && !StyleSheetIsNeeded(((IGlobalCSSheet)item).Component)) //{ // GlobalRulesSheets.Remove(GlobalRulesSheets.First(x => x.Component?.GetType() == ((IGlobalCSSheet)item).Component.GetType())); // RemoveOneStyleSheet((IGlobalCSSheet)item); // GlobalRules.UpdateGlobalRules(); //} //else if (!((IGlobalCSSheet)item).FixStyle && ((IGlobalCSSheet)item).Component != null && ((IGlobalCSSheet)item).IsGlobal) //{ // GlobalCSSheets.First(x => x.Component?.GetType() == ((IGlobalCSSheet)item).Component.GetType()).IsGlobal = true; //} if (!((IGlobalCSSheet)item).FixStyle && ((IGlobalCSSheet)item).ComponentType != null && !StyleSheetIsNeeded(((IGlobalCSSheet)item).ComponentType)) { GlobalRulesSheets.Remove(GlobalRulesSheets.First(x => x.ComponentType == ((IGlobalCSSheet)item).ComponentType)); RemoveOneStyleSheet((IGlobalCSSheet)item); GlobalRules?.UpdateGlobalRules(); //Debug.WriteLine($"Removed StyleSheet for {((IGlobalCSSheet)item).ComponentType}"); } else if (!((IGlobalCSSheet)item).FixStyle && ((IGlobalCSSheet)item).ComponentType != null && ((IGlobalCSSheet)item).IsGlobal) { GlobalCSSheets.First(x => x.ComponentType == ((IGlobalCSSheet)item).ComponentType).IsGlobal = true; } } } if (e.NewItems != null) { foreach (var item in e.NewItems) { if (!ComponentStyleExist(((IGlobalCSSheet)item).ComponentType)) { //Debug.WriteLine($"Added StyleSheet for {((IGlobalCSSheet)item).ComponentType}"); GlobalRulesSheets.Add((IGlobalCSSheet)item); ((IGlobalCSSheet)item).IsGlobal = true; AddOneStyleSheet((IGlobalCSSheet)item); GlobalRules?.UpdateGlobalRules(); } } } }
public override void Initialize() { Actor(); this.Response("who he is", (actor, npc, topic) => { Core.SendLocaleMessage(actor, "\"Jaygmunder,\" <the0> gasps. \"You can call me Jay though.\"", this); GlobalRules.ConsiderPerformRule("introduce self", this); return(PerformResult.Stop); }); this.Response("his mechanical suit", "\"Huh, my suit? Whhat about it? When you get old, you'll need some help getting around too.\" <the0> sighs. \"Not that it does me much good now. I have't got any plasma for it.\""); this.Response("his plush chair", "\"Yeah it's pretty nice.\" <the0> pauses to stroke the arm of his chair. \"I'd let you sit in it if I could get up. Need plasma for that, though.\""); this.Response("plasma", (actor, nps, topic) => { Core.SendLocaleMessage(actor, "\"The red stuff? In the bags? You know what plasma is, don't you?\""); var quest = Core.GetObject("Homestead.PlasmaQuest"); if (GlobalRules.ConsiderValueRule <bool>("quest available?", actor, quest)) { Core.SendMessage(actor, "\"Think you can grab some for me?\" <the0> asks.", this); this.OfferQuest(actor, quest); } return(PerformResult.Stop); }); Perform <MudObject, MudObject, MudObject>("topic response") .When((actor, npc, topic) => topic == null) .Do((actor, npc, topic) => { Core.SendLocaleMessage(actor, "\"Wut?\" <the0> asks.", this); return(PerformResult.Stop); }); Short = "Jaygmundre"; AddNoun("jaygmundre", "jay", "j").When(a => GlobalRules.ConsiderValueRule <bool>("actor knows actor?", a, this)); this.Wear("mechanical suit", ClothingLayer.Outer, ClothingBodyPart.Torso); }
public override void Initialize() { Actor(); this.Response("who he is", (actor, npc, topic) => { SendLocaleMessage(actor, "\"I am Soranus,\" <the0> says.", this); GlobalRules.ConsiderPerformRule("introduce self", this); return(PerformResult.Stop); }); this.Response("the entrails", "\"These things?\" <the0> asks. \"Nothing special. They're for the wolves.\""); this.Response("wolves", (actor, npc, topic) => { SendLocaleMessage(actor, "^<the0> grins, expossing a pair of wicked yellow canines. \"Oh don't worry, they aren't here now.\"", this); var quest = GetObject("palantine/entrail_quest"); if (GlobalRules.ConsiderValueRule <bool>("quest available?", actor, quest)) { SendMessage(actor, "\"Would you mind feeding them for me?\" <the0> asks.", this); this.OfferQuest(actor, quest); } return(PerformResult.Stop); }); Perform <MudObject, MudObject, MudObject>("topic response") .When((actor, npc, topic) => topic == null) .Do((actor, npc, topic) => { SendLocaleMessage(actor, "\"This is my default response,\" <the0> says, showing his sharp little teeth.", this); return(PerformResult.Stop); }); Short = "Soranus"; AddNoun("soranus").When(a => GlobalRules.ConsiderValueRule <bool>("actor knows actor?", a, this)); this.Wear("toga", ClothingLayer.Outer, ClothingBodyPart.Torso); this.Wear(GetObject("palantine/entrails")); }
/// <summary> /// Format a list of mud objects using commas and a coordinating conjunction. EG, into the form 'a, b, and c'. /// </summary> /// <param name="Recipient">The actor that will, eventually, see the output.</param> /// <param name="ListObject">Either a MudObject or a List<MudObject>. The actual items to format.</param> /// <param name="FormattedMessage">Append the formatted message to this StringBuilder.</param> /// <param name="CoordinatingConjunction">The word that separates the final item of a list from those proceeding it. EG, and, or, nor.</param> private static void FormatList( MudObject Recipient, Object ListObject, StringBuilder FormattedMessage, String CoordinatingConjunction) { // ListObject can be a MudObject or a List<MudObject>. The algorithm expects a list, so transform it. List <MudObject> list = null; if (ListObject is MudObject) { list = new List <MudObject>(); list.Add(ListObject as MudObject); } else { list = ListObject as List <MudObject>; } // If ListObject was neither a MudObject nor a List<MudObject>... if (list == null) { return; } for (int x = 0; x < list.Count; ++x) { FormattedMessage.Append(GlobalRules.ConsiderValueRule <String>("printed name", Recipient, list[x], list[x].GetProperty <String>("article"))); if (x != list.Count - 1) { FormattedMessage.Append(", "); } if (x == list.Count - 2 && !String.IsNullOrEmpty(CoordinatingConjunction)) { FormattedMessage.Append(CoordinatingConjunction + " "); } } }
public void UpdateLighting() { Light = LightingLevel.Dark; if (RoomType == RMUD.RoomType.Exterior) { Light = AmbientExteriorLightingLevel; } foreach (var item in MudObject.EnumerateVisibleTree(this)) { var lightingLevel = GlobalRules.ConsiderValueRule <LightingLevel>("light level", item); if (lightingLevel > Light) { Light = lightingLevel; } } if (AmbientLighting > Light) { Light = AmbientLighting; } }
private static void ProcessThreadedCommands() { if ((Core.Flags & StartupFlags.SingleThreaded) == StartupFlags.SingleThreaded) { throw new InvalidOperationException("ProcessThreadedCommands should never be called in single threaded mode."); } IndividualCommandThread = new Thread(ProcessCommandsWorkerThread); IndividualCommandThread.Start(); while (!ShuttingDown) { System.Threading.Thread.Sleep(10); DatabaseLock.WaitOne(); Heartbeat(); DatabaseLock.ReleaseMutex(); while (PendingCommands.Count > 0 && !ShuttingDown) { PendingCommand PendingCommand = null; PendingCommandLock.WaitOne(); try { PendingCommand = PendingCommands.FirstOrDefault(pc => { return(true); //if (pc.Actor.ConnectedClient == null) return true; //else return (DateTime.Now - pc.Actor.ConnectedClient.TimeOfLastCommand).TotalMilliseconds > SettingsObject.AllowedCommandRate; }); if (PendingCommand != null) { PendingCommands.Remove(PendingCommand); } } catch (Exception e) { LogCommandError(e); PendingCommand = null; } PendingCommandLock.ReleaseMutex(); if (PendingCommand != null) { DatabaseLock.WaitOne(); NextCommand = PendingCommand; //Reset flags that the last command may have changed CommandTimeoutEnabled = true; SilentFlag = false; GlobalRules.LogRules(null); CommandReadyHandle.Set(); //Signal worker thread to proceed. if (!CommandFinishedHandle.WaitOne(SettingsObject.CommandTimeOut)) { if (!CommandTimeoutEnabled) //Timeout is disabled, go ahead and wait for infinity. { CommandFinishedHandle.WaitOne(); } else { //Kill the command processor thread. IndividualCommandThread.Abort(); ClearPendingMessages(); if (PendingCommand.Actor.ConnectedClient != null) { PendingCommand.Actor.ConnectedClient.Send("Command timeout.\r\n"); LogError(String.Format("Command timeout. {0} - {1}", PendingCommand.Actor.ConnectedClient.ConnectionDescription, PendingCommand.RawCommand)); } else { LogError(String.Format("Command timeout [No client] - {1}", PendingCommand.RawCommand)); } IndividualCommandThread = new Thread(ProcessCommandsWorkerThread); IndividualCommandThread.Start(); } } if (PendingCommand.ProcessingCompleteCallback != null) { PendingCommand.ProcessingCompleteCallback(); } DatabaseLock.ReleaseMutex(); } } } IndividualCommandThread.Abort(); if (Core.OnShutDown != null) { Core.OnShutDown(); } }
/// <summary> /// Parse and format a message intended for a specific recipient. /// </summary> /// <param name="Recipient"></param> /// <param name="Message">The message, possibly containing specifiers.</param> /// <param name="Objects">Specifier indicies refer to this list of objects.</param> /// <returns></returns> public static String FormatMessage(MudObject Recipient, String Message, params Object[] Objects) { //A leading @ indicates that the message should be interpretted as an entry in the global message table. if (Message[0] == '@') { Message = Core.GetMessage(Message.Substring(1)); } var formattedMessage = new StringBuilder(); var iterator = new StringIterator(Message); while (!iterator.AtEnd) { if (iterator.Next == '<') //We have located a specifier. { var type = ""; var index = 0; if (MessageFormatParser.ReadSpecifier(iterator, out type, out index)) { if (index < 0 || index >= Objects.Length) { continue; //A blank in the output is preferable to crashing. } #region Expand Specifier if (type == "the" && Objects[index] is MudObject) { //'the' overrides the object's article property. formattedMessage.Append(GlobalRules.ConsiderValueRule <String>("printed name", Recipient, Objects[index], "the")); } else if (type == "a" && Objects[index] is MudObject) { formattedMessage.Append(GlobalRules.ConsiderValueRule <String>("printed name", Recipient, Objects[index], (Objects[index] as MudObject).GetProperty <String>("article"))); } else if (type == "l") //No connective clause is used for this style of list. eg 1, 2, 3. { FormatList(Recipient, Objects[index], formattedMessage, ""); } else if (type == "lor") //Use or. eg 1, 2, or 3. { FormatList(Recipient, Objects[index], formattedMessage, "or"); } else if (type == "land") //Use and. eg 1, 2, and 3. { FormatList(Recipient, Objects[index], formattedMessage, "and"); } else if (type == "lnor") //Use nor. eg 1, 2, nor 3. { FormatList(Recipient, Objects[index], formattedMessage, "nor"); } else if (type == "s") { if (Objects[index] == null) { formattedMessage.Append("%NULL%"); } else { formattedMessage.Append(Objects[index].ToString()); } } #endregion } } else { formattedMessage.Append(iterator.Next); iterator.Advance(); } } Message = formattedMessage.ToString(); formattedMessage.Clear(); iterator = new StringIterator(Message); //Apply the ^ transform: Capitalize the letter following the ^ and remove the ^. while (!iterator.AtEnd) { if (iterator.Next == '^') { iterator.Advance(); if (iterator.AtEnd) { break; } formattedMessage.Append(new String(iterator.Next, 1).ToUpper()); iterator.Advance(); } else { formattedMessage.Append(iterator.Next); iterator.Advance(); } } return(formattedMessage.ToString()); }