Saturday, 30 March 2013

jjj sequence editor

Sometimes I hate it when I have ideas in my head and they just wont go. Last night I was getting bored with TV and kept thinking about a sequence editor - the most complex single interface component I'm after - so I fired up the computer and hacked from 11pm till 3am or so. The the less sleep I get the more "anxious to get shit done" I seem to be, until I fall apart.

So although it's not much to look at yet - and the code behind it is even worse - last night together with a few hours today I came up with the following interface and all the guts to make it work.

I'm not sure if i need the jog-wheel there as the functionality is provided by the scale too, but it lets one move the timeline forwards/backwards. I also doubt i need to support any-number-of-layers, but I suppose I may as well - once you have more than one, any number isn't much harder.

The scale at the top can be dragged - it acts like a scroll-bar, or two fingers pressed on it can be used to zoom in and out.

The main sequence interface is modal, in that you are either working with 'layers' (or 'tracks'?) or clips. The "Layers" button is a toggle which switches modes, although I think I need a better interface design for that.

I don't have animations (i'm kind of up in the air on them as most of the time they just piss me off with their added delay) but the clips can be "drag and dropped" around the sequence in fairly obvious ways. So for example dragging a clip creates a layer-locked box which follows your finger, and a | indicator where it can be inserted. Letting go moves the clip to the new location, creating new layers as required.

Layers work similarly, except layers can be re-arranged, and the start position for the layer can be altered. All clips within a layer always run one after another. Doing things like moving the first clip causes the layer origin to be moved so the rest of the content isn't changed and so on, so it kind of 'just works' like you'd expect.

I have a basic single-item selection mechanism too, for deleting or other item-sensitive operations (e.g. where does 'add clip' add?).

So although the code is a complete pigs breakfast it is quite functional and stable, and with a bit of styling and frame graphics it's almost feature complete. Probably the last major functionality missing would be some start/end 'snapping' - but that's more of a nice-to-have.

Compositor too

Yesterday afternoon - when I was supposed to be relaxing - I worked on a prototype of a compositing and video creation engine. Initially I tried to use the ImageZ approach of compositing in floats, row-by-row, but android's java just isn't fast enough to make that practical. So I fell back to just using android's Canvas interface as the compositor. I guess it makes more sense anyway as it gives me a lot of functionality for free. I could get more performance bypassing it, but it's going to be a great deal of work. Obviously, I didn't attempt audio.

Initially I just wanted to see how performance was, but I ended up adding video and text "sources", which can be mixed/matched together. Performance is ... well it's ok, it's just a mobile platform with a purely CPU pipeline. A 10 second 800x600@25fps MP4 medium-quality clip takes about 20 seconds to decode+render+encode on the *ainol elf 2. For the compositor, about half the time is spent in decoding the source material and half spent in compositing, which isn't exactly flash - but it's all bytebuffers and Bitmaps and copying shit around multiple times so it's hardly surprising.

As usual the ideas in my head exploded yesterday and I thought about all sorts things: ways to create an interesting and usable interface, keyframe animation of clips and properties, multiple 'use modes' such as a simple video cutter, potential local client/server operation, using your pc to do the rendering, or using the tablet as an interactive control surface for a pc based application. But whether i'll have the time and motivation to see any of it through is another matter.

If i remember to, I'm going to try to track how much time i've spent on this project, so far it's about 3 days. I initially thought I could do a quick-and-dirty "weekend hack" and see how far I got and just leave it at that, and then thought the better of it. It's a 5-day long weekend for me so that's too much like hard work!

Friday, 29 March 2013

jj .. jay?

Time to drop another ``prototype-bomb'' on the unsuspecting world ..

Ultimate goal is some sort of simple-to-use video editor, but to begin with there are a lot of user interface and algorithmic problems to work out. I tried to find some simple editors in the android store but they pretty much sucked - based on server processing, questionable advertising, difficult and confusing to use, and usually quite slow.

However, a few hours hacking so far and I have a fully functioning "clip" editor, which is one of the basic components required.

I've actually been meaning to play with something like this for quite a long time, and finally the planets aligned such that it was an opportune time to look into it (nothing to do with any internet goings ons). I will probably also dabble in a JavaFX version if I get anywhere with it; although the interface requirements there are very different.

Thursday, 28 March 2013

jjmpeg android hardware decoding

I somewhat embarassingly just discovered that FFmpeg already has some support for using libstagefright for hardware video decoding on Android.

I always thought this was the place it should go, since mucking around with OMX or other junk is just such a pain, and it belongs there.

Bit of a headfuck getting it to compile, particularly since this adds a C++ dependency, and then after all that ... it just crashes.

I/ffmpeg  (10484): Assertion i < avci->buffer_count failed at
   /home/notzed/svn/jjmpeg-1.0/jjmpeg-core/jni/ffmpeg-1.0/libavcodec/utils.c:603
F/libc    (10484): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 10607 (VideoDecoder)
I/DEBUG   ( 1894): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   ( 1894): Build fingerprint: 'samsung/m0zs/m0:4.1.2/JZO54K/I9300ZSEMB1:user/release-keys'
I/DEBUG   ( 1894): pid: 10484, tid: 10607, name: VideoDecoder  >>> au.notzed.jjmpeg <<<
I/DEBUG   ( 1894): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
I/DEBUG   ( 1894):     r0 00000027  r1 deadbaad  r2 40126b0c  r3 00000000
I/DEBUG   ( 1894):     r4 00000000  r5 620f2a04  r6 60bfcc00  r7 61d0b8b0
I/DEBUG   ( 1894):     r8 5b127c80  r9 61bd5718  sl 000104d5  fp 61bd5718
I/DEBUG   ( 1894):     ip 5dc4bcbc  sp 620f2a00  lr 400f8c65  pc 400f52fe  cpsr 60000030
I/DEBUG   ( 1894):     d0  0000000200000000  d1  4b0000004b000021
I/DEBUG   ( 1894):     d2  000000094b000021  d3  0000000000000000
I/DEBUG   ( 1894):     d4  3ce5999061da5385  d5  3f34e1653f349a33
I/DEBUG   ( 1894):     d6  3f35287b3f356f75  d7  0106999ec0000000
I/DEBUG   ( 1894):     d8  0000000000000000  d9  0000000000000000
I/DEBUG   ( 1894):     d10 0000000000000000  d11 0000000000000000
I/DEBUG   ( 1894):     d12 0000000000000000  d13 0000000000000000
I/DEBUG   ( 1894):     d14 0000000000000000  d15 0000000000000000
I/DEBUG   ( 1894):     d16 8000000000000000  d17 ffffffffffffffff
I/DEBUG   ( 1894):     d18 416347d4c0000000  d19 3fe0000000000000
I/DEBUG   ( 1894):     d20 3fe0000000000870  d21 0000000000000000
I/DEBUG   ( 1894):     d22 0000000000000000  d23 0000000000000000
I/DEBUG   ( 1894):     d24 0000000000000000  d25 0000000000000000
I/DEBUG   ( 1894):     d26 0000000000000000  d27 0000000000000000
I/DEBUG   ( 1894):     d28 3f3504f3bf3504f3  d29 bf3504f33f3504f3
I/DEBUG   ( 1894):     d30 0000000000000000  d31 3f3504f33f3504f3
I/DEBUG   ( 1894):     scr 20000010

So yeah, i dunno. Perhaps it is a bug in jjplayer, but I tried removing all output handling, and it still just crashes inside codec->decode(), and the api to that point is so simple I don't think I can screw it up.

I've wasted half a day on this and it's losing it's interest very fast ...

Update: So, just when I was about to give up, I found a threading bug in the codec implementation. This at least lets it decode more frames but it's still crashing later on inside the GLES library.

I haven't got the NV21 image format working properly yet, so it may be related to that, maybe.

Update: Well that was pretty much a full day down the drain, I submitted a patch to the ffmpeg-devel mailing list (yay, subscribe to a high volume list of zero interest to me just to submit a 10 line patch).

It turned out that the file I was testing against refuses to play at all in the bundled players, so it's something to do with the hardware/firmware. At best they load the first frame then abort, although they don't segfault (probably the parser/demux stops it going as far as the codec).

On a video recorded from the camera it seems to work ok, although it certainly doesn't appear particularly smooth - I would need to do some timings to see what's going on. I haven't written the NV21 support either. And the build stuff needs cleaning up and parameterising before I can check it in.

If it's going to still be so useless i'm not sure I really care to be honest. And I've had more than enough for today so whatever I decide will have to wait.

Wednesday, 27 March 2013

duckduckgo

I've decided that i'll be giving duckduckgo a 'go' for a while for my search.

I've been an extremely heavy user of google's search for many years, and it is an indispensible tool for saving time. But i'm getting sick of it trying to be 'smarter than me' - I pretty much have to manually tell it to do a verbatim search every fucking time I search for anything technical (i.e. almost every search) which makes every new search that bit more tedious and frustrating than it already is. And given their spate of recent prunings who knows how long that will even last.

And just in the last couple of days they seem to have fucked around with it even more: i get 4-5 results and then a 'similar searches' section, which I do not find useful whatsoever. Sadly I often find searches I do pointing back to my own posts or code, which indicates to me that perhaps I really am a beautiful and unique snowflake after-all, and subsequently knowing what is "popular" isn't terribly useful to me.

Targetted search based on seeker popularity by default really seems a pretty strange feature unless you're writing a search engine for pop-culture or a shop (insight: oh hang on, that must be exactly what they are doing! silly me). Searches should first be for facts, not for reinforcing currently popular interests or fasionable trends. I already felt extremely uncomfortable with the fact that google search was customising results at all, but this is so much worse. If you went to a researcher and asked for detail on a topic, would you want them to take into account both your appearance and other customer's desires when they fulfilled the request, or would you prefer an unbiased, objective collection of all available information?

I guess those of us working niche fields or with niche interestes are going to have to get used to the fact that we are simply not enough of a product-base for Google to care about (remembering we are the product). They don't really care if they lose us because their customers don't really care if google loses us. Based on "The story so far", I think we can expect to see more of a mainstream/pop/fashion based focus to their products, which is very sad turn of events. With the closing down of various networked services, we're also going to start to lose trust in their long-term commitment to some of them. Surely blogger will not live outside of Google+ for much longer (tbh, not sure i care, so long as google+ is linkable and crawlable by any other search engine), and what about google code?

I also finally worked out one major reason why firefox was constantly using a good chunk of cpu time: apart from adverts and other annoyingly pointless crap, the main culprit was simply the google search results page. Which I find quite baffling because there are no visually moving parts whatsoever. I really wish firefox would simple disable javascript on tabs you're not currently looking at, because all it does is let crappy web page coders make firefox look like a bloated piece of shit, even if it isn't really.

