Translation notice
This page was translated with machine translation and may contain inaccuracies. If you can help improve it, please open an issue or submit a pull request.
TIP
Look at me first qwq
Hello qwq, welcome to read Xiaoye's article. Recently, I was discussing the performance issues of the target selector with a group of friends. I read a little bit of the source code related to entity selection, and used this article to summarize the reading and processing process of entity selection in the minecraft command system, and how it affects subsequent selections.
Before writing this article, I didn’t pay much attention to how to optimize the entity selection internally, because when I write code, I focus more on my own logic and don’t worry too much about the design and optimization of the game code level. Since mojang is given, it is necessary to ensure the rationality of game code processing and optimization under the premise that developers provide normal selection logic. What I want to say here is that optimization is something that can be done as much as possible, rather than something that must be done to the end. How to optimize requires the accumulation of development experience. Optimizing the code should not affect development efficiency too much. Optimization can be achieved within the scope of one's ability.
Let’s take a look at the game code that handles entity selection, summarize some rules, optimize the writing method of entity selection, and complain about mojang. The reason why it is called "entity selection" is because in addition to the target selector, when selecting an entity, you can also provide uuid and player name for direct selection. I refer to these collectively as "entity selection".
There are a lot of analysis processes in the middle. If you want to see the conclusion directly, please jump directly to the "It's time to make it delicious" [1] chapter
has many pictures and the words are sometimes small. The app can directly click on the pictures to enlarge. On the web, you need to enlarge the web page. Try Ctrl+mouse wheel~
The source code version is JE 1.20.6, based on fabric yarn Anti-obfuscation may be different from the official one, but it is enough to look at the simple source code~
command reading and processing
data pack will be executed in the game/reloadRead again. The data pack file provides the game with a bunch of strings. The game verifies whether the command represented by the string conforms to the format. If so, the corresponding command object will be created inside the game. The things that command can do are hard-coded, but they can be assembled and sorted to implement various logics and change the behavior in the game. This is the embodiment of the data-driven nature of the data pack. This step I call reading and processing.
The command we write will be read by the game first, and then it already exists in the memory, waiting for subsequent "use". The reason why I mention this is because when reading the source code involved in this article, it also involves the string processing logic when reading. I don't know how to express this better, but I will remind you later on which ones are "reading and processing" and which ones are "using".
Prepare raw materials
In this chapter we will read the source code~
You need to find an entry point, such as execute as <entity> Here is the logic of selecting entity. Come toExecuteCommandClass:

underlined in the figuregetOptionalEntitiesThe method is to select the entity method, keep clicking in, and come toEntitySelector#getUnfilteredEntitiesMethod:

saw some judgments in this methodincludesNonPlayers、senderOnlyvalue and determines the logic of the branch. Obviously, this is the logic of "using" the target selector, that is, the command is already being executed. In order to understand the meaning of these values, we should go to the "Reading and Processing" section, byincludesNonPlayers、senderOnlytheseusageseasy to findEntitySelectorReaderThe class is the class that handles the reading and processing of the target selector. After observation, thereadThe method is the one that starts reading and processing:

line 453, determine whether the next character is@, if so, follow the reading and processing logic of the target selector, otherwise jump to line 461. In the logic of processing the target selector, line 454 determinesatAllowed, obviously this variable indicates that the target selector should not be used here. After a little reading, it is related to permissions, we don’t care about it here; line 459, callreadAtVariableMethod, read and process the target selector;
line 461, callreadRegularmethod, this method is the processing logic of directly inputting the player name and uuid, because they are not the target selector;
line 464, callbuildPredicateMethod, this method constructs the entity rotation angle predicate and the player level predicate respectively, emphasis is placed;
line 465, adjusts the build method, which is based on the readdx、dy、dzandx、y、zThe selectors are constructed separatelyBoxandFunction<Vec3d, Vec3d>Used for subsequent target selector to filter entities based on area and finally instantiatedEntitySelectorThe object, target selector is read and processed.
Please note that this is the "reading and processing" stage. Without considering the function macro, the logic here will only be executed when the data pack is loaded, and does not involve runtime performance issues.
So according to the above analysis, line 459 callsreadAtVariableThe method we need to care about now is that it reads and processes the target selector. Click to see:

