I got to hear the first few measures of the Ikosa Theme that my daughter has been working on. Sounds pretty cool, with a low bass line that rolls along (kind of like Interplay’s Descent). It makes me want to cobble together a “arcade-cabinet-mode” version of the host and client.
Haven’t seen the images my pixel master has been (sort-of) working on. He’s a busy teen boy; mostly busy with playing online games. Hopefully I can get him to produce some images for the toolbars (and ribbon) to replace the rotated text I have in the toolbars currently.
WCF, WPF, and TPL. Three programming APIs with thread issues. Getting them to work together across multiple application domains with shared state and callback has its challenges.
First of all, the framework currently is spread across three services, each with its own WCF CallbackContract: LoginService, VisualizationService and IkosaService. By using callback contracts, I skirt around forcing clients to ping-poll to see if anything new is ready. I also went with Net.TCP connections, allowing duplex channels without having to muck about with port forwarding on home firewalls.
Currently, I work under a known service pattern, with each service at its own fixed endpoint servicing all requests. Therefore, I have a [ServiceBehavior] attribute that specified ConcurrencyMode.Multiple, InstanceContextMode.Single, and UseSynchronizationContext=false. I made the class use static members, limiting me to one per application domain (may change this in the future, but not pressed to do it at the moment).
What these ServiceBehaviors do:
– ConcurrencyMode.Multiple: multiple threads are allowed in, and I handle thread synchronization
– InstanceContextMode.Single: basically means the service is a shared singleton
– UseSynchronizationContext=false: if I didn’t do this, inbound processing requests would get queued into the main UI’s thread, causing contention with the UI
These concurrent, unsynchronized singleton services need strategies to handle read and update collisions of their shared state. I have tended to favor ReaderWriterLockSlim in the past, but have also delved into TPL concurrent collections for some of the basic list management and notification queues. This works well with classifying service calls as lock-free, requires-read-lock, or write-blocking (and the occasional upgradeable-read-lock).
When probing the shared Ikosa state (or packaged resources) I shift into ReaderWriterLockSlim mode; when dealing with pure client-server stuff (such as managing the list of potential callback candidates), I tend to rely on the concurrent collections like the ConcurrentDictionary, and get to run without locking the ReaderWriterLockSlim stuff.
One last thing on the server-side is providing feedback from the largely free-threaded servicing hosts back to the host UI. Since calls to WPF UI needs to be dispatched, I provide an Action delegate to each service they can use to call-back. When I assign the delegate in the host, I need to use a Dispatcher.Invoke(), but I also need to wrap that in an Task.Factory.StartNew(), so that the feedback doesn’t get blocked by whatever the UI is doing, and can serviced when the UI thread gets around to servicing messages again.
// TODO: do stuff with msg
Well, next I’ll give a brief view of the client threading considerations.
I started tapping my son to generate some images for game items and UI elements. He’s getting into it, so hopefully the demo application will look a little less like a no-frills business app, and a little bit more like a game.
I’m also planning on implementing a hotkey for a regular attack action with whatever weapon is in the actor’s main hand. This will allow combat between two fighters a lot easier, especially if I automate the dice rolls for attack and critical confirmation.
In order to really test delaying on one’s turn, I had to add another character to the setting. Red Dude, Blue Dude, and now Green (I forgot to include the last name of Dude). I need to run three clients to test them out. Eventually I’ll make a multi-character client for TB2.
Anyhow…I did find that although I had most of the code to manage the delay list and remove actors from it when they act, I hadn’t added anything to put them into the list when they delay for the first time.
So, I sorted that all out now; and exposed some client-state refresh issues.
Therefore: next up…better signalling from the host to the clients when the delay step is activated. When the DelayTickStep is the current step, the game clock doesn’t tick unless the delaying actor decides to act. Therefore, one of the normal update signals (time-tick) isn’t sent to the clients, like it is during a LocalTickStep or LocalTimeStep.
I also need some better visual cues in the client that they can or cannot act, based on who has the focused budget.
While I’m at it, I might as well get some character models with different colors. I have some…but they’re not dimensioned to work with the auto-scaling features of the creature sizer; yet I should be able to throw a Scale3DTransform around them in the XAML.
Opportunistic inquiry is on target now. I also found that I was sending the inquiry to the original actor, instead of the one that could opportunistically attack.
As far as the missing initiative roll goes, I haven’t been able to duplicate it. Perhaps I noticed it incorrectly…I’ll keep an eye on it.
Next big test: Ending a turn without using a turn (that is, delaying).
Expedition #3 taught me that in LINQ, you don’t get IEnumerable<???> sequences that evaluate to null. They will always have a value, even if they are empty due to where clauses. Seems pretty obvious, but apparently not to me when I originally wrote the code for opportunistic strike zone geometry culling. Should be fixed now. So expedition #4 will both confirm this, and try to isolate the problems with the missing iniative roll.
Fired up the host and enabled both red and blue dudes. (Literally “Red Dude” and “Blue Dude“).
Fired up a client, logged in as Red user playing as Red Dude. I immediately rolled initiative and sent the prerequisites to the host so I could move the window to get back to the client icon on the desktop.
Then I fired up another client and logged in as Blue user. For some reason, I didn’t get prompted for an initiative roll on blue. Just another thing to hunt down. Initiative startup step is probably reporting CanDoStep as true on any prerequisite being ready instead of all prerequisites being ready.
Anyway, expedition #3 is about exploring the phantom opportunistic attack inquiry. Breakpoint is in place.