이벤트 원인

이벤트는 게임 내에서 어떤 사건이나 행동에 대한 논리적인 변화를 주는 데 유용합니다. 하지만 이벤트 자체에는 무엇이 그 이벤트를 **발생**시켰느냐에 대한 문맥을 거의 제공하지 못한다는 문제점이 있습니다. Cause 객체는 이벤트에 대한 부가적인 문맥적 정보를 제공하고 받는 역할을 합니다.

For example, a world protection plugin needs information on what player has caused a ChangeBlockEvent to occur before they can decide if the event should be cancelled or not. Rather than go with the traditional route of creating a multitude of subevents for the different source conditions this information is instead provided in the Cause of the event.

모든 이벤트는 이벤트가 왜 발생했는지의 이유를 알려주는 Cause 객체를 제공합니다. Cause 객체는 이벤트 리스너에서 Event#getCause() 를 호출하여 받을 수 있습니다.

Cause에서 객체 받기

구조적으로, Cause 객체는 객체들의 순차적인 배열을 갖고 있습니다. 아래에서 설명하겠지만, Cause에서 정보를 가져오는 데 사용하는 메소드는 여러 가지가 있습니다. 전체 메소드 목록을 확인하려면 Javadoc 을 확인하세요.

참고

Cause 내부의 객체들은 순차적으로 정렬되어 있습니다. 첫 번째 객체는 이벤트에 가장 직접적인 원인이고, 그 다음 객체들은 중요도가 낮아지고(또는) 문맥적 정보만 제공하는 것입니다.

Cause#root() 는 Cause 객체의 첫 번째 객체를 반환합니다. 이 객체는 이벤트를 발생시키는 데 가장 가깝고 직접적인 원인입니다. Cause 는 비어 있을 수 없으므로, root 객체는 항상 받을 수 있습니다.

Cause#first(Class) 는 Cause 객체 사슬에서 주어진 클래스와 같거나 하위 클래스인 모든 객체들 중에서 첫 번째를 반환합니다. 아래의 예제 코드는 Cause 객체 사슬(배열)이 [Player, Entity, ...] 임을 가정합니다.

@Listener
public void onEvent(ExampleCauseEvent event) {
    Cause cause = event.getCause(); // [Player, Entity]
    Optional<Player> firstPlayer = cause.first(Player.class); // 1
    Optional<Entity> firstEntity = cause.first(Entity.class); // 2
}

Optional의 두 객체 모두 플레이어 객체를 가질 것입니다. 1번의 경우 Cause 객체의 첫 번째 객체 타입이 비교할 타입과 일치하고, 2번의 경우 첫 번째 객체 타입은 비교할 타입(Entity) 의 하위 클래스인 Player이기 때문입니다.

Cause#last(Class)Cause#first(Class) 와 동일한 조건에서 가장 마지막에 오는 값을 반환합니다.

위 예제 코드에서, Cause#last(Class) 를 대신 사용했다면 firstPlayer는 여전히 Player 객체를 갖겠지만, firstEntity는 Cause 객체 사슬의 두 번째 원소인 Entity를 가지게 될 것입니다.

Cause#containsType(Class) 는 Cause 객체 사슬에서 주어진 클래스(타입)와 일치하는 객체의 존재 여부를 boolean 값으로 반환합니다.

Cause#all() 은 Cause 객체 사슬에 있는 모든 객체를 List 배열로 반환합니다.

이름을 가진 Cause 객체

때로는 Cause 내부 객체들을 순서대로 나열하는 방법이 한 객체가 해당 이벤트와 어떤 관계를 갖고 있는지를 드러내기에는 부족합니다. NamedCause는 이 문제를 말끔히 해결합니다. NamedCause는 Cause 내부의 객체들에게 고유의 이름(태그) 을 부여하여 개별적인 객체를 식별할 수 있도록 해줍니다. NamedCause를 활용하는 예로는 ChangeBlockEvent.GrowNotifier 또는 DamageEntityEventSource가 있습니다.

Cause에서 특정 이름의 항목(객체) 가져오기

@Listener
public void onGrow(ChangeBlockEvent.Grow event) {
    Optional<Player> notifier = event.getCause().get(NamedCause.NOTIFIER, Player.class);
}

This example makes use of Cause#get(String, Class<T>) which can be used to retrieve the expected object associated with a name if it is present within the cause chain. Additionally Cause#getNamedCauses() provides a Map<String, Object> which can be used to find all present names and their associated objects.

참고

NamedCause로 자주 사용되는 식별 이름들은 NamedCause 클래스의 정적 필드(static field) 로 존재합니다. 특정 이벤트에 사용되는 식별자는 해당 이벤트 클래스의 정적 필드에서 찾을 수 있으며, 이런 식으로 존재합니다: DamageEntityEvent#SOURCE

사용자 지정 원인 만들기

Creating a cause to use when firing an event is extremely easy. The hardest part is deciding what information to include in the cause. If you’re firing an event from your plugin which is usually triggered through other means perhaps you want to include your plugin container so other plugins know that the event comes from your plugin. Or if you are firing the event on behalf of a player due to some action it’s usually a good idea to include that player in the cause.

참고

Cause objects are immutable therefore cannot be modified once created.

Using Cause#of(NamedCause), you can construct a cause from a series of objects. The objects will be added to the cause chain in the order that they are passed to the method, so the first object parameter will become the root cause. Remember that a Cause may not be empty, so at least one non-null parameter is always required.

If you already have a cause object and would like to append some more objects to the chain you can use Cause#with(NamedCause, NamedCause…). This constructs a new Cause object containing first the objects already present in the original cause, then followed by the additional objects that you provided.

Finally if you wish to add an object to a cause with a defined named first call NamedCause#of(String, Object) and then pass the returned NamedCause instance to the cause chain as you would a normal object.