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.
Finally got the basics of turn-tracking running much more smoothly (could still use some better visual cues in the client). After establishing initiative, I moved the creature with the highest initiative.
Immediately, a prerequisite dialog box pops on the client (the server wants me to provide some feedback). No useful data on the screen (a prerequisite for which I haven’t provided a DataTemplate), and no title (I hadn’t put a key or name on it either!).
The host tells the story…I am being asked if I want to make an opportunistic attack. I realize I have provoked an opportunity against myself!
Apparently I hadn’t excluded the actor from the weapon strike zones capturing the location I am moving from. I imagine attacking myself as I leave my own threatened cube.
OK. So I add a filtering expression to the “where” clause of the LINQ. Now I can walk (both actors in alternating turns) until they get to the middle room (within line of sight of each other). About 4 rounds of game-turn time. Then I get another opportunistic inquiry (even though they are a good 40+ feet from each other. I’ll check the LINQ tomorrow in debug mode to see what’s going on.
A few days back I ran an initial test with the LocalTurnTracker. Even though the Ikosa Framework is turn-based, I hadn’t really used turn-based tracking yet. Instead I had been doing all my action testing (movement, item slot, and door manipulation) using an alternate mode of operation that I call time-tracking. In this mode, multiple clients can act more-or-less simultaneuously (a read-writer lock is employed to block true simultaneous updates to the game state). The idea is to have this time-tracker auto-pump a single turn-tick on a regular basis (about 6 seconds) if there is no activity, and slow it down if there is pending activity or an “intention” to act. Currently, I don’t have the auto-pump setup, so at least one of the clients has to end the turn, while no one else is using their turn.
Anyway, I fired up a LocalTurnTracker initialized to track initiative. It queues up one roll-check prerequisite for each actor added to the tracker. My first explosion was when I had two clients attempting to send back their results. The service call to set a prerequisite value only uses the BindKey to find the first matching prerequisite, and unfortunately all the roll-check prerequisites had the same key. And then the security kicked in, because the client had to be in the role of the actor associated with the prerequisite, and one of the actors was always finding the other’s roll-check.
That was easy enough to fix, simply make the BindKey based on the actor-id.
Once past that, I ran into another crash on one of the clients (wasn’t running the clients in debug mode). The host was locked with an unfulfilled ActionInquiryPrerequisite, which is a holding prerequisite intended to be cleared after the actor associated with it has acted (or deferred action). Looking at this, I realized I had a bunch of things I needed to re-work in this area; and edge and core cases to ensure I had covered.
So I absorbed what I had coded before (somewhat), overcame the pit in my stomach on hacking through this at this point, and got down to hacking through it…inch by inch. Which is where I am now.
The main complexity is in dealing with delays, and by that, I mean an actor delaying his turn until a future point in time. (And how I get caught up in things that delay me…)
For me, “Ikosa” was always the name of my underlying game-modeling framework. Almost always…in the early stages it was “Icosa”. However, I never intended it to be the name of any game product. Ikosa just doesn’t roll off the tongue. Nor does it imply much about the nature of any game product built on it.
Since I have multiple ideas on how to use the framework, I had multiple ideas of trade names. And these have been through some churn also.
My main motivation (fully automating turn-based pen-and-paper game mechanics for multiple players) is the one I have cared most about. I call that “Guildsmanship”. Of all the non-conflicting names I could think up (with the help of a thesaurus) it is the one that has seemed to resonate the best.
My motivation for Battlescape has been varied. I have gone back and forth several times on whether to push a turn-based team-battle (TB2) system (which doesn’t showcase all the framework features such as searching, disabling, opening doors and chests; versus being able to push out a playable demonstration (with things like various vision modes, lighting, and flight).
Anyway, I settled on Battlescape to get a playable demonstration; versus video captures of the tools. But then I went back and checked the name.
The name “Battlescape” is too loaded with prior products. Hence back to the thesaurus. And the internet. Nothing I found seems to come close to the simplicity of the term (except the very generic TB2: Turn-Based Team-Battles). So I decided to stick with it, and add the unique monicker “Guildsmanship” in front of it to get “Guildsmanship: Battle-Scapes”.
OK. So now I get “Guildsmanship” as a brand-name rather than a product name. What to do about Castle Hackenslash? Punt…worry about it later.