In this method, read@characters after, ifp、a、r、s、e Then assign values to some members respectively. The meaning of these values is already clear.
limitThe limited number of entitiesincludeNonPlayersWhether to select non-playerentitysorterSort bysenderOnlyIs it @s selectorpredicateCondition chain
selector@The following characters will affect the values assigned to members when constructing the target selector object here. For example@pThe selector selects the nearest player. Looking at lines 209~212, you can see that it is passed tolimit、includeNonPlayersIt is implemented by waiting for member assignment, so, in fact,@a[limit=1,sort=nearest]and@pThere is no difference. The difference between them is only the parsing cost when loading the data pack, but the selector object formed after parsing is the same, and there is no difference in their efficiency when the command is run.
We pay special attention topredicate, which is inEntitySelectorReaderandEntitySelectorMembers that exist in , each condition of the target selector will be constructed aspredicate, chain together, and finally test the entity, which helps subsequent reading and understanding of the processing order of each target selector parameter.
line 236, due to@eselectorpredicateFirst adjustEntity#isAliveCheck whether the entity is alive, presumably this is@e[type=minecraft:player]The reason why the player who did not click to revive on the death interface cannot be selected.
Here we only understand the type of the target selector, and reading and parsing the parameters is on line 243readArgumentsMethod, let’s take a look:

The main logic of this method is a large while loop on line 276 to read all parameters of the target selector. In the loop body, lines 279~280 read the name of the parameter, and then callEntitySelectorOptions#getHandlerGet the handler, and then use this handler to process the following content on line 290. For example, the target selector existstype=minecraft:sheepparameter, it will read "type", then get the handler of the "type" parameter, and then use this handler to process "minecraft:sheep".
So as long as you find the handler of each parameter, you can know how the parameters are processed.EntitySelectorOptions#getHandlerMethod:

line 505 hasOPTIONS, the handler is taken out from here, so you need to see where it goes.OPTIONSStuff it, check usages Skip toputOptionMethod:

It has 21 usages, click to see:

found, locate:

You can see the handler written during registration. Here is the reading and processing logic of all target selector parameters. Since the code has more than 400 lines, I read it completely and organized it into a table. See the next section.
Processing raw materials★
In this section we analyze the order and optimization issues
Parameters related to the writing order

We mentioned in the previous section that when reading and processing each parameter of the target selector, all parameters of the target selector will be read in a loop and the corresponding handler will be found for processing. Observing the table, most of the parameters are directly constructed after parsing and then appended to the existing predicate. This means that the order of the predicate chain is related to the order of the parameters, such asnbtparameters andscoresParameters, since their processing logic directly constructs predicate and then splices them, so in the target selector, we put the parametersnbtwritten inscoresPreviously, when selecting the entity, we will first checknbt, then checkscores, and if the parametersnbtwritten inscoresLater, the order will be reversed, which leads to a method that can optimize the target selector: because when selecting an entity, all entities will be taken and tested with predicates in sequence. Each test will filter out entities that do not meet the requirements, and the filtered entities will no longer be tested with subsequent predicates. The remaining entities will be successfully selected, so whichever parameter can exclude more entities should be written in a higher position.
Parameters that are independent of the writing order
However, not all parameters will directly construct the predicate, and some parameters are only temporarily stored inEntitySelectorReaderobject, and then construct the predicate or perform other selection operations after reading, which means that these parameters are not affected by their order in the selector. These parameters are:
x、y、zdx、dy、dzdistancex_rotation、y_rotationlevellimitsort
x_rotation、y_rotationandlevelwill be spelled after the predicate chain in turn. They are always tested at the back position, regardless of the writing position in the selector.
Special parameters (involving optimization)
x、y、z、dx、dy、dzanddistanceIt will also affect the initial optimization:

in constructionEntitySelectorwhen, will be passed inBoxparameter, this parameter will be inEntitySelectorWhen it is subsequently used to select entities, some possible entities are screened out based on the area. This step is the initial screening. Specifically, observing line 132, we can see that as long asdx、dy、dzpart of it, then even if the missing part is not provided, it will be regarded as providing a value of 0.0, in which caseBoxbydx、dy、dzparameters shall prevail; if not provideddx、dy、dzany of the but provideddistance,butBoxbydistance.maxThe coarse screen is based on the side length of 2 * max + 1of rectangular cubeBox, this place is very interesting, imagine ifdistance.maxVery big, so circledBoxThe more useless areas there are, in other words, the "coarser" this preliminary screening (coarse screening) is.
There is another question here,distanceParameters if only providedminwithout providingmax, is there no such optimization, or even degradation? Because mojang likes to be in somenullStuff it somewhereMAXGo in, for example when the selector providesdistance=1.., will mojang process it asdistance=1..Double::MAXWell, this way we get a hugeBoxThere is no point in doing a preliminary screening. But in parsingDoubleRangeI didn't find any evidence support in the class, so it should be possible herenullYes, I don’t have this concern, butdistanceParameters are only providedminwithout providingmaxThere is indeed no optimization effect.
Function<Vec3d, Vec3d>The parameters arex、y、zWhere provided, only part of these three parameters can be provided. The missing parameters will inherit the command execution context.poscorresponding weight.
now remainssortandlimitThere is no exploration, because they take effect during the selection process and when the final result is returned, but we have not looked at what was said at the beginning: if the player name or uuid is directly provided instead of the target selector, let’s take a look at the logicEntitySelectorReader#readRegular:

line 257, trying to convert to uuid. If it can be converted, because uuid can also be a non-playerentity uuid, so setincludesNonPlayersfortrue;
If it cannot be converted, it is considered that the player name has been entered, up to 16 characters, and is set when it is legalincludesNonPlayersforfalse, and save the player name toplayerNamemember.playerNameMembers can only be assigned here and used in selectorsname= 参数是构造 predicate 测试实体名字, different from here.
selector's "selection" process
Above we have read the reading and processing logic of entityselector in detail, and now we can look at the selection process.
returns to the EntitySelector#getUnfilteredEntities method:

130 line: ifincludesNonPlayersforfalse, which means that the selector only selects the player, adjustgetPlayersmethod.getPlayersThe subsequent logic of the method is basically the same as this method, and the differences will be pointed out later;
132 line: If there isplayerName, indicating that the selector directly specifies the player name (for exampleexecute as Mini_Ye), it will not have any conditions, it will directly search whether there is a player with the corresponding name in the player list, and then return;
135 line: if there isuuid, indicating that the selector directly specifies uuid (for exampleexecute as 0-0-0-0-1), it will not have any conditions, it will directly search whether there is an entity corresponding to uuid in the entity list, and then return;
146 line, based on the previously passed inBoxandx、y、zconstructedFunctionConstruct predicate and splice it to the end. This step isx、y、z、dx、dy、dzanddistanceAccurate filtering of parameters:

147 line to determine whether the selector is@s, if so, use the predicate chain to test the entity, and then return;
153 line, callisLocalWorldOnlyjudgelocalWorldOnlyvalue, iftrue, then only select the entity in the dimension where this selector is located, otherwise select the entity in all dimensions. InfluencelocalWorldOnlyThe parameters arex、y、z、dx、dy、dzanddistance, as long as any of these parameters are specified, the selector will only select the entity in the dimension where it is located;
is called regardless of line 154 or line 157appendEntitiesFromWorldMethod:

The parameter i here islimit, but it’s not necessarily what we setlimit, which consists of line 167getAppendLimitThe method is given, and its internal logical judgmentsorterIs itarbitrary, if so, use the one we specifiedlimit,otherwiselimitforInteger.MAX_VALUE, that is, unlimited. Therefore, the target selector'slimitParameters are only insortIt has an optimization effect when it is the default, because there is no order requirement at this time, and the selection will stop as long as the number of entities is selected.
This point still needs to be explained. Entities are stored in a certain data structure in the game memory, such as an entity list. The traversal of the list generally starts from the beginning, assuming that the target selector is not specifiedsort, then the selection order of the target selector is "arbitrary". Assume that the entity list is [A, B, C, D, E]. There are five entities in total. They all meet the requirements. When not specifiedsortandlimit=2When , A and B are always selected, and the following three entities will not be selected. This is in line with the developer's requirements - "Just give me two entities that match. I don't care which two they are, even if you always give me A and B." However,sort=randomWhen, it means that the developer requires the game to be "randomly selected", and all entities should have the same probability of being selected, even iflimit=2, you cannot choose A and B here, because the three entities C, D, and E at the end of the list must also participate in this randomization. In the same way, there aresort=nearest, limit=2When selecting the two closest entities, what if entity E at the end of the list is the closest? You cannot select A and B without selecting the latter ones, which will lead to wrong results.
Therefore, inEntitySelector#appendEntitiesFromWorldIn the method, pass it tocollectEntitiesByTypemethodologicallimitParameters are only insortIf not specified, it will be provided by the developer.limit,otherwiselimitAlways unlimited. In other words, only without specifyingsortwhen, specifylimit 才可能有优化。另外,collectEntitiesByTypeThe method will be based on the providedBoxIn the initial screening, the chunk andBoxDisjoint entities will be excluded first, this isx、y、z、dx、dy、dzanddistancebasis for optimization. andBoxanddistanceIt will be accurately judged again at the end of the predicate chain.
Return to the above picture, line 161, call EntitySelector#getEntities:

In the logic of this method, when there are multiple entities in the selected result, thesortersort,limitThe parameter constrains the number of returns at the end. When I saw the number of judgment results on line 247, my first reaction was why not writeentities.size() > this.limit, after all, as long as the number of selected entities does not exceedlimit, no matter what the sorting method is, it will not cause entities to be filtered again. After thinking about it carefully, it is because even if the number of results does not exceedlimit, should also be usedsortSorting, which affects subsequent execution order. For exampleexecute as @e[sort=nearest] run xxxAlthough there is no limitlimit, but the developer hopes that all entities will be executed in sequence after being sorted by distance.xxx, so even if the number of results does not reachlimit,sorterIt also needs to be applied.
It’s time to make it delicious~
selector selection flowchart

Select directly with uuid or player name, the fastest;
for
@e、@aselector, if not specifiedsort(not specified is equivalent tosort=arbitrary),butlimitThere is optimization effect. For example, there are now 10,000 entities (9999 cows and 1 sheep). To select this sheep, consider using@e[type=minecraft:sheep,limit=1], if this sheep is lucky to be ranked at the top of the entity list, it will be selected quickly, and then the selector will stop selecting;Right
@e、@a、@p、@rselector specifiedx、y、z、dx、dy、dzanddistanceOne of them will cause the selector to select entities only in the current dimension. When the chunk being loaded comes from multiple dimensions, if you can be sure that the entity to be selected is only in the current dimension, you can consider adding at least one of them;Right
@eselector specifieddx、dy、dzone of them, or specifydistanceAnd there isdistance.max, which will make the selector haveBox(even if not specifiedx、y、z, because they will be assigned the position of the selector's current record). The selector will perform a preliminary screening of chunks before testing all conditions, andBoxEntities in chunks that have no intersection will be filtered out in this step. Note that only specifyingdistance=min..no timeBox, without this optimization; note that only@eSelector has this optimization, for example@a[distance=..11]There is no such optimization;is correct
@eselector specifiedtype= 参数, will be in the previous stepBoxTest immediately after initial screeningtype. Please note thattype=#<类型标签>Invalid; please note that@p、@a、@rSelectors come with this optimization because theirentityTypeWill be automatically assigned to player typeEntityType.PLAYER;Please note that this only has optimization effect, the selectortypeParameters will still be tested in the predicate chain; the last few items ofselector are always tested in order:
x_rotation->y_rotation->level->Box(x、y、z、dx、dy、dz)->distance->sort->limit, except for these parameters, the remaining parameters are tested in order according to the writing order in the selector. Therefore, parameters that can filter more entities should be considered to be placed in a higher position;
is out
is full of gains~Let’s take a look at what a good selector looks like~ __M DNL__The main world, hell, and end of the archive have many chunks being loaded
In three dimensions, each dimension has thousands of entities evenly distributed in different places
In addition, there are 1000 sheep are evenly distributed among all loaded chunks in the main world. Two of the sheep have a temp scoreboard of 88 points. It is known that these two sheep are located a few blocks near the main world birth point (0,0), and one of them has been sheared
. Use this entityselector in the data pack to select this sheared sheep and ask her to say QwQ:
execute as @e[type=minecraft:sheep,distance=..10,scores={temp=88},nbt={Sheared:1b},limit=1] run say QwQThis selector is delicious because:
is useddistance=..10Parameter, this is because it is known that the sheep to be selected is a few blocks near the main world birth point (0,0), heredistanceThe maximum value of 10 is more reasonable and filters out a large number of sheep that are far away.
is usedtypeParameters, entities that are not sheep will be filtered and used by
in this step.limitParameter, once the sheep to be selected is lucky enough to be at the front of the main world's entity list, it will save a lot of subsequent selections
scoresThe parameters are located innbtBefore the parameters, it is a very good way to write, becausenbtParameter matchingnbtThe consumption is large, but only 2 sheep can passscoresinspection, greatly reducing subsequent testingnbtParameter cost.
Too much seasoning will be bad
The end of optimization is deterioration
Myth 1: Parameters that can filter more entities must be placed in a higher position
Generally speaking, parameters that can filter more entities should be placed in a higher position, except for some parameters, such asnbt={...}Parameter, the consumption of this parameter is extremely huge, it will consume all the entities of the entity to be detected.nbtMake a copy, along with the rider listPassengersofnbtwill also be copied recursively, sonbtParameters should be placed at a later position;
Misunderstanding 2: limit=xxx parameters must be good
for@e、@aselector, insortWhen it is arbitrary (i.e. not specified)sort), you can use it if you know the number of entities or have requirements for the number of entities.limitParameters are optimized, but due to the limited number of entities, it may not be easy to detect when the number of entities is large.
For example, when making mini-game maps, it is usually usedmarkerPlace the marked location on the map. if there is only onemarker, can be usedlimit=1Optimized, but if you accidentally generate the samemarker, the selector will always choose one of them, and developers may need to take time to notice this error.
Myth 3: If you have a Box, it must be good
(to avoid viewers who jump to read and don’t understand what it means:Boxrefers to the usedx、dy、dzanddistance=..maxWhen, the selector will first select an entity in a possible area (an optimization behavior)
distance=..10000000It's not good, because this coordinate is too large. Normally, the loaded chunk will not be at such a far location (even if there is, if it is relatively small, it is not recommended to write it like this), so it can hardly exclude many entities, but it increases the cost of initial screening.
In addition, there areBoxIt increases the difficulty of reading the code to a certain extent. For example, even if I know that the entity to be selected is within the coordinate 500 range, but I writedistance=..500It will make people confused, so please indicate it next to the command if necessary.distance=..500is to haveBox.
Repertoire: Tucao mojang
When reading this piece of code, I found several very unpleasant places. See the table summarized above for details.
Some parameters will be verified for existence, and there cannot be duplicate parameters, for example:

cannot be more than onenameIt is normal because entity can only have one name, butscoresIt won’t work:

Okay...can't have more than onescoresI admit it~ buttagThe parameter says: "I can!"

selects the entity that “has at least one tag” and “cannot have any tags”. The contradiction is qwq
and inscoresWithin the parameters, scoreboard can be repeated:

becausescoresThe handler is used when processingHashMapStores the parsing results and does not check for duplicates.HashMapexistkeyIf they are the same, the new value will be overwritten with the old value, soscoresWhen internal duplication occurs, only the later score range is valid, see the table summarized above for details.
then@pThe selector can also coversortandlimitcause it to degenerate into@a, in short, there are many of these magical operations, and mojang doesn’t want to guard against it at all qwq:

I don’t want to complain, I’ve been writing this article for a day, I’m so tired qwq
Thanks for reading~
Original text ↩︎