The MUD Development forum.
Game Development Discussion, Chat, Technical Debate, and people who just love games talking about building worlds.

Home » Hosted Mud Projects » Wheelmud » General » More questions on Prompt - WheelMUD - A C# MUD Server - Forums - WheelMUD - Design
More questions on Prompt - WheelMUD - A C# MUD Server - Forums - WheelMUD - Design [message #455] Sun, 22 March 2015 11:43
Karak is currently offline  Karak
Messages: 489
Registered: March 2015
Location: Seattle, WA
Senior Member
Wheelmud
This thread originates from prior WheelMUD forum software archives. It has been migrated here to preserve the contents.

Post by Fastalanasa (09 May 2012 11:20 AM):
Quote:
JFed wrote:
I'm still pretty new to C# and haven't worked on a project this size before, so I suspect this question may be a trivial one. I think I just need a bump in the right direction.

Only trivial questions are the unasked ones. ;)
Quote:
JFed wrote:
What I "want" to do is put a statement like "string prompt = new PlayerAdapter(this.Thing).GetRenderedPrompt();" in session.cs. However, this would require a reference and breaks loose coupling, right? Do I need to create an interface, or use a mediator pattern?

I haven't looked at the code, so I don't know the answer to this question.
Quote:
JFed wrote:
The fact that prompt.cs is living in WRM bothers me as well. This is a game action that will be shared across all rulesets, so my gut tells me that another layer of abstraction may be required.

The reasoning for this, is that anything dependent on specific gaming system's properties and attributes, should be housed with said system. That's why I put the Spar command where it is.
Quote:
JFed wrote:
I hope this makes sense. Having a great time with you guys on the Wheel!

Yes, it makes sense. Glad you are having fun!

Post by Karak (10 May 2012 01:06 AM):
Hmm, first I don't see where JFed asked these questions. Maybe I'm being blind. Which thread was that?
Quote:
JFed wrote:
What I "want" to do is put a statement like "string prompt = new PlayerAdapter(this.Thing).GetRenderedPrompt();" in session.cs. However, this would require a reference and breaks loose coupling, right? Do I need to create an interface, or use a mediator pattern?


