타겟셀렉터 내부 처리 과정

@...
테스트중인 복사본입니다

JE 1.21.11 (25w46a)

타겟셀렉터의 필터링 과정을 서술합니다.
파싱 전/후를 구분하지 않으며 여러가지 요소를 통합하여 서술하기에 성능 관점에서는 실제 측정한 성능과 달라질 수 있습니다.

본 게시글은 화면이 작을 경우 자동 줄바꿈으로 인해 보기 어려울 수 있습니다. 오른쪽 위 ... 아이콘에서 전체 보기로 열기를 클릭하여 좌우 공간을 늘릴 수 있으며, 그마저도 좁다고 느껴질 경우 오른쪽 위 사람 모양 아이콘, 멤버 목록 숨기기를 클릭하세요. 그마저도 좁다고 느껴질 경우 [Ctrl]+[-] 단축키로 화면을 축소시키세요.

모바일의 경우... 화면 가로로 돌려서 보시거나 복사해가지고 어디 다른 코드 에디터 앱에 붙여넣기해서 보시는게 그나마

선행지식
  1. 기초 프로그래밍 문법 (if, else, foreach, function)
  2. AABB (dx,dy,dz,distance 관련 로직을 이해하려면 필수)
@...

[1]

선택자가 호출될 경우 다음 FIND_ENTITIES 함수가 실행됩니다.

if (@a):
    if (limit이 없을 경우): limit=2147483647
    if (sort가 없을 경우):  sort=arbitrary
if (@e):
    if (limit이 없을 경우): limit=2147483647
    if (sort가 없을 경우):  sort=arbitrary
if (@n):
    if (limit이 없을 경우): limit=1
    if (sort가 없을 경우):  sort=nearest
if (@p):
    if (limit이 없을 경우): limit=1
    if (sort가 없을 경우):  sort=nearest
if (@r):
    if (limit이 없을 경우): limit=1
    if (sort가 없을 경우):  sort=random
if (@s):
    if (limit이 없을 경우): limit=1


if (dx,dy,dz중 하나라도 있을 경우):
    if (dx가 없을 경우): dx=0
    if (dy가 없을 경우): dy=0
    if (dz가 없을 경우): dz=0
    [[min(dx,0),min(dy,0),min(dz,0)],[max(dx,0)+1,max(dy,0)+1,max(dz,0)+1]] 크기의 AABB 생성
elseif (distance 최대거리가 지정되어 있는 경우):
    r = distance의 최대거리
    [[-r,-r,-r],[r+1,r+1,r+1]] 크기의 AABB 생성


if (@a,@r,@p,type=player,advancements={...}*,level,gamemode중 하나라도 있을 경우):
    if (@s):
        if (실행자의 타입 == 플레이어):
            CONDITION_CHECK(실행자)
    elseif (x,y,z,dx,dy,dz,distance중 하나라도 있을 경우):
        // ServerLevel.getPlayers
        foreach (현재 플레이어 in 컨텍스트 차원의 모든 플레이어 리스트):
            CONDITION_CHECK(현재 플레이어)
    else:
        // PlayerList.getPlayers
        foreach (현재 플레이어 in 서버의 모든 플레이어 리스트):
            CONDITION_CHECK(현재 플레이어)
↖️ 1
@...
else:
    if (@s):
        CONDITION_CHECK(실행자)
    else:
        enabledFeatures 체크 활성화
        if (x,y,z,dx,dy,dz,distance중 하나라도 있을 경우):
            차원 리스트 = [현재 컨텍스트 차원]
        else:
            차원 리스트 = 서버의 모든 차원 리스트
        foreach (현재 차원 in 차원 리스트):
            if (AABB 범위가 있을 경우):
                // EntitySectionStorage.getEntities
                foreach (현재 섹션 in 현재 차원에서 AABB범위에 가까이 위치한 섹션 리스트***):
                    foreach (현재 엔티티 in 현재 섹션의 모든 엔티티 리스트):
                        if (현재 엔티티의 타입 == type**):
                            if (현재 엔티티의 히트박스가 AABB와 교차할 경우):
                                CONDITION_CHECK(현재 엔티티)
                                if (현재 엔티티의 타입이 엔더드래곤일 경우):
                                    foreach (파트 in 현재 엔티티의 드래곤 파츠):
                                        if (파트 타입 == type**):
                                            CONDITION_CHECK(파트)
            else:
                // EntityLookup.getEntities
                foreach (현재 엔티티 in 컨텍스트 차원의 모든 엔티티 리스트):
                    if (현재 엔티티의 타입 == type**):
                        CONDITION_CHECK(현재 엔티티)


