# Selecting edits

> Every Selection method, by who, when, where and what, with the rules each one follows.

Source: https://oasis-protection.com/docs/api/selections/

A `Selection` chooses edits. Lookups read the edits it chooses, rollbacks and restores act on them, and purges delete them. It is the API’s version of the filters staff type after `/lookup` and `/rollback`.

Start from `Selection.all()`, which chooses every edit, and narrow it:

```java
Selection grief = Selection.all()
    .by(griefer)
    .within(Duration.ofHours(2))
    .near(staff.getLocation(), 30)
    .kinds(Kind.BLOCK, Kind.CONTAINER)
    .not().causes(Cause.WORLDEDIT);
```

A selection is split into parts: who, when, where, the kinds, the causes, the materials, and what it leaves out. Each method sets one part and **replaces** what that part held before. A part you never set does not narrow the selection, so `Selection.all()` holds every edit of every kind, chat, commands and logins included. Every method returns a new selection and leaves the old one as it was.

## Who

| Method                         | Chooses                                                                                                                                      |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `by(OfflinePlayer... players)` | Edits by these players, whether the player acted or started a chain the world carried on.                                                    |
| `by(UUID... ids)`              | Edits by these people: player ids, or `Actor.id()` ids.                                                                                      |
| `by(Actor... actors)`          | Edits these actors made or carried, such as `Actor.LAVA`: the lava with nobody behind it, and the lava that flowed from any player’s bucket. |

People and actors share one part. An edit matches when its [person, or the actor that carried it](/docs/api/concepts/#the-person-and-the-actor), is one of the ids you name. So:

* `by(player)` chooses what the player did, and what the world carried on for them: the lava they poured and the fire that lava started.
* `by(Actor.LAVA)` chooses all lava, whoever is behind it. It is what staff get with `user #lava` in a command.

The three `by` methods replace one another, so `by(player).by(Actor.LAVA)` selects all lava, not the player’s lava. To mix players and actors in one selection, pass their ids to `by(UUID...)`. An actor’s id there works as it does in `by(Actor...)`:

```java
Selection griefOrCreepers = Selection.all().by(griefer.getUniqueId(), Actor.CREEPER.id());
```

To find only the lava one player poured, select the player and check `edit.actor()` on each edit.

A lookup of `by(player)` also reads the player’s logins and logouts. Add `kinds(Kind.BLOCK)` to read blocks alone.

## When

| Method                      | Chooses                                                                      |
| --------------------------- | ---------------------------------------------------------------------------- |
| `within(Duration duration)` | Edits in the last `duration`, counted from the moment the selection is read. |
| `since(Instant time)`       | Edits at or after this time.                                                 |
| `until(Instant time)`       | Edits at or before this time.                                                |

Times are inclusive at both ends. The three methods share the time part, and each one replaces what it finds there:

* `within` replaces the whole time part. It removes a `since` and an `until`.
* `since` replaces a `within` window, and keeps an `until`.
* `until` replaces a `within` window, and keeps a `since`.

A window always runs up to the moment of the read, so a selection never holds a window and an `until`. For a span that ends in the past, use `since` and `until` with an `Instant`:

```java
// Edits from the last day, up to one hour ago.
Instant now = Instant.now();
Selection dayBefore = Selection.all()
    .since(now.minus(Duration.ofDays(1)))
    .until(now.minus(Duration.ofHours(1)));

// Edits older than thirty days.
Selection old = Selection.all().until(Instant.now().minus(Duration.ofDays(30)));
```

`since` after `until` throws `IllegalArgumentException`.

### A window counts back from when it runs

`within` keeps the duration, not a fixed time. A lookup, an estimate or a start counts it back from the moment it reads the selection. So a request you build at noon and start at one o’clock, with `within(Duration.ofHours(2))`, rolls back the edits from eleven to one.

```java
// Built once, in onEnable.
OperationRequest lastTwoHours = oasis.operations()
    .rollback(Selection.all().by(griefer).within(Duration.ofHours(2)));

// Started later. Each start reads the two hours before it.
lastTwoHours.start();
```

A start fixes the window once, when it begins. Its sweeps and its [`forceKept()`](/docs/api/operations/#kept-left-and-force) request then read the same edits as its first pass.

`since` and `until` take an `Instant`, so they never move.

## Where

| Method                                    | Chooses                                                                                              |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `in(World... worlds)`                     | Edits anywhere in these worlds.                                                                      |
| `in(String... worlds)`                    | Edits anywhere in these worlds by their stored names, for a world that is not loaded.                |
| `near(Location center, int radius)`       | Edits in the cube of this radius, in blocks, around the location.                                    |
| `box(Location corner, Location opposite)` | Edits in the box between two corners of one world.                                                   |
| `at(Block block)`                         | Edits at one block, of every kind.                                                                   |
| `in(String world, Selection.Box box)`     | Edits in a box of a world by its stored name, for a world that is not loaded. The box is never null. |

All six share one part, the world and the box, so the last one you call wins. Boxes are inclusive. `near` is a cube, not a sphere, so `near(location, 0)` is the one block at that location. `box` takes its corners in any order.

`Selection.Box` holds `minX`, `minY`, `minZ`, `maxX`, `maxY` and `maxZ`, all inclusive. It throws `IllegalArgumentException` for a lowest bound above its highest:

```java
// The spawn area of a world that is not loaded right now.
Selection spawn = Selection.all().in("world_old", new Selection.Box(-64, -64, -64, 64, 320, 64));
```

Note

`at(block)` chooses every edit at the block, chat and commands included. For the history a staff member sees with the inspector, use [`lookups().history(block)`](/docs/api/reading-history/#the-history-of-one-block), which leaves out books, chat, commands and sessions and marks the edit that stands there now.

### A world that is not loaded

`in(String... worlds)` and `in(String world, Box box)` name a world that no server world has now. A world is not loaded when a staff member unloaded it, or when its folder is gone. The stored name is the one the world had when Oasis logged its edits.

A lookup and a purge read and write the store alone. They take any world that is loaded, that Oasis saw loaded, or that holds edits in the store, with no folder needed. [A purge of whole worlds](/docs/api/purges/#a-purge-of-whole-worlds) deletes the history of such a world.

A rollback, a restore and an undo write the world, so they need it. Oasis loads a world that is not loaded for the pass: through the [world loader](/docs/api/worlds/) of the plugin that keeps it, or from its folder. When nobody can load it, the operation fails with `WORLD_GONE` before it writes anything.

Names are exact, as `World.getName()` reads them. Oasis checks every world name of a selection, chosen or left out, when a request reads it: a lookup, a history, an estimate, an operation, or a purge with its count. A name that is not loaded under that exact name, that Oasis never saw, and that the store holds no edits of fails the request with `Failure.Reason.NO_SUCH_WORLD`, so a typo never widens a rollback.

## What

| Method                           | Chooses                                                          |
| -------------------------------- | ---------------------------------------------------------------- |
| `kinds(Kind... values)`          | Edits of these kinds only, such as `Kind.BLOCK`.                 |
| `causes(Cause... values)`        | Edits with these causes only, such as `Cause.BREAKING`.          |
| `materials(Material... values)`  | Edits of these blocks or items, before or after the edit.        |
| `entities(EntityType... values)` | Edits of these entities.                                         |
| `matching(String... patterns)`   | Edits of blocks, items or entities by id or by a pattern of ids. |

`kinds` and `causes` each have a part of their own. `materials`, `entities` and `matching` share one part, so the last of them you call wins. To choose blocks and entities in one selection, use `matching` with both ids:

```java
Selection signsAndFrames = Selection.all().matching("*_sign", "item_frame");
```

[Vocabulary](/docs/api/reference/) lists every `Kind` and `Cause`.

### Patterns

`matching` and `not().matching` take ids and patterns of ids:

* An id is a namespaced id, such as `minecraft:oak_log` or `minecraft:zombie`.
* An id with no namespace is a vanilla one: `oak_log` means `minecraft:oak_log`.
* A `*` matches any run of characters: `*_sign` matches every sign, and `*_ore` every ore.
* Oasis reads a pattern in lower case, so `OAK_LOG` works too.

A value may hold only letters, digits, `_`, `-`, `.`, `/` and `*`, with at most one `:` between the namespace and the path. Anything else, such as a space or a `#`, throws `IllegalArgumentException`.

## Leaving edits out

`not()` returns the parts a selection leaves out. Each method takes the same values as the method of the same name above, leaves them out, and returns the selection.

| Method                                                                  | Leaves out                                                                    |
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `not().by(OfflinePlayer...)`, `not().by(UUID...)`, `not().by(Actor...)` | The edits of these people or actors, and every chain they started or carried. |
| `not().in(World...)`, `not().in(String...)`                             | Edits in these worlds.                                                        |
| `not().kinds(Kind...)`                                                  | Edits of these kinds.                                                         |
| `not().causes(Cause...)`                                                | Edits with these causes.                                                      |
| `not().materials(Material...)`                                          | Edits of these blocks or items.                                               |
| `not().entities(EntityType...)`                                         | Edits of these entities.                                                      |
| `not().matching(String...)`                                             | Edits whose id matches one of these patterns.                                 |

`not().by(x)` is the exact inverse of `by(x)`: it leaves out what `x` did and the fire and water `x` set going.

Each call replaces its part, as the rest of the selection does. `not().by(steve).not().by(alex)` leaves out Alex only. `not().materials`, `not().entities` and `not().matching` share one part.

```java
// Everything a player broke or placed, but not their signs.
Selection handWork = Selection.all()
    .by(builder)
    .causes(Cause.BREAKING, Cause.PLACING)
    .not().matching("*_sign");

// Everything near a location, except what one builder did.
Selection others = Selection.all().near(center, 20).not().by(builder);
```

## An empty list throws

Every method that takes a list of values throws `IllegalArgumentException` when the list is empty. That covers every `by`, `in`, `kinds`, `causes`, `materials`, `entities` and `matching`, and every one of them after `not()`.

This protects you from a costly mistake. A part you never set chooses everything, so if an empty list meant “no filter”, it would choose every edit on the server. Picture a plugin that collects the griefers in a region, finds none, and rolls back `by()` of that empty list. It would roll back the whole server.

So check your list before you pass it:

```java
public void rollBackGriefers(List<UUID> griefers, Location center)
{
    if (griefers.isEmpty())
    {
        return;
    }
    oasis.operations()
        .rollback(Selection.all().by(griefers.toArray(UUID[]::new)).near(center, 50))
        .start();
}
```

To choose every player, leave the part unset.

## A selection that matches nothing

A selection that cannot match any edit by how it is built throws `IllegalArgumentException` at the call that makes it, in either order of the calls:

* A person, world, kind, cause or material is both chosen and left out.
* `near`, `box`, `at` or `in(name, box)` names a world that is left out.
* `since` is after `until`.

## Every rule that throws

Each of these throws `IllegalArgumentException` when you build the selection, before anything reads the store:

| Method                                    | Throws when                                                                                          |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `by(...)`, all three, and `not().by(...)` | No player, id or actor is named, or a person is both chosen and left out.                            |
| `in(...)`, and `not().in(...)`            | No world is named, or a world is both chosen and left out.                                           |
| `kinds(...)`, `not().kinds(...)`          | No kind is named, or a kind is both chosen and left out.                                             |
| `causes(...)`, `not().causes(...)`        | No cause is named, or a cause is both chosen and left out.                                           |
| `materials(...)`, `not().materials(...)`  | No material is named, or a material is both chosen and left out.                                     |
| `entities(...)`, `not().entities(...)`    | No entity type is named, or a type is both chosen and left out.                                      |
| `matching(...)`, `not().matching(...)`    | No pattern is named, a value is not an id or a pattern of one, or an id is both chosen and left out. |
| `near(center, radius)`                    | The radius is negative.                                                                              |
| `box(corner, opposite)`                   | The corners are in two worlds.                                                                       |
| `near`, `box`, `at`, `in(name, box)`      | The world is left out.                                                                               |
| `since(time)`, `until(time)`              | `since` is after `until`.                                                                            |
| `new Selection.Box(...)`                  | A lowest bound is above its highest.                                                                 |

The message of each exception says what was wrong and how to fix it, so you can show it to the person who typed the value.

## Reading a selection back

A selection tells you what it chooses. Each getter returns an empty set or an empty `Optional` for a part you never set.

| Getter                | Returns                                                                                |
| --------------------- | -------------------------------------------------------------------------------------- |
| `people()`            | The ids chosen, or empty for everyone.                                                 |
| `since()`             | The time set with `since`. Empty when unset, and empty while a `within` window is set. |
| `within()`            | The window set with `within`, or empty.                                                |
| `until()`             | The time set with `until`, or empty.                                                   |
| `worlds()`            | The stored world names, or empty for every world.                                      |
| `box()`               | The box inside the one world of `worlds()`, or empty for whole worlds.                 |
| `kinds()`, `causes()` | The kinds or causes chosen, or empty for all.                                          |
| `materials()`         | The namespaced ids and patterns chosen, or empty for all.                              |
| `left()`              | What the selection leaves out, as a `Selection.Left`.                                  |

`left()` returns `Selection.Left(people, worlds, kinds, causes, materials)`. Each is a set, empty when nothing of that part is left out. `people` holds player ids and `Actor.id()` ids.

`materials()` and `left().materials()` hold namespaced ids. A `Material.STONE` reads back as `minecraft:stone`, and a pattern `*_sign` as `minecraft:*_sign`.