What I would do would be to get rid of the SessionState's "public string Prompt" property, and replace it with a "public Func[string] P<string>romptPrinter" property. This property would be assigned a default printing func in SessionState's constructor like:
this.PromptPrinter = (Wink =&gt; { return "&gt; "; };</string>
<string>The idea is to make SessionState just call this.PromptPrinter(Wink, and it doesn't care what assembly that logic came from or resides in, it just knows that it will either print the default or print "something else" that the user provided to it (regardless of what assembly that logic happens to come from).

Next we add a WRMPlayerBehavior (we'd need something like one eventually anyway) to house player options which apply just </string>to the WRM system, including their chosen Prompt settings. (Honestly I wouldn't mind if such a behavior also housed properties that did some of the same duties as the PlayerAdapter - maybe PlayerAdapter wouldn't be needed if the game coders had a one stop shop for their player-related stuff in their game's version of the PlayerBehavior.Wink So anyway, WRMPlayerBehavior gains a "public string Prompt" property which would be what the player set as their Prompt like "%HP%&gt; " or whatnot. The <i>setter</i> for this property would do some work: If the value is changing from null (which should automatically include when it is set during a constructor by being loaded from the DB, or when being set for the first time by a new user), find the SessionState and do something like:
<string> PlayerAdapter playerAdapter = ...;
sessionState.PromptPrinter = (Wink =&gt; { this.PrintPrompt(playerAdapter); };</string>

Post by Karak (10 May 2012 01:26 AM):
Quote:
JFed wrote:
The fact that prompt.cs is living in WRM bothers me as well.


Ok yeah now that you mention it (I should have read the rest before forming that last reply)... SessionState could still house the Prompt property and core could still house the Prompt action, if we modify my response to have the PromptPrinter work like this:
public Func[string, string] PromptPrinter {get;set;}
...
this.PromptPrinter(this.Prompt);

So some game-specific text is still housed in the Prompt property like "%FATE%&gt; " but the WRMPlayerBehavior would still assign it's own prompt printer to the SessionState upon loading, so the WRM layer would still handle the <i>interpretation</i> of that game-specific data and return the evaluated text.

Post by Karak (10 May 2012 01:35 AM):
Another slight addendum: when a behavior is Constructed it isn't attached to a Thing (player) yet so finding the associated SessionState probably isn't possible. Instead, we can override OnAddBehavior of a new WRMPlayerBehavior to rig up the custom prompt the moment it's being attached to a Thing.
LMK if this helped. Or maybe I was unintelligible, having just had a bit to drink, LOL.

Post by JFed (10 May 2012 06:58 AM):
Good morning! Thanks for the replies Karak. I'm still 'digesting' what you said (only breakfast I've had so far! Razz Wink. You got me thinking though... would adding a virtual function (string)GetRenderedPrompt(Wink to PlayerBehavior that simply displays the raw prompt and creating WRMPlayerBehavior which inherits from and overrides this function work? I haven't been drinking but haven't had my second cup of coffee yet so I'm still pretty impaired myself! Cool
<div> [/quote]
<div>Oh, and you didn't miss anything. I sent Fast a private message at first which included my question -- I thought I may be missing something obvious and didn't want to appear overly ignorant publicly. I'm used to being a lurker on forums. Embarrassed [/quote]

Post by Karak (11 May 2012 12:57 AM):
No, the hypothetical WRMPlayerBehavior is "a particular type of Behavior" but not really "a particular type of SessionState" so should inherit from Behavior. We should reserve inheritence for only the most direct of derivations (and C# intentionally only allows one concrete class inheritence to help enforce this practice).

Here's another way to think about my proposal.

SessionStates are used to define a very global state that we're in. How our input is handled is very much related to that State. When we first connect, we will be in the ConnectedState. Depending on our input, we may get to LoginState or (Character)CreationState. Depending on our inputs there (IE finish creating a character or inputting the right name+password pair), we should then get to the PlayingState. Finally, we're in a state where most of our input is routed through the Actions system, being interpreted as in-game commands.

Because each of these *States handles input for different purposes, how they handle Prompting for that input can vary, so each of these states <i>ought to</i> be able to define a custom prompt. Currently they call Session.SetPrompt (and actually, new addendum, probably SESSION is the correct place to have a PromptPrinter property, anyway...Wink In most cases, these could be somewhat hard-coded, but in the case of PlayingState, we can know little ahead of time about what will be desired because it is game specific. Nothing in core is allowed to reference the game; the game is built-on/references core, rather than the other way around. As you suggested, polymorphism is one way to tackle this sort of scenario but it's not quite a good fit here. What I was suggesting is to make prompt-printing logic redirectable at runtime. When the player logs in to a WRM game, something automatically tells the Session "this is what I want you to use to print my prompt; thanks!" This suggests a Func, Action, or once established, an actual typed Delegate.

Anyway, if you're still unclear, let me know if you want me to do part of this. It's certainly turned out to be a bit more involved than initially thought. It'd pretty much leave the reflection parts to you. Smile

Post by Fastalanasa (11 May 2012 09:25 AM):
I gotta say this has been a most enlightening thread I've read in a while. The gist of having actions in the core while still being able to access game-specific attributes and/or logic got me very excited.

Post by JFed (13 May 2012 06:24 AM):
Quote:


<span style="text-align: left; color: rgb(150, 145, 147); font-family: Arial, Helvetica, sans-serif; font-size: 11px; background-color: rgb(245, 245, 245);">Posted By Karak on 11 May 2012 12:57 AM</span><br style="text-align: left; color: rgb(150, 145, 147); font-family: Arial, Helvetica, sans-serif; font-size: 11px; background-color: rgb(245, 245, 245);">
<span style="text-align: left; color: rgb(150, 145, 147); font-family: Arial, Helvetica, sans-serif; font-size: 11px; background-color: rgb(245, 245, 245);">No, the hypothetical WRMPlayerBehavior is "a particular type of Behavior" but not really "a particular type of SessionState" so should inherit from Behavior. We should reserve inheritence for only the most direct of derivations (and C# intentionally only allows one concrete class inheritence to help enforce this practice).</span><br type="_moz">
Quote:
I think there may have been a small misunderstanding here. What I was thinking is making PlayerBehavior a virtual (or even abstract) base class with a new virtual/abstract member 'GetRenderedPrompt' and each gaming system provided its own concrete derived class, IE WRMPlayerBehavior. This new member would be assigned to a delegate or Func in Session.cs during AddBehavior(Wink at runtime. I don't know if this is correct but I wanted to clarify regardless.
Quote:
<span style="text-align: left; color: rgb(150, 145, 147); font-family: Arial, Helvetica, sans-serif; font-size: 11px; background-color: rgb(245, 245, 245);">Anyway, if you're still unclear, let me know if you want me to do part of this. It's certainly turned out to be a bit more involved than initially thought. It'd pretty much leave the reflection parts to you</span><br type="_moz">
Quote:
This is definitely turning out to be much more involved than I expected. I'm going to play around with this today, and will likely have more questions or hit you up for help later! Razz <br type="_moz">


Post by Karak (14 May 2012 12:09 AM):
[quote] Posted By JFed on 13 May 2012 06:24 AM
Quote:
...misunderstanding here. What I was thinking is making PlayerBehavior a virtual (or even abstract) base class with a new virtual/abstract member 'GetRenderedPrompt' and each gaming system provided its own concrete derived class, IE WRMPlayerBehavior.

Ah yes this is a much better idea than I thought you were getting at!

Post by JFed (25 May 2012 08:02 PM):
Karak, I would appreciate a hand with the prompt stuff when you have time as it involves changes to parts of the game that I'm not familiar with yet. Quite a bit is done and I can flesh everything out if you can lay some groundwork for me to run with. I guess my major sticking point is how to discover the prompt printer function at run-time, as accessing the WRM game stuff from the core isn't an option, and doing it the other way around is tough!

Post by Karak (26 May 2012 01:20 AM):
I'll see if I can block out some time for that this weekend Smile

Post by Karak (29 May 2012 01:50 AM):
Made progress. There is now a WRMPlayerBehavior but it's not being created anywhere. The item of note in that file is the override for BuildPrompt; the custom prompt building logic can go there and it should work fine once we get characters to create and save a WRMPlayerBehavior. Unfortunately using it is going to be a bit more work than I anticipated as I forgot we are persisting an intermediary PlayerDocument instead of just the PlayerBehavior (or whatever object we happen to be using as the PlayerBehavior IE a WRMPlayerBehavior) AS the document. Will have to give it more thought and maybe adjust the approach.

Post by JFed (29 May 2012 07:16 PM):
Heya, thanks for looking at this. However, I seem to be getting null references when I try to login now - are you? Server seems to be getting null exceptions due to null reference on Thing during login.
PlayingState, Line 31, when I login as test/test "Thing" is null causing exception.

Post by JFed (31 May 2012 11:39 AM):
I'm committing some work I've done on prompt (not done) and small temp fix to bypass the null reference exceptions.

Karak: Why are we using a "WeakReference" to PlayerBehavior? I thought in C# these were purely a memory-saving mechanism to allow GC on otherwise unattached objects.

Post by Karak (02 Jun 2012 12:22 PM):
It is possibly too early to worry about such things, but the thinking here on using WeakReference in PlayingState was that player Things are going to be our most "expensive" object trees (since they can likely have tons of inventory, behaviors/effects, and so on) which come and go over time and would otherwise accumulate a lot... so perhaps we want them to be able to GC cleanly some time after they log out or time out, etc. The player is not intended to be "owned by" the PlayingState and thus perhaps player object lifetime should not be dictated by the PlayingState lifetime. Honestly though I don't actually use WeakReferences a lot, outside of caching, so I'd welcome other thoughts on the matter. I was kind of going back and forth on this one and decided that if I was wrong in the end, using WeakReference would be a safer mistake than not using it.

Here's what I see:
Session owns Thing owns (WRM)PlayerBehavior
Session owns SessionState (IE PlayingState)

The danger of preventing GC in my mind is when a circle of referencing gets added in. Suppose PlayingState holds a reference to PlayerBehavior and then someone adds a reference from (WRM)PlayerBehavior to PlayingState as there's something in there they want to access frequently. The circular nature of the references means that even if we stop tracking the player Thing in a list of players or whatnot, I think GC will never get rid of the player object tree since they're both still being referenced (by each other).

Post by JFed (02 Jun 2012 12:28 PM):
You've obviously thought through it wayyy more than I! I've never used them before and was just surprised when I saw it. Thanks for the explanation, I look forward to pondering over it more later! Smile
Previous Topic: Persisting PlayerBehavior - WheelMUD - A C# MUD Server - Forums - WheelMUD - Bug Reports
Next Topic: Emote.cs (further to WMSHARP-72) - WheelMUD - A C# MUD Server - Forums - WheelMUD - Bug Reports
Goto Forum:
  


Current Time: Thu Nov 23 08:47:31 PST 2017