* [advancments={}]와 같이 중괄호 내부가 비어있으면 무시됩니다.
** 여기서는 type=<entity> 형태만을 인식합니다. type=!,type=!#,type=#은 인식하지 않습니다.
*** 현재 차원에서 AABB범위에 가까이 위치한 섹션 리스트를 구하는 과정은 아래와 같습니다. 섹션이란 16×16×16 영역으로 정의되며, 이러한 섹션 조회 방식 때문에 엔티티의 히트박스가 너무 클 경우 가끔 dx,dy,dz가 해당 엔티티를 선택하지 못하는 경우가 발생하기도 합니다.
    (xMin,yMin,zMin, xMax,yMax,zMax) = AABB를 최소와 최대 꼭짓점으로 분해
    (xMin,yMin,zMin, xMax,yMax,zMax) += (-2,-4,-2, 2,0,2)
    // floor division
    (xMin,yMin,zMin, xMax,yMax,zMax) /= 16
    for (int x=xMin; x<=xMax; x++):
        // LongAVLTreeSet.subSet
        foreach (현재 섹션 in 현재 차원의 섹션 집합의 [[x,0,0],[x+1,0,0]) 구간):
            y = 현재 섹션의 y좌표
            z = 현재 섹션의 z좌표
            if (yMin<=y<=yMax and zMin<=z<=zMax):
                if (section.getStatus().isAccessible()):
                    yield 현재 섹션
@...

[2]

CONDITION_CHECK 함수는 다음과 같은 순서대로 주어진 엔티티가 조건을 만족하는지 확인하며, 모든 조건을 만족할 경우 해당 엔티티는 "결과 리스트"에 추가됩니다.
1. (선택자가 @n 또는 @e일 경우) 이 엔티티가 살아있는지 체크
2. 선택자에 배치된 순서대로 체크: name,team,tag,predicate,nbt,gamemode,scores,type*,advancements
3. x_rotation 체크
4. y_rotation 체크
5. level 체크
6. ([1]에서 "enabledFeatures 체크 활성화"가 실행된 경우) 현재 엔티티의 타입이 허용되어 있는지 체크
7. AABB 체크**
8. distance 체크
9. (sort=arbitrary일 경우) limit 체크***

* CONDITION_CHECK 함수 실행 전에 이미 type을 체크해둔 경우도 있기에
 이곳의 type은 중복체크일 가능성이 있습니다.
** CONDITION_CHECK 함수 실행 전에 이미 AABB 범위를 체크해둔 경우도 있기에
 이곳의 AABB는 중복체크일 가능성이 있습니다.
*** JE 1.19.3에서 추가, sort=arbitrary일 경우 결과 리스트 길이가 limit에 도달했을 때
 타겟셀렉터 과정을 예정보다 빠르게 종료하는 식으로 최적화합니다.


[3]

FIND_ENTITIES 함수가 완전히 종료되고 완성된 "결과 리스트"는 다음 과정을 진행한 후 최종적으로 반환됩니다.
1. sort
2. limit
@...
선택자가 조금 독특하게 동작하는 특수한 상황을 모아봤습니다. 위 코드를 이해하는데 도움이 될 수 있을겁니다.

  1. @e[level=0..]은 모든 플레이어를 선택합니다.
  2. @e[type=player]는 죽은 플레이어를 선택하지 못하지만, @a는 죽은 플레이어도 선택합니다.
  3. @s는 죽은 엔티티를 선택할 수 있습니다.
  4. @e[x=0]는 실행된 차원의 엔티티만을 선택합니다.
  5. @s는 x,y,z,dx,dy,dz,distance로 인한 차원 제한을 무시합니다.
  6. @p는 다른 차원에 있는 플레이어도 선택할 수 있습니다.
  7. 모든 플레이어가 실행자와 같은 차원에 있다 하더라도 @a의 순서와 @a[x=0]의 순서는 서로 다를 수 있습니다.
  8. 모든 엔티티가 실행자와 같은 차원, 실행지점으로부터 5칸 안에 있다 하더라도 @e의 순서와 @e[distance=..5]의 순서는 서로 다를 수 있습니다.
  9. @e[type=!pig,type=!sheep,type=!...]으로 모든 엔티티를 제외시키고 플레이어만을 남겨둔 선택자는 @e[type=player]의 플레이어 순서와 서로 다를 수 있습니다.
🤯 2

정보

서비스 안내

가이드라인