Friday, May 19, 2023

Traveller World Explorer - Automatic Updates or How to Do Databinding and ObjectNotify

Sorry - a far more technical-sounding title than I usually use. Basically, software is an interesting thing: often we have the data (stored in a database, locally, or just transient data the user is accessing) and have to show that on the screen. Most systems have a way of handling the persistence of data (getting it saved off to wherever it is to be saved off to). For web applications, this is a bit trickier and usually involves more stuff behind the scenes than the consumer of this data knows about. Fortunately, there are several frameworks that can handle all this fairly seamlessly for you. 

And there are some notes at the bottom of this post about the Traveller RPG as a framework. It won't hurt my feelings (and I'd never even know!) if you skip to the end and ignore all the software explanations.

Actual explanation getting started!

Anyway, the same thing applies in my little world explainer: while I am not actually saving off any data (yet!), we do have the concept of the world via the UWP representation. All these fields (starport, size, atmosphere and the rest) need the representation on the layout. Easy enough with a text field (though in MAUI this is really a label - tough for me as there used to be an actual text box...things change). But behind the scenes we need to interact with this data in order to explain our world.

Way, way back when I started with Windows GUI software, you had to extract each piece of data from the associated object it was attached to, something like:

string starport = TextBoxForStarport.Text (or often .Value or something like that)

With the more current ways of doing things, both in the web and local applications, you generally databind a model to the layout. What this means is that each object can be associated with a field, and so we no longer need to grab each thing, we have them all via the model.

In this particular case, I wanted to have the explanation come up more automatically. That is, click the explain button and it should display that data. Originally, I'd do this by creating the text, then assigning it to the label, and somehow update the layout. However, the .NET framework has a lot of things to help automate this so that we don't have to manually update things and the framework handles it for us. I never could get it working before, but after poking around I did find a really nice class that I'm using to get this to work. So now I just update the world's explanation field (which is not persisted to the DB, assuming I get to a DB at some point) and the layout automatically displays the updated value.