Duckduckgo is certainly more concise, perhaps overly so to the point of missing relavant information, so it may not fullfill my requirements. A search for jjmpeg only finds 1 post from this blog for instance, and I often prefer to see multiple in-site references (perhaps it is an option: at this point i've used it only a few times). But i'll see how it goes, my search needs vary depending on what i'm doing. I just wish it paged the results using numbers mind you, I'm using the html version since the infinitely scrolling "Web 2.1.x(tm)" page completely sucks: as they all do.

Bummed out, or am i?

This week I've been experimenting with the performance of some NEON code. It is from an algorithm which was developed in OpenCL for desktop GPUs and then downscaled to fit on a beagleboard (only for development purposes). The overall algorithm is identical but the way some of the steps are implemented is different (and for some significant components much less computationally and bandwidth intensive).

The OpenCL code took many months to develop - although that included dead-ends, multiple steps if refinement, and other distractions including completely unrelated work. Even with that, I'd put the effort at around 4-10x that for the NEON code.

The NEON code took a few weeks. It obviously helped immensely that the algorithm was primarily known in advance although the downscaling alterations were not. On the other hand, my total experience with NEON is far less than OpenCL and certainly C or Java in terms of hours.

One reason the NEON code was much easier to write is that because as it is so cheap to invoke, one can just concentrate on the bottlenecks, and leave the housekeeping to C. e.g. I can write a routine that processes as little as 16x16 pixels in assembly, and leave the addressing crap to C. There is also no marshalling or other api binding to worry about: the C is plain C, and the assembly is plain assembly, and even though JOCL is far far better than using the C api directly it's still quite a bit of work. As much fun as OpenCL is, it's even more fun hacking NEON because you can concentrate on the fun bits even more.

Although the OpenCL model is also based on simple kernels which should equally be simple and isolated - it isn't really quite like that in practice. All but the simplest of kernels end up turn into 64-way parallel subroutines utilising LDS, barriers, and so on. Without that you end up leaving skads of performance on the floor, so it really is necessary. Not to mention all the marshalling and boilerplate in the host-code to communicate with it. And because of the marshalling and invocation latencies pretty much everything is forced onto the GPU.

So what's the point i'm getting at?

Well after all that, the projected performance on the previously-latest-version of a popular handset is only about 5x slower than a HD7970 on a pretty beefy desktop!

Yes that 5x speedup is important enough that it is worth it and opens it up to more applications, but on a personal level i'm just totally bummed it isn't much more. It's a highly parallel and bandwidth intensive workload which should be well-suited to a GPU. Obviously opencl has the advantage that it isn't tied to a single bit of hardware. It's a pity SSE sucks so much otherwise it would be interesting to see how a desktop cpu fared on it's own.

I plan to "back port" the algorithms so it can be improved on the GPU, but I have a fairly educated feeling that another 2000% performance isn't very likely. I will also need to use some AMD proprietary extensions, so the portability will suffer too.

I'm sure I can improve it, but I just take it as a big personal slap in the face for all the effort that's gone into it so far!

Of course, the alternative view is that ARM+NEON is the bees knees - with less effort i'm getting relatively great performance. But we all knew that so it isn't such a revelation ...

The main bottleneck on ARM cpus at the moment is the memory, and if you can utilise the cache effectively it really flies. I would really like to see how a beagleboard like machine with big/little A15/A7 quad core and much faster memory would fare, all these cheap android dongles are far too constrained by their form-factor.

Update: Well I might need to eat my words here. Today I started to look at GPU optimisations based on what i'd learnt from the ARM experience and trying to reduce the bottlenecks of the GPU code.

The key word of the day: batching.

One reason I wasn't previously batching the processing is because it didn't really fit the data-flow of an earlier application. But I have now achieved something like a 50x boost in one key algorithm by a combination of batching the work more aggressively and some other significant algorithmic changes.

This is more like it. No longer bummed out ...

Tuesday, 26 March 2013

Another pointless bruhaha at an IT conference

I just have 2 words to say: Google Glass.

Better get used to it, and more.

Monday, 25 March 2013

#$#@$ Android

One of those "I can feel the hair going grey and falling out" mornings. Although at least it wasn't one of those "I made every mistake I could have, every time I did anything" one. My throat is hoarse from all the screaming of definitely un-pc obscenities.

I'm trying to get some jjmpeg stuff working on android - but I can't use the jjmpeg android branch due to licensing issues. I need to use a fully shared library version.

Compiling the code was easy enough but trying to use shared libraries on Android turned out to be a complete head-fuck.

First, for some fucked reason although shared libraries are placed into a per-architecture location, the bloody library path isn't setup to automatically use it. So you have to hard-code all the architecture into the dlopen() calls.

Actually second, it's even worse than that, you need to use absolute paths for dlopen() otherwise it doesn't work. I just hard-coded it, a stack overflow answer stated that finding the path from Java was 'trivial', but then neglected to include which api trivially provided it. I didn't really want to change jjmpeg anyway.

And thirdly, it's flaccid linker doesn't support versioning of the shared libraries. To fix that I hacked up ffmpeg/library.mak to only create non-versioned sonames, and then hacked a build script to make it compile properly as with that change it didn't seem to want to build the libraries properly. I did this by specifying each lib*/*.so manually as the build target.

And the fun's only just started, i've still got a pile of C and NEON code I've got to get to work ...

Update: Oh fucking joy, no complex numbers now.

Update 2: Well I managed to replace complex.h with a few lines of #defines - fortunately they're complex numbers are in the compiler, just not in libm, and apart from the basic arithmetic I only needed creal/cimag and conjf.

And somehow, after re-arranging 1500 lines of C and 1500 lines of NEON assembly language, creating JNI wrappers, and hooking it into ffts and jjmpeg/head ... it worked almost first time. I'd set myself all week to get that far so i'm pretty chuffed, even if the headache from the days earlier tribulations hasn't yet receded.

I really have the beagleboard and a GNU operating system to thank for that - without being able to develop on a proper system first it would've been a nightmare.

Time for a couple of glasses of nice Barossa red methinks.

Sunday, 24 March 2013

clAmdFft

Just a quick note, I created a small JNI wrapper using JOCL to access the clAmdFft library from AMD, to see how it compared to the Apple FFT I ported to Java.

Bummer, the Apple FFT is still faster for my problem size - image processing. Kernel time on a 1024x1024 sp fft is ~0.5ms vs ~0.38ms for the Apple one (HD7970). And the apple one only requires 3 passes. Smaller and/or batches seems to scale linearly too.

All that (minor) frustration with the clMiXeDAbbrCamlCase API for nought.

Update: So I actually tried writing a 64-element parallel FFT to see how I could do ... well, not good. Apart from wasting a lot of time wondering why "no matter what I did it took 15ms" because I hadn't allocated a properly sized buffer (out of range access I guess), when I finally got it going it was about 10x slower than the Apple one. Blah. Actually the apple one was about the same speed (if not faster) than a simple memcpy (using float2).

I wasn't being very smart about it, I just broke a 64-element FFT into one-calculation steps and used shared memory to communicate the results. A big overhead was the index twiddling for which I just used lookup-tables, but even the shared memory communication was expensive.

I may have gotten something wrong anyway, the profiler said the apple fft code had many many fewer global read/write operations, and I couldn't work out why. Although I was trying to do it all in LDS in a single kernel.

Since I didn't verify the results - I was just seeing how the memory access patterns would work - I have no real idea whether it worked or not anyway. But I may have another play another day, I wrote some code which outputs the expressions required between two arbitrary "layers" of the calculation so I can use larger kernels than the radix-2 ones I was testing with.

Friday, 22 March 2013

Nvidia and OpenCL

I don't particularly follow much what Nvidia are up to anymore - for the last 18 months that I was subscribed to the 'gpgpu' mailing list of theirs it never mentioned OpenCL once, so I de-subscribed a few weeks ago because it contained nothing of interest.

I've been poking around anantech/tomshardware too much recently (work-stress induced apathy mostly) and read a few articles about Nvidia's latest developer conference.

Well, no mention of OpenCL anywhere, and now they're pushing their CUDA stuff on mobile as well. The lack of OpenCL 1.2 support, and the poor showing in compute performance of their recent hardware makes it pretty obvious that they're not interested in it, but this really nails the coffin shut.

In comments people always claim that the poor OpenCL performance is just unoptimised drivers. However this doesn't hold much water - because the architecture of their driver design is such that both CUDA and OpenCL are simply thin layers above the same infrastructure. It would be like saying ``Oracle only focuses on Java, and that's why Scala isn't as fast'' - but any improvements they make to the JVM benefit Scala too.

The only alternative is too crazy to imagine, that they're deliberately knobbling the OpenCL performance significantly on purpose. Anyone investing heavily in their super-computer infrastructure would be well to take such nonsense into consideration.

Risky move?

With fairly broad support for the HSA foundation (it seems?), and a computing model which is more advanced than CUDA, can they go it alone? Well apart from their crappy controller/game thing, it doesn't seem like anyone is too interested in Tegra4, so let us all just hope that it is a no on that one.

I'm a little bummed that the priorities at work has kept me away from OpenCL for a while, I had a single day back at it after some leave but then the focus changed again. Although I might be doing some more NEON assembly soon so it's not all bad.

Thursday, 21 March 2013

The farmer quest

I haven't had much time to work on anything this week, but I did get the "farmer quest" from Zabin's game implemented (or at least, the script for it). I'm a bit blank on the imagination at the moment, so by just getting old functionality working the problem is just one of filling out the implementation and script api.

This was pretty nasty in Dusk script, but is relatively straightforward in javascript. I experimented with just using the java api's a bit here too, rather than just adding script-friendly functions; it's a bit harder to use, but is more powerful.

The 'quest' is operated by walking onto a location infront of the farmer, this is just attached to the action for the location. Rather than 'bounce' the player back a square I just let them stay there - actions only trigger when you enter a location, not for standing on one. Prevents some ugly flicker in the client.

if (trigger.isPlayer()) {
    var state = trigger.getInteger("farmerquest");
    var list = trigger.getAllItems("bugcorpse");

    if (state == 0) {
        // first meeting
        trigger.chat("Farmer says: It doesn't look like my crop is going to support me this year.");
        trigger.chat("Farmer says: Those $%$# bugs ate half my crop.");
        trigger.chat("Farmer says: I have a job for you, if you could kill those bugs and bring me back their corpse, i'll reward you for each one.");
        trigger.chat("Farmer says: If you bring me 20 corpse's at once and I'll give you something special.");
        trigger.setInteger("farmerquest", 1);

Since the state can be tracked explicitly, it's easier to test at what position the script is, without resorting to returning from the script. Actually as a side-effect of the way scripts are invoked, it's actually impossible to return anyway - every script must be fully structured just like the original PASCAL. i.e. there is no 'return' or 'exit' keyword allowed, since the scripts are run as top-level code.

    } else if (state < 3 && list.size() >= 20) {
        // Check for 20 at a go
        for (var i=0;i<20;i++) {
            trigger.removeItem(list.get(i));
        }
        trigger.chat("Farmer says: That's 20 corpses, here take my Kaizer Blade for helping me.");
        trigger.setInteger("farmerquest", 3);
        trigger.addItem(game.createItem("kaizer blade"));

This bit can also just access the Java List interfaces directly to do bulk operations, as well as the wider 'game' object which has general utility functions. I do however have to make sure I design these public interfaces properly as for example the scripts are executing on another thread (eventually in a sandbox), so getting the mix right will be an ongoing experiment.

    } else if (state <3 && !list.isEmpty()) {
        trigger.removeItem(list.get(0));
        trigger.chat("Farmer says: Great, you killed another Bug, here's 30 gold for your reward.");
        trigger.addGold(30);
        trigger.setInteger("farmerquest", 2);

Simple reward is trivial to implement, it also tracks if you've done it at least once.

    } else if (state == 1) {
        trigger.chat("Farmer says: Have you collected any corpses yet?");
    } else if (state == 2) {
        trigger.chat("Farmer says: What are you doing back again, haven't you got work to do?");
    } else {
        trigger.chat("Farmer says: Thank you for your work, I think my crops will be safe for another year.");
    }
}

And finally a bit more detail for the default case since we track the state explicitly. And this version doesn't let the player keep killing the newbie enemies for easy cash once they've got the grand prize.

Update: I decided to keep up with this approach and i'm working on porting Zabin's game to the new engine bit by bit. I've imported the map just as another world which you can enter via a door and started on the tutorial. This will let me work on it in little pieces and also expose any implementation issues in manageable chunks.

e.g. When I added all the mobs from the original I hit a bug with the entity index and decided to change it completely. Fortunately as one of the first things i "fixed" was to abstract the map and map-related operations it means I have complete flexibility on how the internals are implemented.

Previously it was implemented as a 2d index storing a pointer to the first entity at a given tile location. These were then linked using a next field in the object and managed using some single-linked-list logic. For big maps with sparse entities it takes a lot of memory just for the array - 2MB just for the original dusk map. I changed it to use a hash table keyed on an x+y pair and stored using a LinkedList. Lookups will be marginally slower but it's made up for by the reduced footprint and ease of use, and because I'd abstracted the map previously I can always change it.

Update: Another thing I noticed that doorways were a pita to write. For each one you need at least an entrance and leave action, as well as an entrance and leaving location. And each action requires a one-line script. So I added another function in the map file which allows jumps without requiring a script to be run.

   x.y.goto=alias-name

It still requires 4 settings, but it doesn't need the two single-line scripts. This also means that map location aliases must now be globally unique.

Sunday, 17 March 2013

Scripts and conditions

As i've managed to get most of the guts of the game working - battles, internal indices, and so on, i've started to fill out the implementation by adding commands, and hooking up the scriptable actions. Even though i've only spent a few hours here and there it's coming together easily as there isn't much work to hook up a new script point. It's mostly just deciding on a convention and then sticking to it.

And to test the idea simply trying to port over some of the simple scripts and objects, such as absinthe ...

Revisiting the on-drop script for absinthe:

order trigger "get absinthe"
removeItem trigger absinthe
chat trigger "You pour the absinthe out and stare in disbelief as it eats a hole in the ground."
endscript

Took me a while to work out that first two lines simply remove the object from the game: the script is only executed after the default 'drop' action is executed - i.e. to leave it at the current map location. I decided to change the behaviour slightly so that if supplied, a script must implement the default action as well. This makes for a simpler and more obvious script in this case. Functions like 'removeItem' are not working on symbolic names either but the exact object, which should reduce coding errors.

owner.chat("You pour the absinthe out and stare in disbelief as it eats a hole in the ground.");
owner.removeItem(item);

Then I looked at the on-use script:

order trigger "emote wails: Oh, my head!"
addconditionwithduration trigger tired 500
if > trigger inte 10
  inc trigger intelligence -1
end
removeitem trigger absinthe
endscript

Fairly straightforward, except I hadn't filled out the condition stuff - the logic was there just no script hookage.

But I started with the JavaScript and worked backwards from there:

owner.emote("Wails: Oh, my head!");
owner.setCondition("tired", 500);
if (owner.getINT() > 10) {
  owner.addINT(-1);
}
owner.removeItem(item);

Previously, item definitions included fields which defined the scripts to execute. I am scrapping that and scripts are just found based on conventions from the item name. So for example, these scripts are stored in onScript/absinthe.drop and onScript/absinthe.use respectively. This means I don't have to little the objects themselves with references to names of files, and I guess I could also use it to implement an interface-based object model, particularly if i allowed scripts to store arbitrary persistent variables on items.

The existing condition mechanism defines conditions via a two-step process. Conditions are referenced by name, and then details about the conditions such as their firing rate and which scripts to execute are loaded from a file. I decided to scrap that and just let conditions be created on the fly, and use a convention to discover the scripts to execute. There aren't enough details to warrant the hassle required to maintain another directory full of files. So in this case, the "setCondition" is all that is required to set the condition, and the scripts which define the condition behaviour are defined in onCondition/tired.start, onCondition/tired.end, and onCondition/tired.tick. i.e. in a way very analogous to the item scripts, and one which will be repeated for all other script types.

Looking at the actual functionality for these scripts, the start/end is simply a notification to the player.

tired.start:

owner.chat("You suddenly feel very tired.");

tired.end:

owner.chat("Your normal level of conciousness returns.");

And the guts is in the tick script:

number tiredint * rand 150
number tiredint + tiredint 50
#
# You need an int of at least 50 to reduce the effect of tired
# and an int of 200 or more to be immune
#
if > tiredint trigger inte
    order trigger "sleep"
end
endscript

And the converted script, showing that anything with maths and logic is going to be a lot easier to write:

/*
* You need an int of at least 50 to reduce the effect of tired
* and an int of 200 or more to be immune
*/

var test = Math.random() * 150 + 50;

if (owner.getINT() < test) {
   owner.sleep();
}

Incidentally i'm considering making 'sleep' itself a condition, but at this point I haven't.

Current scripts also use conditions as a sort of general-purpose persistent state-holding variable, and I am going to remove that functionality and force the use of variables instead. To help enforce this, conditions will be visible in the client. This will leave conditions to be used for what they are intended: timed and/or periodic scripted behaviour.

I also decided to change the map alias stuff a little bit. Locations are still given symbolic names in the same way, but the visible/action/moveable script names are supplied directly. This lets 'true'/'false' be optimised, and still lets scripts be shared amongst locations. I also made the per-tile-type scripts follow the same conventions so everything is consistent.

Whilst looking at some of the existing scripts like the banks and so on, I noticed they add global commands to the game, but then have to have location checks on them to make sure they only activate at the correct locations. So another fairly simple extension may be to have per-map commands. On further thought I thought it might be more useful to have per-location commands implemented using an 'on-command' script, so then for example an ATM or bank teller for a certain bank could be implemented inside a single script, and then attached to any number of locations throughout the map. Since it's quite easy to add this, such a feature could also be added to tile-types or objects or even mobs instead ... (e.g. if you're standing on/next to an object/mob).

Friday, 15 March 2013

The network is the computer?

Apparently Google in their benevolent wisdom have decided to shut down Reader. It's not something I use but evidently it has a loyal following.

They'll whine a bit, then move on: like we all do. Adaptability is about the only positive trait of the human condition.

However maybe enough will realise this push to centralised network computing, or so-called "fog-computing", isn't really such a great deal after-all. If they are as "tech-savvy" as they seem to think they are, they should've seen the risk anyway. One gives up an awful lot for a bit of convenience - and I think our modern lives (for westerners certainly, and the `middle-class' everywhere else) are convenient enough as it is that we can cope fairly well without a little bit more. The fact that the biggest internet company of all can decide on a whim to kill a product used by thousands - who have no recourse whatsoever - should be a wake-up call for everyone.

As an aside ... it's nice to see our benevolent rulers at google deciding that advert-blocking apps are suddenly to be excluded from android's software library using language which is obviously there for the "good reason" of banning cracking software. Ahh lawyers, you suck. I just turn off wifi to block ads, saves power and most of them are for american shit I couldn't buy even if i cared to.

This is all just part of an on-going land-grab of a public resource - another tragedy of the commons - whereby private organisations are surrounding public culture with their own fences and gateways. All that crap we put in our blogs or our status updates or our "twats" may be crap but it's still public culture. Except when it's actually controlled and owned by a centrally managed group which locks it away in their own corral. It's all about control: making sure you read the information in the way they want you to.

Simply so you can't avoid the advertising!

For fuck sake, how many adverts do we need to be exposed to? Just how much junk can we buy? With Australia's extra tv channels now there is no regulation on the number of adverts and it's basically impossible to watch anything but the ABC anymore without using a recording or a mute button (i've already worn out my tv's remote). If i'm alone I barely turn the tv on anymore - it's not like there's much to watch most of the time anyway that isn't either a repeat you've seen too many times or some "it's the same every show" show which one eventually tires of.

All of these companies providing or facilitating centralised network services are will eventually start to see increasing risk from this type of behaviour. For one, people could get sick of the spying although that doesn't seem to be about to happen any time soon. It would be nice to think they'd get sick of buying junk: but that is unlikely too. Customers might start to realise that their 'lightweight browser-based app' is actually a shitty user-experience and to boot, more of a resource-hungry monster than a stand-alone desktop or pocket application. For coding on DuskZ I've been running netbeans 7.3 on my 5-year-old+ X61 thinkpad w/ 2G ram, and although it's a bloody heavy application doing some pretty complex shit, it still manages to use less resources than firefox running a few web pages (blogger's simple front-end, and the google search page seem to be the worst offenders, but unfortunately firefox has no 'top' function to find out which tab is hogging the resources). Personal mobile platforms might always be about 10 years behind the performance of desktop computers, but pretty soon a 10 year old desktop computer will be more than enough processing power for processing tasks to be run locally instead of remotely.

Here's an idea: a network isn't a one-way street. We don't all have to passively "consume" "content" from a centrally managed location. Well we could do that years ago; radio and television provided just that service. Every computer (including phones) connected to the network is just as much a part of the network as giant data-centres in tax-free low-cost-power locales. Every computer is capable of creating culture to add to the shared wealth of the public domain. Before the microsoft era computers were used far more for creative endeavours.

Yes I see the irony of posting this on blogger, using gmail, and with thousands of lines of code published in google code. I've long been somewhat uncomfortable about blogger and it's future and the closing of Reader only increases the discomfort. I've even started and worked on a blogging/documenting server several times (I called it 'wanki'), maybe when I get some more time I will revisit it again - i've since learnt better ways to do much of what I was trying to achieve with it. Now if only the fucking NBN would get it's act together and get me that fat pipe ...

PS I borrowed Sun's old trademark just because I could ...

Wednesday, 13 March 2013

Waiting for a free JavaFX

Today in my morning paper break I came across this post complaining about the state of GTK+ development. I knew GNOME was going its own way but I didn't realise GTK+ was coming along for the ride too, although given it's history it shouldn't surprise me. Even from before GNOME 2, Gtk+ was engulfing non-toolkit functionality for the purposes of control and now they've simply merged into one.

To be honest, ... i'm a bit surprised anyone is still writing desktop applications in C anyway. I guess when the primary alternative is C++ ... there's a pretty compelling reason. Unfortunately Java just doesn't seem to have caught on much in the free software world (or 'open sauce'). Historically the reasons are obvious, but a free, stable, and performant version of openjdk has been available for some time now, and both the platform and the tools are better than they've ever been. And in my experience the current JRE runs better on even an 'unsupported' GNU/Linux distribution than it does on Microsoft.

Instead developers seem to be focusing on scripted-glue technologies like Python, which aren't powerful enough to do significant processing without calling a library, and have other limitations like poor thread support - I think I just heard the mid 80's calling, they wondered what you did with the last few decades ...

And where needs require it for the re-use of existing libraries, I've found interfacing with C to be quite straightforward and sane from Java - more sane than just writing the GUI in C.

Here's one interesting data-point, the post above is complaining about maintaining software against a moving target controlled by a corporate interest with other goals in mind. However, I can take 13 year old Java like Dusk, and run it on the current platform - unchanged. Not only are the api's incredibly stable and long-lived, they don't need to be discarded to move forward (and the planned modularisation mechanism will prevent the platform growing forever). Try doing that with a 13 year old gtk+ app - apart from the dependency hunt you're left with unpatched and insecure libraries if you can find them all. Not that Qt would be any better - C++ is a big pain of dependencies which go right down to the compiler and the standard library which makes it even worse, you might have to build your own compiler first. I don't know about Apple, but Microsoft stuff is arguably worse with forced upgrades tied to each version of the ide usually based on flavour-of-the-month experimental api's which silently 'disappear' after a couple of years.

Pretty ironic when Oracle of all companies is producing a more developer friendly platform than a so-called "open sauce" company (or Ubunutu). Actually it's not ironic at all, they're only working on the platform and not (apparently?) competing with their own customers. I guess one problem Sun had is that they always wanted to be the next Microsoft, but Oracle just wants to make money; and although there is correlation they are not the same goal. All these new 'platform' providers seem to be falling into the same trap (except it isn't Microsoft where anyone wants to `go today').

So when JavaFX finally becomes fully free it might be a good opportunity for free software (and "open sauce") developers to investigate a modern, GPU-accelerable, cross-platform toolkit. It still has a way to go, but it's getting there.

Although I don't expect anything to change much as it's unlikely Java will ever be fashionable again, due in part to misinformation but also simply because competitors and developers have a financial interest in it staying unfashionable. I'm sure the Apple and Google mobile platform juggernauts will be consuming increasing developer resources as well with their casinos masquerading as shop-fronts (remember, the house always wins!).

Monday, 11 March 2013

New server stuff

I just managed to get enough of the new server to run in order to login, walk around (anywhere - no walls) and run some simple commands (e.g. 'look').

The greeting is implemented in javascript:

    player.chat("Welcome to the DuskZ test game.");
    player.chat("Copyright(c) 2013.. etc etc fixme.");

    count = player.getInt('logincount');
    if (count == 0) {
       player.chat('Welcome newbie!');
       player.setInt('logincount', count+1);
    }

    true;

I've added arbitrary variables to objects which can be used to track quests and so on, rather than using hidden 'conditions' - I will instead reserve conditions for real conditions (poision, etc).

The code base is just under 3 000 lines of code at this point, but there are still many things left to implement. I do have much of the guts there (albeit untested, or unfinished) including battle engine and mob ai. But most of the commands aren't ported over yet, and the skill/spell system is not sorted out.

All the code is either new or radically altered old code, as I've tried to leverage the object hierarchy to properly abstract away and hide information. e.g. the details of the mathematics of the battle system is now mostly on the Active class, with the Battle class just used to oversee the battles. This factoring of code into sub-classes is proving to be quite lengthy and involved (and probably error-prone).

I also only run one master clock thread now which handles movement, ai, battles, and updates. Rather than having mob ai on another thread. I will have to think of another way to leverage multiple threads, if it is required.

Another big change is the server-client sync mechanism. Rather than cascade changes as they happen - e.g. mob moves, then update every player who can see it straight away, everything is updated at the end of each turn (clock tick). Each Player tracks the visible objects the client should have, and sends deltas if required. This may or may not be more efficient in cpu cycles and ram, but it's a lot easier to understand and debug. It would also let me put all updates into a single message in an 'atomic' way to properly fix the jumping-sprites-when-moving problem. And it opens a few other possibilities too.

So it's turning out to be a pretty big job, but one step at a time ... at least it's nice to have something to run (and crash) after hacking solid for a few days on it.

Update: So I woke up too early this morning (with a bit of a hangover to boot) so I had a quick look at trying to get the tile/movement scripts hooked in.

I decided to try giving map locations symbolic names and use those for both finding which script to run, and referencing inside of scripts. Each map has an alias file.

main.alias:

  alias.49.33=goto-drop-inn
  alias.49.34=drop-inn-porch

do-drop-inn.alias:

  alias.4.15=leave-drop-inn
  alias.4.14=entrance

These could be set inside tiled using properties.

And then the doorway scripts are simply ...

onLocationAction/goto-drop-inn:

  trigger.jumpTo("do-drop-inn", "entrance");

onLocationAction/leave-drop-inn:

  trigger.jumpTo("main", "drop-inn-porch");

As the doorway is currently a tile that cannot be occupied, it also needs a "can move" script.

onLocationAble/goto-drop-in:

    true;

Hmm, whilst the automatic symbol indirection works ok for the action script, it doesn't allow one to re-use scripts or optimise the simple true/false case. Maybe I should just use the alias mechanism for symbolic references, and define script linkage separately.

Update [Thursday]: Still plugging away at it but not enough to warrant a further post. Work is frying my brain a bit (crypto stuff atm, something useful to know about but a bit painful nonetheless) but I'm doing a bit of hacking on duskz most nights, filling out the implementation and clearing out TODOs and FIXMEs.

I got battles working a couple of days ago, together with items and equipment yesterday. Tonight I got most of the rest of the client updates doing - inventory, equipment, stats and status - actually re-arranged the code a bit so client-server sync is in another class, which only sends updates if things have changed and groups them a bit more sensibly (or will do when i'm done). Less clutter in Player which is big enough as it is. So for all that work ... the update mechanism is a lot less chattier than it was previously, particularly at login, which was a desired result. Code is simpler too, and can be made to work with pull clients.

This is the guts of the 'player' level game, so what I will look at doing next is filling this level of functionality out before worrying about the rest of the commands. I need to organise the code a bit too before checking it in.

And debug.

Friday, 8 March 2013

Boolean logic, or the art of making the simple complex

I've been slowly rewriting big chunks of code, i've basically decided to rewrite almost everything apart from the battle system, but it's days may also be numbered.

Things are looking promising because I finally understand why some parts of the code is so complicated where it doesn't seem to need it - it's trying to monitor changes of state every time they happen and then propagate thing to the client. This just isn't necessary, all it needs to do is keep track of 'what i think the client has' (which it does already), and then perform a search of 'what the client should have' at the end of every turn and propagate that to the client. No need to have some convoluted cascade of 'you can now see this, lets set it right now and incidentally use that to track other state at the same time'. Because it's already doing the former anyway to implement the latter ... which also means extra update messages.

But that is not the topic of this post.

There is also quite a bit of convoluted logic which is hard to decipher. For example when an entity tries to follow another one. I will break it into two parts, 'can you do this', and 'do this'.

First, the 'can' test:

    if (lt.isSleeping) {
        return "You can't do that while you're sleeping";
    }
    DuskObject objStore = lt.getLocalObject(args);
    if (objStore == null) {
        return "You don't see that here.";
    }
    if (objStore.isLivingThing()) {
        LivingThing thnStore = (LivingThing) objStore;
        if (lt.getMaster() != null && thnStore != lt.getMaster()) {
            if (lt.isPet()) {
                return "You can only follow your owner.";
            }
            return "You're already following someone. Leave them first.";
        }
        if (Math.abs(lt.x - thnStore.x) + Math.abs(lt.y - thnStore.y) > 1) {
            return "They're too far away.";
        }
        if (thnStore == lt) {
            return "You can't follow yourself.";
        }
        if (!thnStore.isPlayer() && !lt.isMob()) {
            return "You can only follow players.";
        }
        if (thnStore.noFollow || (thnStore.isPet() && thnStore.getMaster().noFollow)) {
            return "They won't let you follow them.";
        }

Here 'lt' is the thing that wants to follow, and 'objStore' and 'thnStore' are the thing it wants to lead it.

The tests in order ...

Sleeping?
Obvious.
Visible in surrounding area?
Obvious.
LivingThing?
Only LivingThings can move and fight.
Already following something?
Can only follow one leader, pets can only follow their owner.
They're on an adjacent location?
Proximity test.
Insanity test.
Obvious.
Player/Pet can only follow Player. But mobs can follow anything.
This was the hardest bit to understand ... blah. I got it wrong a few times and much refactoring ensued.
Honour no-follow setting
Pets are treated as proxies for their masters.

The problem is not so much the amount of code - although this type of logic is littered throughout the code-base - it's understanding the finer points. Just looking at that it's pretty hard to tell who can be leaders - e.g. can you follow a pet.

It does a lot of tests so it can't really be simplified much but maybe it can be made more obvious. I tried implementing it over the 4 Active/Player/Pet/Mobile classes by using the class structure to most of the need for the 'what am i' checks.

First, the code in Active:

public void follow(Active master) {
  if (sleeping) {
        chatMessage("You can't do that while you're sleeping");
        return;
    }
    if (distanceL1(master) > 1) {
        chatMessage("They're too far away.");
        return;
    }
    if (group != null) {
        chatMessage("You're already following someone. Leave them first.");
        return;
    }
    if (this.ID == master.ID) {
        chatMessage("You can't follow yourself.");
        return;
    }
    if (!isCanLead()) {
        chatMessage("They won't let you follow them.");
        return;
    }

This does the generic tests applicable to all classes. I've done some obvious things like add a distance measure function - rather than cut and paste the code everywhere. The isCanLead() is overriden in the Pet to proxy it's master's setting rather than having to have a class test inline.

Player only needs to add an additional test to ensure they're following a Player either directly of via a Pet.

public void follow(Active master) {
    if (master.getType() != TYPE_PLAYER
        || master.getType() != TYPE_PET) {
        chatMessage("You can only follow players.");
        return;
    }
    super.follow(master);
}

Pets can only follow their owner so don't need the "is it a player" test.

public void follow(Active master) {
    if (this.master.ID != master.ID) {
        chatMessage("You can only follow your owner.");
        return;
    }
    super.follow(master);
}

And Mobile just uses the Active method.

I think that covers all the cases and defines the same behaviour? The local-visibility and 'is it a living thing' tests are performed before it gets to this point for obvious reasons.

To me it is simpler, and the class types enforce behaviour at the language level and so are less error prone.

The downside of this is that the logic is now scattered over 4 separate files. Tooling helps to track what's going on, but it still requires some effort. Another problem is that adding such specificity in the class structure means changes are more difficult and perhaps costlier. For some reason I can't recall I nearly added this extra level in the class structure at this point before, I think it might be useful elsewhere (battle code).

And now the 'do' bit:

        if (lt.isPet()) {
            thnStore.setFollowing(lt);
            lt.setMaster(thnStore);
            thnStore.updateStats();
            lt.updateStats();
            return "You are now following " + lt.getMaster().name + ".";
        }
        LivingThing thnStore2 = thnStore;
        while (thnStore2 != null) {
            if (lt == thnStore2) {
                return "You're already in that group.";
            }
            thnStore2 = thnStore2.getMaster();
        }
        thnStore.chatMessage("You are now being followed by " + lt.name + ".");
        while (thnStore.getFollowing() != null) {
            thnStore = thnStore.getFollowing();
            if (thnStore.isPlayer()) {
                thnStore.chatMessage("You are now being followed by " + lt.name + ".");
            }
        }
        thnStore.setFollowing(lt);
        lt.setMaster(thnStore);
        thnStore.updateStats();
        lt.updateStats();
        return "You are now following " + lt.getMaster().name + ".";
    }
    return "That's not something you can follow.";

The pet part is fairly obvious (and again you see the immediate update calls, whereas a 'has changed' indicator would suffice). Although if the player is already following someone, it seems to just overwrite the link pointers - corrupt list?

But the rest is kind of not too obvious - and I suspect buggy anyway.

First some background to explain the loops. The entities use two pointers 'following', and 'master' to create a double-linked list of the 'following' group to which the entity belongs. This is for movement but mainly for battle (and knowing this will help me simplify the battle code too). Although there is a direct ordering relationship, it isn't really used apart from notification - when you follow you're placed at the end of the group.

So the first loop just determines if you're already in the pack. It only checks in one direction though which I think is a bug.

The second loop notifies everyone else in the pack that you've just joined. Again it only checks in one direction, which think is also a bug.

And finally the Active is linked into the end of the group, and stats updated.

I suspect this doesn't work very well in practice as you need to be next to someone to follow them, but if you follow the leader you are actually set to follow someone at the end the a conga line. I think this will immediately break when you move, since you need to be adjacent for movement.

What's worse this confusing ('thnStore'??) and error prone double-linked-list code is littered throughout the code - this snippet came from the command executor, but it is repeated throughout LivingThing, DuskEngine, and Script in several places.

ADT to the rescue!

The first big thing is to just wrap the pack in an ADT. A list is pretty much enough but since it probably needs some policy enforcement I wrapped it in a new Group object (actually I might call it Pack since Group is a bit too generic).

The code then becomes something like below, although i'm still trying to work out what's going on with the pet mechanism - I probably need a separate relationship for that. Actually now I think about it, perhaps a completely separate mechanism should be used for Pets to follow Players.

    if (master.group == null) {
        master.group = new Group();
        master.group.addFollower(master);
    }

    for (Active a: master.group.members()) {
       a.chatMessage("You are now being followed by " + name + ".");
    }

    group = master.group;
    group.addFollower(this);

    chatMessage("You are now following " + master.name + ".");

Which I think we can all agree is a marked improvement.

Whilst writing this I re-designed this stuff several times, mostly trying to understand that one troublesome if statement in the can follow block. I'm sure it isn't finished either - one I delve into the battle code I might find surprises.

Just start from scratch?

I guess I wasn't quite at the top of my game when I looked at this (it's been a long week, apart from 30+ hours on dusk i've done 30+ hours for work) - but it took far too long just to work out what was happening here. Trying to understand someone else's code - even in such a simple and harder-to-abuse language like Java - is pretty much a headfuck. It rapidly gets to the point where starting from scratch becomes an attractive proposition.

The server code less the scripting engine is only about 6KLOC (counting semicolons). If I fully knew what I wanted to do and didn't need to muck around making decisions or writing big blog posts I could easily polish that off in a week or two - and still have long weekends.

Although and i'm pretty sure it can be done in significantly less actual code anyway (but the deeper object hierarchy will add some overhead in locs).

Poking continues ...

Thursday, 7 March 2013

Class heirarchy and stuff

I was looking through the duskz code as it is right now and there are still plenty of messy bits that I want to do something about. In some places there just seems to be more code than there should be to do fairly simple things, often a simple routine is just coded inline and repeated multiple times. And each object implements it's own parser as well as a different format/convention for storing it's state.

Although there is an object class hierarchy for the in-game objects, there is little to no use of the facilities in Java for object oriented programming such as virtual methods, or even inheriting during object initialisation. Apart from the classes there is also class information stored in type fields as well. e.g. a basic LivingThing can be a player, or a pet, an Item can be any type of item from armour to a backpack. Each container includes fields which are only applicable depending on some other field. They then have testing methods to determine what type they are, but it leads to ugly code with lots of manual checking in places. Actually the code looks more like a C design, where the objects are just data containers.

The existing class structure is something like:

DuskObject
  LivingThing
    Mob
  Item
  Merchant
  PlayerMerchant
  Prop
  Sign

Each class either does its own i/o completely, or has it's i/o done for it in a completely separate class. I started an inconsistent stab at addressing this but I think to do it properly will require a common format/parser too. I'm going to stick with a Properties-like format for simplicity (name=value), but relax the requirement for unique keys (to allow multi-valued items).

First requirement is to clean up the class structure. I started at the base class and came up with a new hierarchy, which grew somewhat large by the end. I've also filled out most of the fields - in some cases it was hard to work out just which type of object a given field is restricted to. I gave them mostly unique names with the aim of avoiding clashes as I develop it, although they'll probably mostly stick.

Thing
  Active
    Player
    Pet
    Mobile
  Holdable
    Useable
      Food
      Drink
      Item
    Wearable
      Weapon
      Armour
    Container
  Shop
    Merchant
    PlayerMerchant
  Sign
  Prop

This will let me do things like move the 'get list of sellable items' to a function on Shop, rather than having it appear in multiple places throughout the code, each having to test which type of merchant too.

The code for game logic is also spread across mostly LivingThing and DuskEngine in random ways (some of it my fault by this time). Even things like the game pulse - there are two separate tick threads, for no particularly obvious reason? It might have been created to run enemy a/i on a separate thread, but extra stuff got added later - in any event it should run on a common clock.

I started just investigating the current structure to see how it's used, but I think i'll just rewrite the whole lot, the objects, the i/o & file formats, and well, most of the behaviour code too. The game logic itself is fairly simple so i'm hoping I can hide some of the dumb-but-necessary code like i/o away to expose the simplicity. There wont be much left of the original code apart from the battle logic, and the overall behaviour.

Could keep me busy for a while though ...

Update: I found there was a single weapon - a "runed shortsword", which was also "useable", so I decided to put the useable fields into Holdable instead so anything can be "used", if the game so desires. Non-usability is set by simply not setting hose fields. This could be extended to other types of item, but doesn't seem useful.

Tuesday, 5 March 2013

Layers & multiple maps

Ok just a quick post, but it's just a quick feature ... I needed a break from coding Android all day so i whipped this up.

I added layers to the server, protocol, and client. Only took a couple of hours so I haven't had time to do a very good example, but here's a shot in-game:

And because it isn't clear what's going on since the tiles aren't properly transparent (i.e. apart from appearing over the player, the visual appearance could just as easily been created using tall tiles), here it is in tiled with the opacity of the upper layer changed to show what it includes:

As part of the tiled converter it automatically shrinks each layer to only include the used region (apart from the ground layer), and the server only sends non-empty layers to the client even if they contain sparse data. So for example as the player moves and the 'forest' disappears, the server only sends a single layer of information. It keeps track of which layer is the ground layer so the client can render things in the correct order.

Update: I started hacking a bit later in the evening and got multiple maps working to some limited extent. There are plenty of bugs and various state files are broken and location scripts are still global, plus new issues to deal with like what to do with the blank area around maps, but I just got it to the point where I can walk inside the inn and then out again (through the door). Had to add a new script command to jump to a location in a map, and maps are referenced by their filename.

Outside (64x64, 2-layer map):

Inside (16x16, 1-layer map):

Scripting thoughts

So last night I had a bit of a look at how a javascript thing might work for the game engine. It tempered some of my enthusiasm but I also need to adjust my expectations. Security is a pain, and JavaScript itself turns out a bit ugly, although i can probably do something about that. Strangely the script engine doesn't let you implement new functions for the script? That's probably the weirdest thing about it; you can add objects but not top-level functions. The fact the javascript engine keeps variables around between invocations also makes things a bit more interesting, and not all in a good way.

I had a look at how I might implement a simple existing item: absinthe (it's not a drink i'm a fan of btw so the choice is arbitrary).

The existing item is defined over multiple separate files: the item descriptor and one for each script - 3 total files for absinthe.

Object

Obvious is to implement it as a full object with methods. The object name would match the script name, and all objects could be loaded at once into the script engine.

var absinthe = {
    onUse: function(thing) {
        thing.emote('wails: oh, my head!');
        if (thing.getInte() > 10)
            thing.incrInte(-1);
        thing.remove('absinthe');
    },
    onDrop: function(thing) {
        thing.order("get absinthe");
        thing.remove('absinthe');
    }
};

Drawback here is the scaffolding required. It's almost to the point that you may as well just use Java directly. I will probably do that too.

If the script engine were a proper execution container then things could be very interesting - the state could live inside it too, but I don't want to go down that path.

Script fragments

This is similar to the existing system, separate standalone scripts define each action. They are referenced via names in the objects.

absinthe_use:

    thing.emote('wails: oh, my head!');
    if (thing.getInte() > 10)
       thing.incrInte(-1);
    thing.remove('absinthe');

absinthe_drop:

    thing.order("get absinthe");
     thing.remove('absinthe');

It has similar drawbacks and advantages to the existing system - lots of little files to manage, separated logic, but also isolation and simplicity. It does allow for pre-compiled scripts though.

Script of functions

Here the script just defines top-level functions. It makes it a bit easier for the user to develop as they don't need to include all the scaffolding.

var onUse =function(thing) {
    thing.emote('wails: oh, my head!');
    if (thing.getInte() > 10)
        thing.incrInte(-1);
    thing.remove('absinthe');
};
var onDrop = function(thing) {
    thing.order("get absinthe");
    thing.remove('absinthe');
};

This approach has the drawback that the script must be parsed and executed separately, and must be parsed and executed every time.

Scripted actions

A compromise between the first two is to have a single script but also pass the script action to the script and let it decide what to do. e.g. use a switch statement. i.e. lets one put all behaviour in one file, but 'simplifies' the scaffolding.

switch (action) {
case 'use':
    thing.emote('wails: oh, my head!');
    if (thing.getInte() > 10)
       thing.incrInte(-1);
    thing.remove('absinthe');
    break;
case 'drop':
    thing.order("get absinthe");
    thing.remove('absinthe');
    break;
}

Problem is that it doesn't really simplify the scaffolding, although it allows for precompiled scripts.

Thoughts

One problem is i'm thinking about the scripting system from the wrong viewpoint - more of a library of potentially compiled functions rather than thinking of it as scripts. I need to shift my thinking because they really should just be scripts, and not self-contained applications. If I support Java classes to define behaviour as well then efficiency of the scripting language itself isn't of a primary concern.

The fact that Java can't directly define functions sucks a bit - for example the "thing." prefix to every call. But I guess i can hide that with some simple global functions I inject on my own which hide the details for the main target object.

At this point i'm leaning toward the second option - i.e. much the same as the existing system. But I will look at some techniques for simplifying their configuration such as convention-based lookup rather than needing a pile of settings for each object. A convention-based mechanism would let me support multiple mechanisms without needing to change the file formats.

I might need to let this stew a bit for the moment, and whilst that is coalescing have a look at the multi-map/layer stuff which will be more fun to hack on too.

Saturday, 2 March 2013

Binary Protocol

So I mowed the lawn and swept the yard and did some of the things i've been neglecting for a few weeks. Although now with no milk in my coffee I wasn't in need of the shop ...

By late afternoon i was a bit bored so I had a look at the binary protocol work I had started.

I decided to change tack rather than use the manual serialisation I had started. It was a variant of Externalizable, which although the fastest mechanism requires the client and server to have total knowledge of each end. Being java there are other possibilities such as sending the decoding library to the client at run-time, but I wanted to keep things simple.

I started creating general purpose containers because I saw I was just re-typing the same stuff again, and there was a fair bit of re-use possible. After a few iterations I refined it and came up with something i'm pretty happy with. Basically it's a typed and tagged data structure which can be encoded in such a way that it can only be decoded one way. It is a bit like BER encoding but stripped to the bare-bones without any of the complication only a committee and the requirement to solve every possible conceivable future use can provide. But I can also include custom objects for efficiency or convenience sake.

So this defines the syntax - defining, encoding, decoding, and access.

But the semantics are completely separate and are defined by the application. This is very important as for example it allows me to create messages with variable content or new messages that the client/server can ignore if they wish - without breaking the protocol exchange.

DuskMessage

So I basically have a thing called a dusk message, and every dusk message has a type and a name. The type defines the container, and the name is application specific. They are encoded on the wire as 2 separate bytes, limiting each to 256 which is more than enough for an application-specific protocol.

DuskMessage {
    byte type;
    byte name;
};

This on it's own is enough to pass simple notification messages that require no arguments. Then there are the simple types, which includes byte, short, int, long, float, and string.

ValueMessage<T> {
    DuskMessage;
    T value;
};

And I created a couple of list types, one is just a list of strings, and the other is a list of DuskMessages (i.e. 'any's) which is how more complex datatypes are created without requiring custom types.

ListMessage {
    DuskMessage;
    List<DuskMessage> value;
};

(etc, you get the idea)

List are just encoded in the obvious way - a length (short), followed by length-items of the DuskMessage. I've got some simple accessors like getByte(int name) and so on for easy client use.

There are also some custom types such as the map and entity updates which occur very frequently which should benefit from the reduced protocol and run-time overhead. I just hard-coded these into the system but with a small amount of work it would be possible to turn it into a reusable and extensible library where customisations were provided by the application code.

And finally I created another copy of all of these which also contain an ID - for messages which are intended for a specific Entity. They don't occur very often but it provides a bit more flexibility and efficiency for a small coding overhead.

Of course when I went to use it there were bugs in my code so I then wrote some protocol dumping routines which dump to a human-readable format. As a human never needs to read it other than for debugging there isn't much point sending a human-readable format for any other reason.

DuskProtocol

The semantics are then defined by the DuskProtocol interface. This is just a pile of integer constants which define the names to use for objects. Any class in the client or server that wants to use the constants just 'implements' the interface to gain access to them. There are two sets of constants.

MSG_*

The message constants define the message type and are set on the name of the highest level message. For simple types this is all they contain but the list can contain others. I decided to make these globally unique, although they could also be made unique-per-container-type. I tried that at first but it just got too messy and complicated to manage to make it worth doing. The more I worked on this the simpler it got, which is always a nice side-effect.

FIELD_*

The field constants define fields in compound (ListMessage) messages. These only have to be unique to each message type. I initially tried to work on creating a global namespace for them where I could pick and choose what to include but that just complicated things again and all it did was save some source-code space and one-off typing. I will move to per-message or at least related-groups grouping, and start each group at 0. Otherwise it's just unmaintainable.

So for example the player info update which is the most complex message (all the str/dex and so on) has some constants like:

    public final static int MSG_INFO_PLAYER = 15;
  ...
    public final static int FIELD_INFO_CASH = 0;
    public final static int FIELD_INFO_EXP = 1;
    public final static int FIELD_INFO_STR = 2;
    public final static int FIELD_INFO_STRBON = 3;
  ...

Currently i'm sending all of them every time - as the previous version did - but I can cut out things which aren't changes now, without having to change the client code (if i implement it the right way).

I did have a thought of having the server send some meta-data in an initial protocol exchange (say, allowing symbolic names for all messages and fields), but what the fuck for? It's already engineered enough to work, it doesn't need more than that.

So why bother/why not use another library/etc?

Firstly i find this kind of problem solving fun - it's just a never-ending puzzle trying to aim for a good solution knowing there are always trade-offs.. I've also done it quite a few times in the past - from LDAP which uses BER, to object serialisation in Evolution/camel, to using BerkelyDB, and plenty of other places besides. It just keeps cropping up and although I know this wont solve every problem it works for this one.

What I don't find particularly fun is learning how some behemoth general-purpose library works in the first place, let alone learning it's bugs and limitations. Even something as "trivial" as json requires some rather large library, and with all that it doesn't even interoperate reliably without fucking around.

For the application itself, as mentioned having a protocol with atomically-decodable messages allows for extension without completely breaking the protocol.

Having dynamic messages means I don't have to write code to handle dynamic messages into a custom protocol; it's already provided. Field access requires code for each field, a general list can be iterated - potentially reducing code required too. All the parsing and i/o handling is done and all I have to do is use the objects or their accessors.

It also means the encode/decode is in one place, and not scattered throughout the code as printf statements. So I can also force encapsulation at each end by forcing communications through this choke point. Which provides all sorts of benefits ...

... such as if i should decide to change the wire protocol in the future, or more likelty to support "web friendly" text-mode protocols.

In DuskZ

So I didn't just design the protocol syntax last night (ok it stretched into the night a bit - nothing on tv), I implemented it and changed DuskServer and DuskZ to use it. And debugged it enough to the point that you can log in and play some of the game ...

At this point the client is still talking the original protocol outward (i.e. a command line!), but I will need to change that to support some required features. For example scripts are able to ask the user questions but at the moment do some really nasty hackery like telling the client to pause, then ask the question, then flush the incoming stream, then hope it got it right. That can all go.

And the server protocol still has the same basic structure with only some minor tweaks.

There are other messages which could be combined in a more general 'update some object' type message. e.g. set range could become an optional field in an 'update player' message (although it looks like range isn't sent anyway). And others that could be combined for other reasons, such as wanting an atomic change of state, such as entering a battle - rather than sending a bunch of messages for each individual entity. Fortunately with the simple design I can just as easily embed messages inside a list as a field.

I also had a think about the auth protocol - right now it just creates a new user if you didn't exist, but it might be useful to get the player to confirm their password, and also go through the "new user" stuff like choosing a race all in one go.

Before i commit I want to clean up some of the naming conventions (netbeans refactor tools have had a work-out this week, although they're not bug free they work surprisingly well with 'broken' code), see about moving some of the fields to grouped messages, and change the login stuff. The login stuff goes a bit deeper so will require more work.

Update: So I managed to do most of what I intended - boy it was a lot more work than I thought. Getting the login working at both ends took a bit more thinking than I expected, but I managed to fit in a query mechanism that lets the game query the user for arbitrary values (currently: an item from a list) and the same convention is used during user creation to ask for whatever is needed. It took a while to work out how the current code needed it all to work too, and then I merged some of the message types (e.g. resize map + update images became map init), and then had to fix the client. It isn't quite done but it's enough to login and play around a bit.

Here's a protocol dump of the client creating a new user. It still goes through the same single login window I did before, but the client behaviour can be altered quite freely without changing the protocol. e.g. it could instead show 'unknown user', with a button for 'create' which runs a setup wizard locally, and then creates the user at the end.

sending: MSG_AUTH ListMessage name=0 value = {
        StringMessage name=3 value=x username
        StringMessage name=4 value=x password
}
state=Username: MSG_AUTH ListMessage name=0 value = {
        ListMessage name=2 value = {
                ListMessage name=0 value = { name = name of response string below
                        StringMessage name=0 value=Choose race
                        StringListMessage name=1 value= {
                          'lizardman'
                          'ork'
                          'indian'
                          'demon'
                          'elf'
                          'darkelf'
                          'halfling'
                          'dwarf'
                          'human'
                        }
                }
        }
        EntityIntegerMessage id=-1 name=0 value=3
        StringMessage name=1 value=Insufficient information to create player.
}
sending: MSG_AUTH ListMessage name=0 value = {
        StringMessage name=3 value=x
        StringMessage name=4 value=x
        ListMessage name=2 value = {
                StringMessage name=0 value=human name = name of choice above
        }
}
state=Username: MSG_AUTH ListMessage name=0 value = {
        EntityIntegerMessage id=195 name=0 value=0
        StringMessage name=1 value=Login ok.
}
state=Ready: MSG_CHAT StringMessage name=7 value=DuskZ Server 3.0 dev ...

So fairly clean and concise, and no screen-scraping required. And the query can include as many questions as needed, ... or even more complex content with some appropriate glue (such as a web form using the WebView?).

Friday, 1 March 2013

The brain dump

Last night I was pretty exhausted - the neighbours had some bricks delivered around 7am which woke me too early after what was a pretty lousy night of sleep. I got up with the idea of turning off the watering system (starts at 7am) after it had done the pot plants and kind of forgot about that and ended up hacking for nearly 15 hours straight.

Ok so it was probably a bit obsessively intensive, but i'm pretty surprised I still get such a level of buzz out of hacking code after 25 years of doing it for fun. In terms of effort and intensity, it could have been any other xmas holiday from any of those previous years. On the other hand i'm writing more and better code than ever before because the platform is fuller than ever before. The only downside is that I can't keep up the intensity for as many consecutive weeks as I used to and end up with aches and pains from sitting still for so long.

But as I intended to take a bit of a break for a few days I thought i'd jot down some feature ideas, todo's and wishlists in my engineering notebook (a paper one) while the ideas were still hot.

As I wrote things down my memory started to vanish very rapidly - as if waking from a dream. I got the main feature points down fairly completely but I was pretty stumped at the todos. Although only minutes before I had a mind full of all those little refactoring, renaming, and tweaking ideas for improving the codebase, by the time I went to write them down I drew a near blank. I've experienced something similar before when I left the Evolution project; as I wrote all my knowledge down all the anxieties from keeping track of the 'known bugs' and 'future plans' in my head just melted away. But it wasn't quite so abrupt, although both instances were very cathartic. To be honest i'm still surprised I can keep so much in my head for weeks at a time - when I often forget where I put a screwdriver only a minute prior.

So it was a very good idea to write it down and let me get a good nights sleep. And although my brain is now bereft of most of the details I managed to write most of them down, and i'm sure the others are in the source as FIXMEs or will come back when I get into it again - if they were important.

Scrollbars!

So apparently one of the side-effects of the touch-interface 'revolution' is the abolishment of scrollbars.

Well fuck that for a poke up the arse with a glowing poker.

Even before the latest firefox came along with a serious bug on my *ainol tablet (I love it, even the the logo includes a diagrammatic anus) which makes it almost unusable - flicking the screen normally-but-not-always makes it flick in the wrong direction - this no-scrollbar idea makes the device itself very clumsy for reading any web pages more than about 3 screens high. The android browser is just the same. And given some of the pages I read are dozens of pages long, i'm faced with a usability nightmare.

Just bring the friggan scrollbars back ...

It's probably related to another annoying "web 3" feature - infinite scrolling screens that load on demand. They might solve a technical problem and have a bit of 'wow' factor, but they are not a usability improvement. And they kill some pretty important features we've come to expect like searching within a page. But I suppose we have to get used to this sort of dumbing down of the internet - such a big chunk it is all about advertising wrapped in entertainment; not learning, education, or information.

While i'm ranting about firefox mobile i find it pretty much a pain to work with even without that killer bug - there are too many small buttons too close together, the tab system is really annoying, as is the click-to-title-bar-to-go-to-another-screen thing, with the popup keyboard always getting in the way. It has a habit of zooming far too often when I go to move around - a mis-fingered 'scroll' turning into a 'press'. If we already have a two-finger zoom, why not bloody stick to it? I just don't see the point of overloading gestures to the point of confusion. Well here's two fingers for the designers: Y, and while i'm at it: !). Mouse/keyboard users have other mechanisms for this.

The whole point of "touch" is it is supposed to be natural and "intuitive", but if you have to concentrate hard every time you try to press on something it has lost any edge it had.

I don't really know the answer for the tiny-button-problems as it would need some serious thought but for all the serious thought the UI designers have put into the current offerings they still have a distance to go. The only practical thing I can think of from the top of my head is an icon/menu bar which POPS onto the screen (doesn't slide, doesn't fade, doesn't push the content over to one side, doesn't fucking animate) and has nice big buttons for the most useful functions. You know, like the microsoft 'charms' thing, although it sounds like they overloaded 'gestures' to the point of absurdity (i'd really like to know what that dickhead in the 'clean up the party' ad is supposed to be doing when he draws a circle and a line on the photo near the start). Gestures suck because they're completely non-discoverable and require a lot of practice to get consistent with.

But pressing inside a page should be reserved for links or focus. Not for zooming-sometimes-in-sometimes-out. It's just too easy to do it by mistake.

Inflexion points

Hmm, and on a completely unrelated note, I think i've hit one of those inflexion points where I suddenly change a food forever. It's getting close to the point where my coffee goes black, strong, sugarless. I use a Baletti stove-top coffee maker which makes great coffee easily and cheaply and with the right coffee, amount of milk and temperature it's pretty near to a decent 'bought one', but it's a bit hit and miss. Yesterday I ran out of milk and although I usually can't stomach the black coffee I managed ok. About the time I started using one of these machines my coffee went sugarless - I can't stand even a small amount of sugar now. Although a little sugar and milk is still needed in tea, but I don't drink that as often.

It reminds me that one day after having had cornflakes every morning for breakfast for years I suddenly decided it was too sweet and decided I vaguely remembered vita-brits being pretty nice. Can't stand cornflakes now, although I could probably go some sugarless vita-brits once in a while. Although these days breakfast is usually left-overs, coffee, toast, or often just reading my morning round of blogs and organising my on-screen workbench.

Hmm, my nice neighbours are getting some work done in their yard next to this room, I should go mow the lawn or something before I get rooted to the chair for another day.