No, that’s not what I meant. Let me try to explain.
The client it self has two verbs:
query: A request to the back-end for information. The state of the game will not be changed by that request - it will provide information for the client to render certain things.
action: A request to the back-end to perform some action. Thus can change the state of the game, and the back-end will always give back a status code, such as accepted or declined.
Now, that seems rather limited, and indeed it would be if that was the only thing going.
If we consider that pieces (I’m staying with the current Vassal lingo here) - and perhaps other game components - have traits (again, current Vassal lingo - could also be though of as capabilities), then those traits will have one representation in the client, and another in the back-end.
Traits then formulate their own “verbs” - or sub-verbs to the client verbs query and action.
The query sub-verb would come into play when a piece with the particular trait needs some information in the client.
For example,
- When a piece is selected in the client, then the client asks the piece to form a query of all its traits.
- The client then ships that off to the back-end
- The back-end delegates the query to its representation of the piece.
- The back-end piece delegates the sub-queries to its back-end representation of its traits.
- Each trait then formulates an answer to the query
- The piece collects the answers, and hands that off to the back-end
- The back-end then ships back the answers to the client(s)
- The client gives the answers to its representation of the piece
- The client piece delegates the answers to its traits.
An action sub-verb is more selective, in that it typically will involve a single trait.
For example,
- An action is chosen from the pieces context menu
- That action is tied to a specific trait, which sees that the action is performed
- The trait then formulates a request, and asks the client piece to do that action
- The client piece asks the client to send the action request to the back-end
- The client ships off the request to the back-end
- The back-end gets the request and delegates it to its representation of the piece
- The back-end piece delegates the request to its back-end representation of the trait
- The trait checks if the action is valid, and if so performs it, and thereby changes the state of the piece. It can formulate an accept reply that encodes that state change. If the action isn’t valid, then it formulates a decline reply, possibly with some reasoning attached.
- The reply from the trait is handed off to the back-end piece
- The back-end piece gives the reply to the back-end, which then ships that off to the client(s)
- The client(s) receive the reply, and then delegates that to its representation of the piece
- The client piece then hands the reply to its trait
- The client trait then does the necessary updates in the client.
Let us take an example. Suppose we have the trait Movable. On the client it’s code could look like
from vassal.client import Trait
from vassal.client.trait import Query, Action
class Movable(Trait):
def form_query(*args,**kwargs):
...
return Query('possible-destinations',...)
def process_query_response(response):
...
def form_action(*args,**kwargs):
...
return Action('move',source,destination,...)
def process_action_reponse(response):
...
On the back-end, the trait could look like
from vassal.backend import Trait
from vassal.backend.trait import Information, Result
class Movable(Backend):
def query_response(query,*args,**kwargs):
....
if not Game().is_in_movement_phase():
return None
,,,,
return Information(possible_destinations)
def action_response(action,*args,**kwargs):
check = Game().is_move_legal(action.source, action.destination, piece):
if check.reject:
return Result(False, check.reason)
....
return Result(True, new_state)
When a piece is selected, then the Movable trait will formulate a query to get the possible moves of that piece. The back-end responds with some information, which could be None.
When a piece is moved in the client, then the Movable trait will formulate an action to move that piece. The back-end then checks if the move is legal, and if it is, updates the state of the piece. The back-end then replies with the new state of the piece. If the move isn’t legal, then the back-end gives back a response which states why the move wasn’t legal, so that the client may display that to the user.
On query, an Attacker trait could ask the back-end for possible targets. On action, the Attacker trait could initially declare a combat, secondly ask for a resolution of that combat. Note, when a combat is resolved it may entail waiting for the users to implement the result. Thus, the back-end may need to enter a state where only certain actions (for example a retreat) are allowed.
In terms of logging, only action verbs need to be logged. query verbs are specific to the session, and will not need to be logged. Also, since an action may change state, it means that the result would likely need to be pushed to all clients.
I hope this makes it a bit clear what I had in mind.
The point of this, is that traits is what defines what is possible, and that traits can be as complex as the module developer would like them to be. A module developer could for example provide a factory of traits a la
class TraitFactory:
def make_client_trait(name,*args,**kwargs):
if name == 'MyMovable':
return mymodule.client.traits.MyMovable(*args,**kwargs)
def make_backend_trait(name,*args,**kwargs):
if name == 'MyMovable':
return mymodule.backend.traits.MyMovable(*args,**kwargs)
Both the client and back-end will, when it reads in the piece definitions - say
{
"pieces": [
"a": {
"traits": {
"movable": {
"type": "MyMovable",
"parameters": {
"allowance": 4,
" mobility": "tracked"
}
},
...
},
...
},
...
],
...
}
ask known trait factories to create their representation of the trait.
Thus, a module developer can encode the games rules into the traits (in conjunction with some game instance).
Vassal can of course provide some basic traits which does very little, but which can form the basis (base class) for more elaborate traits.
Yours,
Christian