In web applications, this requires a lot of javascript usually, and is (for me at least) painful to implement well. Oddly, Filemaker (Apple's version of MS Access) does this out of the box, and I remember how surprised I was about that. For instance, you have a customer record open, and someone else edits that record and saves it. Your layout will be updated automatically. Magic! Okay, not magic to non-developers, but for someone who has been writing code for 40 some years (if you count college and high school) it is really cool.

Anyway, more code to see how the UI looks:

<Label x:Name="ExplainLbl" Text="{Binding Explanation, Mode=OneWay}" />

This bit of XAML code says we have a label, and it has a name in case we want to reference it (though we really don't need to but old habits), and the text for that label is bound to the model's Explanation field. The OneWay mode simply means we get it from the model, and we are not updating the model. Basically, a read-only field (though I think I may have to add that option).

Behind the scenes, we enable this when they click the Explain button:

 private void ExplainClicked(object sender, EventArgs e)

    {

Worlds world = (Worlds)this.BindingContext;

if (world == null)

{

DisplayAlert("Traveller World Explainer Error", "World is not set", "OK");

}

else if (!ValidateWorld(world))

{

DisplayAlert("Traveller World Explainer Error", "World is not valid", "OK");

}

else

ExplainWorld(world);

    }


    private void ExplainWorld(Worlds world)

    {

        int size = int.Parse(world.Size, System.Globalization.NumberStyles.HexNumber);

        int atmo = int.Parse(world.Atmosphere, System.Globalization.NumberStyles.HexNumber);

        string atmosphere = Atmospheres[atmo].ToString();

        world.Explanation = $"Size: {size * 1000} km diameter\nAtmosphere: {atmosphere}";

    }


C# code that handles the button click (and does some verifications) and then performs the explanation if if have a valid world (and I'd like to move that validation to the actual model somehow, and only enable the explain button if we get a valid world). It is that last line that sets the world model bound to the layout. This class uses a base class that, when setting a field, triggers the something has changed event which then tells the UI to update that info. While I've tried in the past to figure this out, I finally broke down and, after some web crawling, found a really nice and easy class to handle it. Though I probably should rename this class to match my stuff, but as this is a personal project, I like to remember where things came from.

Part of that .NET framework is converting hex strings to numeric values. So that A = 10, B = 11, and so forth. We use the int.tryparse to convert from the hex value to a numerical value so that I can simply reference the array of descriptions. This will break down as Traveller uses a pseudo-hex code: they don't use I for instance. However, as the UWP does not include that, we're good. In theory the ValidateWorld(world) method would check all that (or preferably, do that in the UX layer. I know there is a way!) so it won't break. I do need to add more safety checks, and really need to see about testing (another area I am really bad at). And to make this safer, I'd probably instantiate my own Traveller hex to int function (really simple - just list the values like "0123456789AB..." and find the position of the character. Easy-peasy).

I've also started using ChatGPT to help with some of my software: it has actually been useful in a few cases. While I don't see developers being out of a job anytime soon, it does change a number of things.

The Traveller Framework

Finally - what the heck does this have to do with gaming? Well, two things: first, this blog was started when I got laid off the first time more than 20 years ago, and decided to chronicle my stuff about writing software for Traveller. It has expanded way past that as it is more a generalized gaming blog. But also, speaking of frameworks, the original intention of Traveller was itself a framework (as technically all RPGs are, but to me Traveller excelled at this). And the 2nd part is just that: Traveller is itself a framework.

For me, Traveller is the ultimate toolbox for gaming (and I think I've noted that in my COTI signature). The original 3 LBBs let you do anything you wanted to. The Traveller framework, just like a software framework, gives you tools to make things faster and more consistent. An RPG is a group effort for the most part: there is a back and forth between players and referees. What makes that work is the framework of the rules. Later on, as GDW expanded on the Imperium and codified it more, that setting itself becomes another framework to work with. Just as many software projects will have multiple frameworks in it. RPGs can also have multiple frameworks to handle specific aspects. 

Now, being an opinionated developer (as most developers tend to be), I am not always in favor a framework: while it is easier up front, sometimes you need to do things that the framework does not handle well or is even actively preventing you from doing. And then you are fighting against that. In Traveller, the Megatraveller framework of the Rebellion was not something I liked (and I was not playing actively much then either) so my framework is the original version: while I often use the 3rd Imperium background, it has been fairly loosely adhered to. 

Same for the T5 rules: there are a LOT of rules in that 800+ pages. I do NOT like the trading rules and stick mostly to the classic Traveller ones (the Merchant Prince rules, which are the basis I believe for T5's trade rules) are just so dry. There is no flavor. I also use the GURPS trade rules a bit sometimes, but my current players are not really interested in the trading game, so I've not explored those much lately. But other aspects are really great - QUERBS really allows you to customize anything. I may use those rules even in fantasy games, though in pure mechanics it just ends up something like a +1 or something, but we could add chances of weapons breaking and all that with that framework.

Anyways - frameworks are everywhere. Sometimes useful, sometimes they just get in the way.

And finally - I start my new job Monday. It looks to be a somewhat challenging job, plus there is a bit of a commute. Though it will become a hybrid job and I am hoping for just 2 days a week in the office, we'll see what happens. Of course I am nervous: I am not good with change despite working in a field that is all about change (and frameworks!). 


Ads

Hmm, I had not realized they would have been so intrusive...if you want me to remove the ads, please let me know. I panicked when getting laid off, and wondered if I could make any $$ via ads. But they are really intrusive so for the 3 cents I may get a year, probably not worth it!

Edit again: I think I turned off the ads in the middle of the text, so we just have top/bottom and sides. But if it is annoying, let me know. I really do this blog mostly for myself, and for anyone interested in my ramblings.

No comments: