# V4 Move Processor prototype

**URL:** <https://forum.vassalengine.org/t/v4-move-processor-prototype/90343>\
**Category:** Development\
**Tags:** v4\
**Created:** [July 20, 2026, 10:39am UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343 "2026-07-20T10:39:24Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![uckelman](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/uckelman/32/10020_2.png) [@uckelman](https://forum.vassalengine.org/u/uckelman)\
**Post date:** [July 20, 2026, 10:39am UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/1 "2026-07-20T10:39:24Z")

</div>

I mentioned in the most recent [development update](https://forum.vassalengine.org/t/development-plan-2026-07-16/90330) that I’m working on a move processor prototype, and I wanted to give a brief overview of what that is.

One can divide a game into the state and the visual representation of the state. The game state consists of such things as the identities, locations, and properties of the pieces. The visual representation of the state is the what you see on your table or on your screen. The game state is what matters formally, for playing the game according to the rules. The visual representation is for humans (players and spectators), to aid in interacting with the game state.

A system handling changes to the game state only need not be concerned with the visual representation at all. This is helpful, as visual representations—images—are frequently large in comparison with state data. A small piece image can be several KB of compressed data, while the representation of the same piece in the game state is unlikely to be more than a few hundred bytes. Our existing game server works this way. Only state changes are transmitted, no image data. It’s an efficient design, and one which I intend to keep.

What is in the game state—for any game? The games we’re interested in are on paper and turn-based. Some properties of what we need to represent come from the physicality of the cardboard, such as the location of pieces with respect to each other, how the pieces are stacked, which sides are up, how the pieces are oriented; other properties come from what is printed on the cardboard, such as unit types, strengths, movement allowances, etc. The properties which are due to the pieces being physical objects are also the ones which affect their visual representation; this is not a coincidence.

The possible state changes make a short list: create, destroy, clone, move, splice, flip, rotate, set property, unset property. Here “object” refers to a game object, such as a piece, a card, or a map.

- create: Create an object
- destroy: Destroy an object
- clone: Make a copy of an object from another object
- move: Move an object from one location to another
- splice: Move an object out of or into a stack
- flip: Turn an object over to a differnent side
- rotate: Rotate an object about its center
- set property: Set a named property with a given value on an object
- unset property: Remove a named property from an object

(The difference between move and spice is that in a move, all objects stacked above the moving object remain in place above the moving object, whereas with splice, the moving object is removed from any stack it is in and inserted into another stack. Move and splice could be handled jointly; for technical reasons, it may be more congenial to treat them separately.)

These state changes are sufficient for handling the moves in any printed game. A move processor—a program which can take representations of these state changes as input and use them to update the game state—is an essential component for V4, and is what I’m prototyping now.

---

<div class="post-metadata">

**Author:** ![akulkis00](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/akulkis00/32/11366_2.png) [@akulkis00](https://forum.vassalengine.org/u/akulkis00)\
**Post date:** [July 20, 2026, 1:54pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/2 "2026-07-20T13:54:59Z")

</div>

Thanks for saying this.

My mental model of vassal has long been that it holds, represents and stores game states, and gives the viewer ways to manipulate the state of a game. So thanks for confirming this.

---

<div class="post-metadata">

**Author:** ![cholmcc](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/cholmcc/32/8319_2.png) [@cholmcc](https://forum.vassalengine.org/u/cholmcc)\
**Post date:** [July 20, 2026, 2:26pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/3 "2026-07-20T14:26:54Z")

</div>

> [@uckelman](#):
>
> The possible state changes make a short list: create, destroy, clone, move, splice, flip, rotate, set property, unset property.

Consider the states such as _selected_, or _query_. For example, the front end may make a _query_ to the back-end as to which actions are permissible, or which moves are allowed, in the current game, piece, … state.

Yours,  
_Christian_

---

<div class="post-metadata">

**Author:** ![uckelman](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/uckelman/32/10020_2.png) [@uckelman](https://forum.vassalengine.org/u/uckelman)\
**Post date:** [July 20, 2026, 2:52pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/4 "2026-07-20T14:52:19Z")

</div>

> [@cholmcc](#):
>
> Consider the states such as _selected_, or _query_.

Those are neither part of the game state nor state changes.

I’m not saying they’re unimportant, only that they’re not relevant for a move processor.

---

<div class="post-metadata">

**Author:** ![cholmcc](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/cholmcc/32/8319_2.png) [@cholmcc](https://forum.vassalengine.org/u/cholmcc)\
**Post date:** [July 20, 2026, 9:16pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/5 "2026-07-20T21:16:30Z")

</div>

> [@uckelman](#):
>
> > [@cholmcc](#):
> >
> > Consider the states such as _selected_, or _query_.
> 
> Those are neither part of the game state nor state changes.

In some sense, the client shouldn’t have specific representations of _flip_, _rotate_, _destroy_, _clone_, etc. Instead, the client queries the back-end for the current state of the piece, which then replies with a JSON object. It is then up to the client to reflect that state to the user.

For example, a piece may have a `Rotatable` “trait”. Both the back-end and the client will know that the piece has that trait. However, the back-end will have code that sets, checks, etc. the current rotation, while the client will have code that can render the piece rotated. When the client requests the state of the piece, then the back-end will reply with a JSON object that contains something along the lines of

```auto
{ 
  ...
  "states": [
     {
         "trait-type": "Rotatable", 
         "name": "Rotation",
         "angle": 60, 
      },
     ...
  ],
  ...
}

```

The client `Rotatable` code will see this and render the piece rotated by 60°.

In that way, a module developer can supply back-end and client code for new traits. For example, a trait could be `Movable`, and on a query to the back-end, the back-end code will calculate possible moves and return those, taking the game specific rules into account. The client can then visualise those possible moves.

In fact, I think the protocol between client and back-end should be fairly simple:

- A trait can specify that when the piece is selected, then a query should be sent to the back-end. For example, the above `Movable` trait could send the query `can-i-move`, and the back-end determines it can, then it sends back a reply which the client `Movable` code then visualises. A similar thing could happen for an `Attacker` trait.
- A trait can specify certain actions. For example, if the `Moveable` trait was told that it can be moved, then an action could be `move` with parameters such as source and destination. The back-end can then decide the new state of the piece and reply with that, as well as some flag that says whether the action was completed or not. The client `Movable` code should then visualise the result of the action (including, for example, an error dialog).

The core code can provide simple traits, for example `AlwaysMoveable` as well as stubs that can be fleshed out by the module developer - say `CheckedMovable`.

Of course, most queries should only have responses that contain the needed or updated states - not necessarily the full state. Likewise, a `Moveable` trait need not reply with _all_ possible paths when asked for legal moves, but rather a list of possible destinations.

Yours,  
_Christian_

---

<div class="post-metadata">

**Author:** ![uckelman](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/uckelman/32/10020_2.png) [@uckelman](https://forum.vassalengine.org/u/uckelman)\
**Post date:** [July 21, 2026, 10:05am UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/6 "2026-07-21T10:05:28Z")

</div>

> [@cholmcc](#):
>
> In some sense, the client shouldn’t have specific representations of _flip_, _rotate_, _destroy_, _clone_, etc. Instead, the client queries the back-end for the current state of the piece, which then replies with a JSON object. It is then up to the client to reflect that state to the user.

What I’m getting from this is that you’re claiming that the client shouldn’t have any verbs to send to the server. Without that, the client would have no way to effect changes to the game state, so I’m sure I’m misunderstanding what you’re saying and it won’t be fruitful to comment further before I do understand.

---

<div class="post-metadata">

**Author:** ![cholmcc](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/cholmcc/32/8319_2.png) [@cholmcc](https://forum.vassalengine.org/u/cholmcc)\
**Post date:** [July 21, 2026, 9:23pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/7 "2026-07-21T21:23:44Z")

</div>

> [@uckelman](#):
>
> What I’m getting from this is that you’re claiming that the client shouldn’t have any verbs to send to the server.

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

```auto
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

```auto
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_

```auto
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

```auto
{
  "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_

---

<div class="post-metadata">

**Author:** ![repeat83](https://forum.vassalengine.org/letter_avatar_proxy/v4/letter/r/d07c76/32.png) [@repeat83](https://forum.vassalengine.org/u/repeat83)\
**Post date:** [July 22, 2026, 12:50pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/8 "2026-07-22T12:50:33Z")

</div>

Hi. I’d like to ask: what is the core difference between ‘flip’ and ‘rotate’? Essentially, it’s the same thing, just in different planes. Wouldn’t it make more sense to specify 3D coordinates and rotation angles along three axes? Even if one of the coordinates and its rotation angle is always 0 right now, this approach won’t limit the format from using a 3D client in the future.

Then, of course, any event simply passes through registered event handlers (for checks, transformations, etc.), potentially even with a defined priority.

An action doesn’t need to know beforehand whether an object possesses a specific property. The action just takes the object (conditionally a JSON) and performs the manipulation if the property is present. Ensuring the property exists is the responsibility of the module developer when creating objects.

For example, a movement action simply receives an object, checks its properties list for the required capability from the ‘capabilities’ array, and then executes the manipulation.

```auto
{
  "id": "token_knight_01",
  "name": "Knight Token",
  "type": "token",
  "transform": {
    "position": {"x": 10.0, "y": 0.0, "z": 5.0},
    "rotation": {"x": 0.0, "y": 0.0, "z": 90.0}
  },
  "capabilities": [
    "movable",
    "rotatable_z_only",
    "non_stackable",
    "flippable",
    "snappable_to_grid",
    "selectable"
  ]
}

```

---

<div class="post-metadata">

**Author:** ![uckelman](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/uckelman/32/10020_2.png) [@uckelman](https://forum.vassalengine.org/u/uckelman)\
**Post date:** [July 22, 2026, 1:06pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/9 "2026-07-22T13:06:07Z")

</div>

> [@repeat83](#):
>
> what is the core difference between ‘flip’ and ‘rotate’? Essentially, it’s the same thing, just in different planes.

Flip isn’t an operation in 3-space at all. It sets which face of a piece is visible. Pieces can have more than two faces. E.g., consider a unit that has three steps or a marker with digits 0-9 on it. In a physical game, those would be multiple pieces, but it’s convenient to be able to condense each of them into a single piece in a digital format.

---

<div class="post-metadata">

**Author:** ![repeat83](https://forum.vassalengine.org/letter_avatar_proxy/v4/letter/r/d07c76/32.png) [@repeat83](https://forum.vassalengine.org/u/repeat83)\
**Post date:** [July 22, 2026, 1:23pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/10 "2026-07-22T13:23:16Z")

</div>

So you mean it’s about ‘faces’, and essentially a ‘flip’ is just switching faces from 0 to 1 or 1 to 0.

It turns out, if we create a ‘flip’ action, add it to the context menu, and apply it to the object below, it will sequentially cycle through the first two faces. At the same time, we could implement a ‘select face’ action in the context menu — with a label like ‘Health’ — to give users the ability to choose a specific face directly.

```auto
{
  "id": "token_knight_01",
  "name": "Knight Token",
  "type": "token",
  "transform": {
    "position": {"x": 10.0, "y": 0.0, "z": 5.0},
    "rotation": {"x": 0.0, "y": 0.0, "z": 90.0}
  },
  "capabilities": [
    "movable",
    "rotatable_z_only",
    "non_stackable",
    "flippable",
    "snappable_to_grid",
    "selectable"
  ],
  "current_face_index": 0,
  "faces": [
    {
      "name": "Knight (Healthy)",
      "asset": "assets/images/knight_full_hp.png"
    },
    {
      "name": "Knight (Wounded)",
      "asset": "assets/images/knight_wounded.png"
    },
    {
      "asset": "assets/images/knight_dead_or_back.png"
    }
  ]
}

```

---

<div class="post-metadata">

**Author:** ![uckelman](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/uckelman/32/10020_2.png) [@uckelman](https://forum.vassalengine.org/u/uckelman)\
**Post date:** [July 22, 2026, 2:21pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/11 "2026-07-22T14:21:30Z")

</div>

> [@repeat83](#):
>
> So you mean it’s about ‘faces’, and essentially a ‘flip’ is just switching faces from 0 to 1 or 1 to 0.

Yes, that’s exactly what I mean.

> [@repeat83](#):
>
> It turns out, if we create a ‘flip’ action, add it to the context menu, and apply it to the object below, it will sequentially cycle through the first two faces.

That’s what I intend to happen on the client side. The move processor doesn’t need to know about actions at all.

---

<div class="post-metadata">

**Author:** ![akulkis00](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/akulkis00/32/11366_2.png) [@akulkis00](https://forum.vassalengine.org/u/akulkis00)\
**Post date:** [September 2, 2026, 5:16am UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/12 "2026-09-02T05:16:16Z")

</div>

Query is an action.

Selected isn’t really game state, it’s more data-processing status.

---

<div class="post-metadata">

**Author:** ![akulkis00](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/akulkis00/32/11366_2.png) [@akulkis00](https://forum.vassalengine.org/u/akulkis00)\
**Post date:** [September 2, 2026, 5:45am UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/13 "2026-09-02T05:45:09Z")

</div>

The confusion comes from your earlier statements calling “query” and “action” as game state, not verbs.

---

<div class="post-metadata">

**Author:** ![cholmcc](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/cholmcc/32/8319_2.png) [@cholmcc](https://forum.vassalengine.org/u/cholmcc)\
**Post date:** [September 3, 2026, 6:02pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/14 "2026-09-03T18:02:57Z")

</div>

> [@akulkis00](#):
>
> The confusion comes …

I think if you read the later replies in full, it will be evident what I meant - I don’t get what it is you are trying to address.

Yours,  
_Christian_

---

<div class="post-metadata">

**Author:** ![uckelman](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/uckelman/32/10020_2.png) [@uckelman](https://forum.vassalengine.org/u/uckelman)\
**Post date:** [September 7, 2026, 10:21pm UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/15 "2026-09-07T22:21:40Z")

</div>

I’ve fleshed out the design a bit more:

## Basic actions

These actions are state changes to objects:

- create: Create an object
- destroy: Destroy an object
- clone: Make a copy of an object from another object
- move: Move an object from one location to another
- flip: Set which face of an object is up
- rotate: Rotate an object about its center
- set property: Set a named property with a given value on an object
- unset property: Remove a named property from an object
- set permissions: Set permissions for an object

These actions involve a source of randomness:

- roll: Roll a die
- choose: Randomly select objects from a set
- shuffle: Shuffle the locations of a set of objects

These actions reveal information:

- `vis:piece`: Reveal the positional properties of the piece (parent, location, angle, anchor, children)
- `vis:type`: Reveal the piece type
- `vis:name`: Reveal the piece name
- `vis:face_up`: Reveal the face which is up
- `vis:faces`: Reveal the complete face list
- `vis:prop:propname`: Reveal the property with the name `propnanme`.

`vis` is short for “visibility”.

## Permissions

A permission is a named set of actions.

```json
{
    "id": "",
    "description": "",
    "actions": {
        "allow": [],
        "deny": []
    },
    "order": ["deny", "allow"]
}

```

- `id`: The id of this permission.
- `description`: The description of this permission.
- `actions.allow`: A list of permitted actions.
- `actions.deny`: A list of prohibited actions.
- `order`: The order in which `actions.deny` and `actions.allow` are applied.

`actions.allow` and `actions.deny` may have `*` in their lists, which matches all action, and may also have `x:*`, which matches all actions in action group `x`.

## Player sets

Player ids are singleton player sets.

Composite player sets are:

```json
{
    "id": 0,
    "description": "",
    "members": []
}

```

- `id`: The id of the player set.
- `description`: A description of this player set.
- `members`: A list of members of this player set.

Player sets can be members of other player sets. Player sets must be well-founded. `*` is the set of all players.

## Piece types

```json
{
    "id": 0,
    "description": "",
    "name": "",
    "faces": [],
    "occluded_face": ""
    "props": {}
}

```

- `id`: The id of this piece type.
- `description`: A description of this piece type.
- `name`: The name this piece type. Inherited by pieces of this type if not overridden.
- `faces`: The faces of this piece type. Inherited by pieces of this type if not overridden.
- `occluded_face`: The face of this piece type to show when a piece of this type is occluded by other pieces. Inherited by pieces of this type if not overridden.
- `props`: Named properties of this piece type. Inherited by pieces of this type if not overridden.

## Pieces

Pieces are game objects.

```json
{
    "id": 2,

    "parent": 1,
    "location": { "x": 1.0, "y": 2.0, "z": 3.0 },
    "angle": 0.0,
    "anchor": "Center"

    "children": [],

    "face_up": 1,

    "type": 1,

    "props": {},

    "permissions": {}
}

```

- `id`: The id of this piece.
- `parent`: The id of the parent of this piece. A piece sits atop its parent.
- `location`: The coordinates of the piece within the coordinate system of its parent.
- `angle`: The rotation of the piece about its anchor point.
- `anchor`: The origin of the piece’s coordinate system with respect to the piece’s faces. Possible values are `BottomLeft`, `BottomCenter`, `Bottom Right`, `CenterLeft`, `Center`, `CenterRight`, `TopLeft`, `TopCenter`, `TopRight`, a pair of percentages, a pair of absolute measurements.
- `children`: A list of ids of children of this piece. A piece’s children sit atop it.
- `face_up`: The index of the showing face of this piece.
- `type`: The id of this piece’s type.
- `permissions`: A map of player sets to lists of permissions granted to the players in those sets.

`name`, `faces`, `occluded_face`, `props` may also be specified, overriding those inherited from the piece type.

## Example

This example shows how a card from Twilight Struggle could be represented:

 ![image](https://forum.vassalengine.org/uploads/default/original/2X/0/0fd073ffc976f4d969046eaf2657cad669aec517.jpeg)

Players:

```json
{
    "id": 0,
    "name": "US"
    "description": "US player",
}

```

```json
{
    "id": 1,
    "name": "USSR"
    "description": "USSR player",
}

```

Images:

```json
{
    "fyp": "images/fyp.png",
    "back": "images/back.png"
}

```

Piece type:

```json
{
    "id": 0,
    "description": "Five-Year Plan card",
    "name": "Five-Year Plan",
    "faces": ["fyp", "back"],
    "occluded_face": "back",
    "props": {
        "event": [0],
        "ops": 3,
        "time": "early",
        "removed_on_event": false
    }
}

```

Permissions for the card when it is in the draw pile, in hand, and in the discard pile:

```json
{
   "id": "in_draw_pile",
    "description": "Permissions for cards in draw pile",
    "commands": {
        "allow": [
            "vis:piece",
            "vis:face_up",
            "move",
            "set_permissions"
        ],
        "deny": [
            "vis:type",
            "vis:name",
            "vis:faces",
            "vis:prop:*"
        ]
    },
    "order": ["deny", "allow"]
}

```

```json
{
   "id": "in_hand_own",
    "description": "Permissions for cards in own hand",
    "commands": {
        "allow": [
            "vis:piece",
            "vis:face_up"
            "vis:type",
            "vis:name",
            "vis:faces",
            "vis:prop:*"
            "move",
            "flip",
            "set_permissions"
        ],
        "deny": [
        ]
    },
    "order": ["deny", "allow"]
}

```

```json
{
   "id": "in_hand_opponent",
    "description": "Permissions for cards in opponent's hand",
    "commands": {
        "allow": [
            "vis:piece",
            "vis:face_up"
        ],
        "deny": [
            "vis:type",
            "vis:name",
            "vis:faces",
            "vis:prop:*"
        ]
    },
    "order": ["deny", "allow"]
}

```

```json
{
   "id": "in_discard",
    "description": "Permissions for cards in discard pile",
    "commands": {
        "allow": [
            "vis:piece",
            "vis:face_up"
            "vis:type",
            "vis:name",
            "vis:faces",
            "vis:prop:*",
            "move",
            "flip",
            "set_permissions"
        ],
        "deny": [
        ]
    },
    "order": ["deny", "allow"]
}

```

Piece in draw pile:

```json
{
    "id": 0,

    "type": 0,

    "parent": 1,
    "location": { "x": 1.0, "y": 2.0, "z": 3.0 },
    "angle": 0.0,
    "anchor": "center"

    "children": [],

    "face_up": 1,

    "permissions": {
        "*": ["in_draw_pile"]
    }
}

```

Piece in US player hand:

```json
{
    "id": 0,

    "type": 0,

    "parent": 1,
    "location": { "x": 1.0, "y": 2.0, "z": 3.0 },
    "angle": 0.0,
    "anchor": "center"

    "children": [],

    "face_up": 0,

    "permissions": {
        "0": ["in_hand_own"],
        "1": ["in_hand_opponent"],
    }
}

```

Piece in discard pile:

```json
{
    "id": 0,

    "type": 0,

    "parent": 1,
    "location": { "x": 1.0, "y": 2.0, "z": 3.0 },
    "angle": 0.0,
    "anchor": "center"

    "children": [],

    "face_up": 1,

    "permissions": {
        "*": ["in_discard"] 
    }
}

```

## Notes

I’m not certain that specifying permissions as allow/deny lists is useful or desirable; it might be simpler and more ergonomic to specify them as purely additive, e.g.,

```json
{
   "id": "in_draw_pile",
    "description": "Permissions for cards in draw pile",
    "commands": {
        "vis:piece",
        "vis:face_up"
        "move",
        "set_permissions"
    }
}

```

---

<div class="post-metadata">

**Author:** ![jrwatts](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/jrwatts/32/6972_2.png) [@jrwatts](https://forum.vassalengine.org/u/jrwatts)\
**Post date:** [September 8, 2026, 1:18am UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/16 "2026-09-08T01:18:59Z")

</div>

> [@uckelman](#):
>
> I’m not certain that specifying permissions as allow/deny lists is useful or desirable; it might be simpler and more ergonomic to specify them as purely additive

It makes life simpler if you want to grant all but a few permissions. I.e., for a card in hand,

```json
{
   "id": "in_hand_own",
    "description": "Permissions for cards in own hand",
    "commands": {
        "allow": [
            "*"
        ],
        "deny": [
            "delete"
            "clone"
        ]
    },
    "order": ["allow", "deny"]
}

```

could be easier than specifying every single possible allow.

(FYI, you’re missing the closing double-quote after the word `deny` in multiple instances in your post.)

---

<div class="post-metadata">

**Author:** ![uckelman](https://forum.vassalengine.org/user_avatar/forum.vassalengine.org/uckelman/32/10020_2.png) [@uckelman](https://forum.vassalengine.org/u/uckelman)\
**Post date:** [September 8, 2026, 9:35am UTC](https://forum.vassalengine.org/t/v4-move-processor-prototype/90343/17 "2026-09-08T09:35:15Z")

</div>

> [@jrwatts](#):
>
> It makes life simpler if you want to grant all but a few permissions.

I’m not sure it does when there are only 18 actions, many of which won’t be relevant for any given object. Having alllow/deny for individual permissions may also be confusing when an object is granted multiple permissions.

> [@jrwatts](#):
>
> (FYI, you’re missing the closing double-quote after the word `deny` in multiple instances in your post.)

Thanks. Fixed.
