Skip to content

Instantly share code, notes, and snippets.

@takkaria
Last active August 29, 2015 14:09
Show Gist options
  • Save takkaria/3f7cb76462c4e233d004 to your computer and use it in GitHub Desktop.
Save takkaria/3f7cb76462c4e233d004 to your computer and use it in GitHub Desktop.
angband core-ui split design doc
Angband core-UI split
=====================
This document rather hopefully describes a methodology for continuing work on the split. It does so instead of specifying particular things that need to be done in the hope that it's more illuminating and useful for others.
12 step plan:
1-3. Accept a higher power and its providence over the following task.
4-6. Split UI stuff into ui* files; make sure that only ui-* files include ui-* or z-term headers
7-9. Write tests that use the command & event interfaces to do stuff and get them passing.
10-12. Make the game UI-driven instead of the UI game-driven.
This isn't a linear plan and the different elements are all at least a bit interdependent. The work of 4-6 and 10-12 is intertwined and writing tests as part of 10-12 will probably help drive the right solutions in parts 4-6. You can also invoke the deity anytime (press 'p').
Notes on points 4-6
-------------------
This is the basic point of the split. The game core shouldn't be relying on any particular UI. We're quite far along with this bit already.
A few things which will definitely need sorting for this to happen:
* some kind of "panel tracker", a generic way of subscribing to updates within only a certain part of the dungeon, and also so the game knows what is being displayed (e.g. in target.c and for 'offscreen' messages) - and the textui updated to make use of it
* some kind of "flush input / display" signal to replace message_flush() / flush() - this can probably be implemented as a new EVENT_ type.
* the get_ functions currently in ui-input.c need to be replaced by hooks in maybe a new file, input.c, and then the textui needs to claim those hooks. the idea is not to get rid of all uses of the get_ functions in the core code as that would be really complex. where possible (e.g. within a command's execution) the cmd_get_* functions should be used instead, though.
* the store interface is currently invoked in move_player(), which is part of the core. Instead (and this probably requires some of what is detailed in the notes for points 10-12) the text ui should notice that the store has been entered (maybe because of an EVENT_STORE message or similar) and throw up the display.
This list is definitely not exhaustive; it's just the stuff I've run up against in the recent past.
Notes on points 7-9
-------------------
This is definitely entwined with points 10-12. The idea here is that if you can write tests to exercise every bit of command-based functionality, then we find out pretty fast which bits of code rely on UI accesses. Because we don't initialise z-term or any of the ui- stuff in tests, any code that relies on them will crash straight away. So writing a test and then getting it running is a really great way to progress the split.
And at the end of it we get a pretty exhaustive testsuite - hooray. That's what I call the law of intended consequences.
Notes on points 10-12
---------------------
The idea of the command interface (as yet unachieved) is that you can drive the game entirely using it. It should be as simple as e.g.
angband_init();
savefile_load("Tester");
cmdq_push(CMD_MOVE); set_arg(direction, left); cmdq_execute();
cmdq_push(CMD_MOVE); set_arg(direction, left); cmdq_execute();
Note that at present this won't result in a usable game, because the code that passes game time (and moves monsters, regenerates etc.) is thoroughly entwined with the current UI (in dungeon.c). The idea would be that after each command execution, the game then checks to see if energy was used, and runs another turn before it passes back control to the caller.
This places control of the game and speed of execution in the hands of the UI. This is the opposite of what happens at the moment, where the game runs a loop and polls the UI for updates from inside it. The present approach is not amenable to testing.
In short:
* AT PRESENT: The UI starts the game using play_game(). play_game() only returns when the game is over. The game polls the UI for commands.
* AIM: The UI waits for a command, and sends it to the game. The game gets the command and executes it, then processes monsters and other game updates, and returns.
At the moment I'm sort of skirting around the outside of this problem in the hope that by crumbling the edges, the big task will become easier. The best way to work on this is to write tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment