├── LICENSE ├── README.md ├── assets └── load_balancing │ ├── graph1.png │ ├── graph2.png │ ├── graph3.png │ └── graph4.png └── docs ├── advanced.md ├── basics.md └── lang └── fr-fr ├── advanced.md └── basics.md /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Optimization in .mcfunction 2 | 3 | This repository has for objective to contain guides, explainations and examples of optimization for minecraft datapacks. 4 | 5 | In this repository, a lot of references can be done about messages, threads or ressources from the Minecraft Commands Discord server. This server is a great place to learn about minecraft commands and datapacks. You can join it [here](https://discord.gg/QAFXFtZ). 6 | 7 | ## Table of contents 8 | 9 | ### English 10 | - [Basics & Beginners introduction](./docs/basics.md) 11 | - **[WIP]** [Advanced optimization](./docs/advanced.md) 12 | 13 | ### Français 14 | - [Bases & introduction aux débutants](./docs/lang/fr-fr/basics.md) 15 | 16 | 17 | ## Contribute to this repository 18 | 19 | If you want to contribute to this repository, you can do it by creating a pull request. You can also create an issue if you want to report a problem or suggest something. 20 | 21 | We are discussing about the structure of this repository on the Minecraft Commands Discord server [on this thread](https://discord.com/channels/154777837382008833/1072289159701606511). 22 | 23 | All contributions are welcome. Any contribution must be verified by existing benchmarks or your own tests. Add the source in the adapted section of the same file of the contribution. 24 | 25 | ## License 26 | 27 | This repository is under the Unlicense license. You can read the license [here](./LICENSE). This license is a public domain license. It means that you can do whatever you want with this repository. You can also read the license on the [unlicense website](https://unlicense.org/). -------------------------------------------------------------------------------- /assets/load_balancing/graph1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neylz/opti-mcfunction/ed41180437e6f182d2a9038fb4801b017847c41d/assets/load_balancing/graph1.png -------------------------------------------------------------------------------- /assets/load_balancing/graph2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neylz/opti-mcfunction/ed41180437e6f182d2a9038fb4801b017847c41d/assets/load_balancing/graph2.png -------------------------------------------------------------------------------- /assets/load_balancing/graph3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neylz/opti-mcfunction/ed41180437e6f182d2a9038fb4801b017847c41d/assets/load_balancing/graph3.png -------------------------------------------------------------------------------- /assets/load_balancing/graph4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neylz/opti-mcfunction/ed41180437e6f182d2a9038fb4801b017847c41d/assets/load_balancing/graph4.png -------------------------------------------------------------------------------- /docs/advanced.md: -------------------------------------------------------------------------------- 1 | [← Back to the README](../README.md) 2 | 3 | #### 🌐 Language 4 | **[EN]** [[FR]](lang/fr-fr/advanced.md) 5 | 6 | 7 | # Work in progress 8 | ### This file can contain errors or be incomplete. 9 | 10 | ### Table of contents 11 | - [References and sources](#references-and-sources) 12 | - [Introduction](#introduction) 13 | - [Load Balancing](#load-balancing) 14 | 15 | 16 | # Introduction 17 | 18 | This document is a collection of more advanced methods and techniques that can be used to improve the performance of your datapacks. It is not meant to be a complete guide, but rather a collection of tips and tricks that can be used to improve your datapacks. 19 | 20 | Unlike the [basics of optimization document](./basics.md), this document is not meant to be read from top to bottom. Each section is independent from the others, and you can read them in any order you want. That's why there is no indices to each section. 21 | 22 | The goal is to gather more advanced methods, giving links and references to external sources. The methods can be mcfunction specific, or more general optimization methods of programming. 23 | 24 | # Load Balancing 25 | Load balancing is an extremely important optimization to avoid lag, usually applied to large operations. 26 | 27 | First, let's emphasize one thing: In minecraft, the game is updated 20 times per second. Each update is called a tick. In order for the game to maintain this update rate, each tick must be executed in a maximum of 50 milliseconds. 28 | 29 | Each command we add to our datapack takes a certain amount of time to execute, and we need to make sure that no tick exceeds this 50 millisecond margin. _Of course, how much each computer can process in a tick depends on the computer, but the idea is the same._ 30 | 31 | To demonstrate the problem, I made a datapack that burns all entities exposed to the sky. Every tick, it checks for each entity in the game if it is being exposed to the sky, and if it is, it changes the `Fire` NBT to ignite the entity. In our example, the function of checking if the entity is exposed to the sky is extremely heavy. 32 | 33 | ### The problem 34 | 35 | Let's take a look at the duration per TPS graph (which you can open by pressing Alt+F3) in a local world 36 | 37 | ![Normal TPS](../assets/load_balancing/graph1.png) 38 | 39 | Seems great! Now let's turn on our datapack. Let's check the graph again 40 | 41 | ![Very bad TPS](../assets/load_balancing/graph2.png) 42 | 43 | The green graph is before we turn on the datapack, and the yellow after. That's a bad sign. On some computers or servers, this line can go into the red and cause the server to run in slow motion. This can also go into the red according to datapack growth... 44 | 45 | We need to make the graph as close as possible to the disabled datapack. 46 | 47 | ### Finding the problem 48 | 49 | We need to understand if it is possible to optimize this. In this example case, we already know that the function of detecting if the entity is exposed to the sky is the problem, and it cannot be optimized directly as there are no further optimizations to be done. 50 | 51 | However, there is still a way out. When you no longer have direct optimizations, always ask yourself: does this function really need to be run every tick? 52 | 53 | In our case, it doesn't have to be so instantaneous. If you have a few ticks of delay, it will be fine 54 | 55 | **Well... let's start with the optimizations.** 56 | 57 | ### Trying to solve the problem 58 | 59 | Let's start by doing what most people do to try to solve this: Use `/schedule `. Instead of checking every tick, I will set it to check every 5 ticks. 60 | 61 | Let's start by changing our command from the tick function to a specific function, and trigger the schedule from the load function 62 | 63 | ```mcfunction 64 | #> schedule _5t.mcfunction 65 | execute as @e[type=!player] run function example:try_sky_burn 66 | 67 | schedule function example:schedule _5t 5t replace 68 | ``` 69 | 70 | ```mcfunction 71 | #> on_load.mcfunction 72 | function example:schedule _5t 73 | ``` 74 | 75 | This is already enough to run the function every 5 ticks 76 | 77 | Let's reload take a look at the graphic again 78 | 79 | ![Lag Spikes](../assets/load_balancing/graph3.png) 80 | 81 | Would you say this is acceptable? Wrong! Maybe it's even better, but look at the spikes! The difference from before and now is that before all ticks were slow, now only some. This can still cause lag! 82 | 83 | We need to tone down those spikes, but... how? 84 | 85 | ### The real solution for this problem 86 | 87 | We need to distribute the weight of each spike among all ticks, to make the graph flat and reduce the ms max. 88 | 89 | Instead of checking all entities in just one tick every 5 ticks, we will check 1/5 of all entities every tick, thus uniformly checking a specified amount of entity every tick. 90 | 91 | To implement this system, I will separate the entities into 5 groups. To do this, I will create a score that will cycle between 0 and 4 and will progress with each entity added to the group, and I will give a tag corresponding to the entity's group. _I'm using a tag instead of a score because besides tag verification being faster, minecraft has a bug with scores on despawning entities that can even corrupt your world over time_ 92 | 93 | First, let's add the score to cycle the groups 94 | 95 | ```mcfunction 96 | #> on_load.mcfunction 97 | scoreboard objectives add sky_burn_entity_group dummy 98 | scoreboard players set .current_group sky_burn_entity_group 0 99 | scoreboard players set .current_group_to_assign sky_burn_entity_group 0 100 | scoreboard players set .group_count sky_burn_entity_group 5 101 | ``` 102 | 103 | Next, let's create a function to assign a group tag to an entity 104 | 105 | ```mcfunction 106 | #> assign_group.mcfunction 107 | # Cycling the group 108 | scoreboard players add .current_group_to_assign sky_burn_entity_group 1 109 | scoreboard players operation .current_group_to_assign sky_burn_entity_group %= .group_count sky_burn_entity_group 110 | 111 | # Setting the tag group 112 | execute if score .current_group_to_assign sky_burn_entity_group matches 0 run tag @s add sky_burn_group_0 113 | execute if score .current_group_to_assign sky_burn_entity_group matches 1 run tag @s add sky_burn_group_1 114 | execute if score .current_group_to_assign sky_burn_entity_group matches 2 run tag @s add sky_burn_group_2 115 | execute if score .current_group_to_assign sky_burn_entity_group matches 3 run tag @s add sky_burn_group_3 116 | execute if score .current_group_to_assign sky_burn_entity_group matches 4 run tag @s add sky_burn_group_4 117 | 118 | # Setting a tag indicating that this entity has been grouped 119 | tag @s add sky_burn_has_group 120 | ``` 121 | 122 | Now, let's put in the tick function to assign a group to entities that do not yet have a group 123 | 124 | ```mcfunction 125 | #> on_tick.mcfunction 126 | execute as @e[tag=!sky_burn_has_group] run function example:assign_group 127 | ``` 128 | 129 | This is already enough to give each entity a group. Note that I also removed the schedule functions from the load, since we won't be using it anymore. I also deleted the `schedule _5t.mcfunction` function 130 | 131 | Now, we need to modify our tick to select one of the groups to update, and update the corresponding group. Let's do it. 132 | 133 | ```mcfunction 134 | #> on_tick.mcfunction 135 | # Assign a group to the entities 136 | execute as @e[tag=!sky_burn_has_group] run function example:assign_group 137 | 138 | # Cycle the current group being updated 139 | scoreboard players add .current_group sky_burn_entity_group 1 140 | scoreboard players operation .current_group sky_burn_entity_group %= .group_count sky_burn_entity_group 141 | 142 | # Execute the verification on the entity of that group 143 | execute if score .current_group sky_burn_entity_group matches 0 as @e[tag=sky_burn_group_0] run function example:try_sky_burn 144 | execute if score .current_group sky_burn_entity_group matches 1 as @e[tag=sky_burn_group_1] run function example:try_sky_burn 145 | execute if score .current_group sky_burn_entity_group matches 2 as @e[tag=sky_burn_group_2] run function example:try_sky_burn 146 | execute if score .current_group sky_burn_entity_group matches 3 as @e[tag=sky_burn_group_3] run function example:try_sky_burn 147 | execute if score .current_group sky_burn_entity_group matches 4 as @e[tag=sky_burn_group_4] run function example:try_sky_burn 148 | ``` 149 | 150 | Now, let's reload and check the graph... and if everything is still working, of course 151 | 152 | ![TPS With appropriate load balancing](../assets/load_balancing/graph4.png) 153 | 154 | Much better, and everything still works! Each individual entity is checked every 5 ticks, but not all at the same time. 155 | 156 | Note that the spikes in this graph are from the game itself, not the fault of our code. 157 | 158 | We have successfully load balanced by distributing the load between the ticks! Yay 🥳🥳 159 | 160 | _The more attentive and experienced readers will realize that checking if an entity is exposed to the sky is something trivial using `positioned over`. I added the line `execute as @e as @e as @e` at the end of the function just to make it heavy and be able to better demonstrate things ;)_ 161 | 162 | ### When not to use Load Balancing 163 | 164 | You probably noticed that although we have applied load balancing correctly, our datapack has a much higher overhead since it needs to manage and execute commands based on groups. In this case, it was a good thing, as it made the datapack more efficient. But it may not always be so 165 | 166 | If you have applied load balancing correctly and the max ms of the graph is still greater than without it, then the overhead is significantly heavier than the operation and you should not apply this. In other words, do not use it if it does not bring a performance benefit. 167 | 168 | ### Another way to solve (but it is inconsistent) 169 | 170 | Another way to solve would be to use a random predicate and select entities based on that, for example 171 | 172 | ```mcfunction 173 | #> function beign executed as a entity 174 | execute if predicate example:20_percent_chance run function example:random_entity_check 175 | ``` 176 | 177 | Mathematically, 20% chance per entity would be dividing 1/5 of the entities per run, with almost no overhead and with a lot of simplicity, correct? 178 | 179 | Well... kinda. The problem with this is **inconsistency**. Although on average it will actually execute on 1/5 of the entities, this is not always the case. There can be ticks that many entities are executed at the same time, and there can be ticks that almost no entities are executed! This means that commands can cause **random spikes**! 180 | 181 | In this example case, I set 5 ticks of delay for each entity, but in a real scenario I would like a precise delay of 20 ticks interval, to be able to align with the fire damage time and cause 1 damage per second. So, in this scenario, I could not use randomness at all. 182 | 183 | But, because of the huge overhead reduction, this can actually be an optimization possibility. I would particularly avoid using randomness because of inconsistency and prefer options that give me real control of what is happening. Keep in mind that we want the graphic flat, not full of waves. As a developer, it is up to you to analyze and see if it is worth using or not in your case. 184 | 185 | ### Conclusion 186 | 187 | We should use load balancing to distribute the weight of our commands by spreading them across ticks. 188 | 189 | Knowing this, keep in mind that this is not the only possible use of load balancing. You might want to spread your commands in another way, for example by modifying large areas of blocks separating them into small parts over several ticks. You might also want to have 2 schedules that execute every 2 ticks intercalated with each doing a completely different or intertwined function. The important thing is to distribute the weight of your commands between the tick to keep the graph apartment and avoid tick overload! 190 | 191 | # References and sources 192 | 193 | 194 | [← Back to the README](../README.md) 195 | -------------------------------------------------------------------------------- /docs/basics.md: -------------------------------------------------------------------------------- 1 | [← Back to the README](../README.md) 2 | 3 | #### 🌐 Language 4 | **[EN]** [[FR]](lang/fr-fr/basics.md) 5 | 6 | 7 | ### Table of contents 8 | 9 | - [0. Preface](#0-preface) 10 | - [1. Introduction](#1-introduction) 11 | * [What should we optimize ?](#what-should-we-optimize-) 12 | * [How optimize by yourself ?](#how-optimize-by-yourself-) 13 | - [2. The mcfunction programming language](#2-the-mcfunction-programming-language) 14 | * [2.1. Selectors](#21-selectors) 15 | + [2.1.1. Which target selector choosing ?](#211-which-target-selector-choosing-) 16 | + [2.1.2. Selectors' arguments efficiency](#212-selectors-arguments-efficiency) 17 | + [2.1.3. In what order to put the arguments?](#213-in-what-order-to-put-the-arguments) 18 | * [2.2. `execute if` selection](#22-execute-if-selection) 19 | * [2.3. `as @e[scores={}]` or `as @e if score @s`](#23-as-escores-or-as-e-if-score-s) 20 | * [2.4. Tagging instead of testing NBT](#24-tagging-instead-of-testing-nbt) 21 | * [2.5. Predicates](#25-predicates) 22 | - [3. Datapack Structure](#3-datapack-structure) 23 | * [3.1. Running functions only when needed](#31-running-functions-only-when-needed) 24 | + [3.1.1. Tick functions](#311-tick-functions) 25 | + [3.1.2. Schedule functions](#312-schedule-functions) 26 | * [3.2. Advancements and Events](#32-advancements-and-events) 27 | * [3.3. Functions and Structure](#33-functions-and-structure) 28 | - [4. Summary and Important Points](#4-summary-and-important-points) 29 | - [5. References and sources](#5-references-and-sources) 30 | 31 | 32 | 33 | 34 | 35 | # 0. Preface 36 | 37 | 38 | Here is the big question: How can we optimize our code in datapacks? This crucial question has the potential to transform the outcome of datapacks causing the game to crash into improved performance content. 39 | 40 | 41 | I realized the problem of the low documentation of optimizing datapacks when someone came into the Minecraft Commands discord with a datapack that was crashing the game because of its low optimization. 42 | 43 | 44 | So here I will talk only about basic optimization. 45 | 46 | 47 | This gist has the goal of being updated in the future with your feedback to be improved. So, feel free to discuss it on MCC discord! 48 | 49 | 50 | # 1. Introduction 51 | 52 | 53 | There are different levels of optimization, but like I said above, **I will not talk here about maths optimization**. 54 | I will divide this gist into two principal sections : 55 | * The mcfunction language 56 | * Datapack Structure 57 | 58 | 59 | ### What should we optimize ? 60 | 61 | Optimization is important because it allows the creation of larger programs without negatively impacting the user experience. The goal is not to strive for perfection, as this can hinder productivity. Instead, The goal is to be realistic about the circumstances and choose the best option. Different levels of optimization provide multiple options to choose from. 62 | 63 | In this gist, you will find some key points that will provide you with more options. 64 | 65 | ### How optimize by yourself ? 66 | 67 | This gist provides an overview of how the mcfunction language works, but sometimes these guidelines may not be sufficient and you may need to conduct your own tests. There are several tools available for this purpose. One such tool is Alt + F3, which displays the evolution of your TPS (ticks per second) and MSPT (milliseconds per tick). 68 | 69 | MSPT is the most important value, as it represents the time it takes the game to calculate a single tick. A lower MSPT value is better. It is more important than TPS, as TPS is capped at 20 while MSPT is not. 70 | 71 | You can also use F3 + L in single player mode to profile the performance of your code. This will allow you to analyze the results with the misode's [report inspector](https://misode.github.io/report/). On server, use the command `/profile` but the results will be not as complete as in singleplayer. 72 | 73 | 74 | 75 | # 2. The mcfunction programming language 76 | 77 | 78 | In this section, you can find optimization using a language approach. In other terms, there are some structures to absolutely avoid in your code. 79 | 80 | 81 | 82 | ## 2.1. Selectors 83 | 84 | 85 | The selectors are essential in mcfunction. There are many reasons and justifications for why they are so important, but did you know that they can have a performance impact depending on how you use them? 86 | 87 | 88 | ### 2.1.1. Which target selector choosing ? 89 | 90 | 91 | `@e` is the generic selector, it selects all entities. To this selector you can add target selectors (I will not handle all of them; you can find them all [here on the wiki](https://minecraft.wiki/w/Target_selectors)). So to recap, `@e[type=minecraft:player]` will select only players. 92 | 93 | We can find properties of `@e` in other selectors, but they still have their own properties: 94 | 95 | >- `@a`, equivalent of `@e[type=minecraft:player]` but is faster and can points to dead players where `@e` can't 96 | >- `@p`, equivalent of `@a[sort=nearest,limit=1]` or `@e[type=minecraft:player,sort=nearest,limit=1]` but faster 97 | >- `@r`, equivalent of `@a[sort=random,limit=1]` or `@e[type=minecraft:player,sort=random,limit=1]` 98 | >- `@s` is the exception - even if simmilar to @p - it only detects the source. If the source isn't an entity (like a command block or the console), no entity is selected. 99 | 100 | 101 | 102 | ### 2.1.2. Selectors' arguments efficiency 103 | 104 | 105 | Let's go back to optimization; the first important point is to select an entity **only when you need it**. Let me develop: 106 | 107 | 108 | Selectors can have different impacts on performance depending on their nature, so it's really important to choose them according to the circumstances. Here is a list of selectors' arguments organized by categories and their impact in order to refine our selection and select only the pertinent entities. 109 | 110 | 111 | 112 | | Category | Selectors Arguments | Direct Performance Impact (compared to `@e`) | Comment | Global Impact description when used | 113 | |--|--|--|--|--| 114 | | Selection | `limit` | negligeable | / | improves performances | 115 | | Selection | `sort` | low | `sort=arbitrary` is implicit in almost all commands | improves performances | 116 | | Entity type | `type` | negligeable | Should be used each time you can sepcify it (*cf. **2.1.3.***) | improves performances | 117 | | Position | `x`, `y`, `z`, `dx`, `dy`, `dz`, `distance` | negligeable | Should be used on large sets of entities | improves a bit performances | 118 | | Scoreboard values | `tag`, `team`, `scores` | low - medium | To the most efficient to the less one, they are good selectors agrument to refine your selection (*cf. **2.1.3.***), you should use them when possible | improves performances | 119 | | Predicate | `predicate` | depends of what's inside | better than `NBT/Player Data` checks unless the NBT condition is used depends | 120 | | NBT/Player Data | `advancement`, `name`, `nbt` | strong | to avoid (use a string parser, which is very heavy) | decreases performances | 121 | 122 | 123 | The last column indicates if it is worth to use or not, the argument to refine the selection. IE: Does the performance cost of this argument preferable to the one involved in executing the following command? 124 | 125 | So, in the list above, you have the majority of arguments available, sorted from the slightest ones to the heaviest ones. `Selection`, `Position` and `Rotation` categories don't have a significant impact. 126 | 127 | 128 | `Entity type` should be — as far as possible — always present. It has been shown that the game always checks the mob type with the selectors [[link to MCC's discord]](https://discord.com/channels/154777837382008833/154777837382008833/985503145239142461). 129 | 130 | 131 | `Scoreboard values` should be — once again as far as possible — used to restrain the number of entities selected by the selector. The performance impact is low, and even if it is not null, it is still better than executing the rest of the command for entities that we don't want. 132 | 133 | 134 | If there was only one rule to respect, limit your usage of the arguments in the `NBT/Player Data` category. It will strongly negatively impact your performances. Using `Predicate` is better than NBT but is still code heavy, so use these categories sparingly. 135 | 136 | `sort` decreases performances if set to anything other than default `arbitrary` 137 | 138 | 139 | ### 2.1.3. In what order to put the arguments? 140 | 141 | 142 | So I said that the main point was to limit the damages when using NBTs. But how? The selectors have an order. For example: `@e[type=minecraft:bee, nbt={HasNectar:1b}]` is a way more optimized than `@e[nbt={HasNectar:1b}, type=minecraft:bee]`. Because in the first case, the game will check the NBT value of only the bees; whereas in the second case, the game is checking for all entities if they have nectar, and only after that will check if the entity is a bee. So what's the order? 143 | 144 | 145 | `Selection`, `Position` and `Rotation` categories have no order with a consequent impact. 146 | 147 | 148 | As mentioned in the board of ***2.1.2.***, `type` should be used whenever possible. Same thing for `tag`. 149 | 150 | 151 | With these details, we can now determine the best order: 152 | - `type` 153 | - `tag` 154 | - `scores` 155 | - `level` 156 | - `gamemode` 157 | - `name` 158 | - `advancements` 159 | - `predicate` 160 | - `nbt` 161 | 162 | > `type` is always checked first regardless of order when an exact entity (not with entity tags). Additionally, this check uses an efficient class type check which is more performant than any other check. *(cf. **5.7.**)* 163 | 164 | ## 2.2. `execute if` selection 165 | The execute commands permits to verify conditions with the `if` argument [[ wiki ](https://minecraft.wiki/w/Commands/execute)]. 166 | 167 | 168 | In addition to the entity selectors, you can — and it is recommanded — to use the `if` argument. There is a variant of the `if` argument which is `unless` equivalent to "if not". 169 | 170 | 171 | In 1.19.3, you can test with it: 172 | | condition type | Description | 173 | |--|--| 174 | | biome | Test a biome | 175 | | block | Test one block | 176 | | blocks | Test an area of blocks | 177 | | data | Test the existence of an NBT | 178 | | entity | Test the existence of at least one entity with the specified tag | 179 | | predicate | Test if the predicate is true | 180 | | score | Test if the score matches a specified integer or can test the relation between two scorers | 181 | 182 | 183 | 184 | 185 | ## 2.3. `as @e[scores={}]` or `as @e if score @s` 186 | 187 | 188 | 189 | These two commands below have the same effect (they will give the same context to the following command). 190 | ```hs 191 | execute if score @s matches run ... 192 | ``` 193 | ```hs 194 | execute as @e[scores=] run ... 195 | ``` 196 | So which one of those is the most efficient? 197 | 198 | 199 | According to the analysis posted a few months ago on the MCC's reddit by u/Wooden_chest (*cf. reference 6*), the performance delta between the two cases is too small to be considered. But sometimes, the `@e[scores={}]` syntax is preferable because it can avoid checking next arguments. 200 | 201 | _eg:_ 202 | ```hs 203 | execute as @e[type=item,scores={objective=10..},nbt={custom:1b}] run ... 204 | ``` 205 | 206 | Here, like explained in 2.1.3., the order of arguments is important. So using the `if score` syntax will slow down our code. 207 | 208 | To summarize, `as @e[scores={}]` is more efficient than `as @e if score @s` but in reality the optimization is so minimal compared to the rest that you will never see the impact. 209 | 210 | 211 | ## 2.4. Tagging instead of testing NBT 212 | 213 | 214 | In some cases, it is possible to not check the nbt for each tick and instead use tags. It is really case-specific and must be adjusted in each situation. 215 | 216 | 217 | So let's do an example: 218 | 219 | 220 | We want to create some particles on some custom items which have the custom NBT tag `{customItem:1b}`. 221 | 222 | 223 | Then let's create our tick function (we should use the loop function instead, but let's keep this example simple. For loop functions, *cf **3.1.***). 224 | 225 | 226 | ```hs 227 | # # # # # # # # # # # # # # # # # # # # # # # 228 | # foo:tick                                  # 229 | #                                           # 230 | # function executed each tick               # 231 | # # # # # # # # # # # # # # # # # # # # # # # 232 | 233 | 234 | # check the items that have NEVER been checked before, unless they have the custom tag; they are tagged with foo_ignore in order to not be checked anymore 235 | execute as @e[type=item,tag=!foo_ignore,tag=!foo_custom,nbt=!{Item:{tag:{customItem:1b}}}] run tag @s add foo_ignore 236 | 237 | 238 | # Then all items not yet tagged with foo_ignore are in all case items having the customItem tag 239 | # So we can just tag them 240 | tag @e[type=item,tag=!foo_ignore] add foo_custom 241 | 242 | 243 | # Now we are able to select the entity with a tag and not with a NBT 244 | execute as @e[type=item,tag=foo_custom] at @s run particle ... 245 | ``` 246 | 247 | 248 | So the goal of this method is to limit the usage of the selector argument `nbt` and instead use tags. This technique should be used in conjunction with the one described in section ***3.1.1.***. 249 | 250 | 251 | ## 2.5. Predicates 252 | 253 | The impact performance indicated in the section ***2.1.2.*** is a generalization which must be explained. The content of prerdicates is as variable as its performance impact. In fact, using the `nbt` field in the predicate is less efficient than using `nbt=` selector argument. 254 | 255 | So if a nbt check can be replaced by a predicate using other fields than `nbt` in order to replace a `nbt` argument can mokre efficient in most of cases. 256 | 257 | Eg: using a predicate in order to check the item in the mainhand is preferable than using the nbt `SelectedItem`. 258 | 259 | 260 | # 3. Datapack Structure 261 | 262 | 263 | Datapacks are not only mcfunction files; their structure is important, and there are other features to datapacks that permit you some optimization without using functions. 264 | 265 | 266 | ## 3.1. Running functions only when needed 267 | 268 | 269 | ### 3.1.1. Tick functions 270 | These functions are running every tick, so be careful when putting commands in them. The majority of lags from datapacks come from (too many) useless functions put in these files. 271 | 272 | 273 | ### 3.1.2. Schedule functions 274 | Schedule functions are amazing! 275 | They can permit you to run slower loops than tick functions. 276 | 277 | 278 | In fact, a lot of functions don't require to be executed each tick and can be satisfied with being run only once every two ticks. Using this method, you can reduce your lag almost by two. Of course, you can use slower functions to reduce lag even further. 279 | 280 | 281 | #### How to use schedule loops ? 282 | ```hs 283 | # # # # # # # # # # # # # # # # # # # # # # # 284 | # foo:load                                  # 285 | #                                           # 286 | # function executed when the pack is loaded # 287 | # # # # # # # # # # # # # # # # # # # # # # # 288 | 289 | 290 | schedule function foo:loop2t 2t 291 | ``` 292 | ```hs 293 | # # # # # # # # # # # # # # # # # # # # # # # 294 | # foo:loop2t                                # 295 | #                                           # 296 | # loop function running every two ticks     # 297 | # # # # # # # # # # # # # # # # # # # # # # # 298 | 299 | 300 | # execute your commmands here 301 | 302 | 303 | 304 | # schedule the next itteration 305 | schedule function foo:loop2t 2t 306 | ``` 307 | The concept is pretty simple; we are scheduling our function a first time at the load of our datapack, and then the function will schedule itself when executed. 308 | 309 | 310 | 311 | ## 3.2. Advancements and Events 312 | Another way to replace commands running from `minecraft/tags/tick.json` is to use the Minecraft advancements. We can consider them an equivalent of events in other programming languages. Indeed, advancements are allowing the use of functions as rewards. 313 | 314 | 315 | So we can use this functionality to use built-in conditions for advancements. 316 | 317 | 318 | You can find the full list of triggers on [this page](https://minecraft.wiki/w/Advancement/JSON_format#List_of_triggers) of the wiki. 319 | 320 | ⚠️ Using the `minecraft:tick` crtiteria **will not** optimize your code at all. 321 | 322 | 323 | #### An example to illustrate: 324 | Let's detect when a player kills a zombie and make him say make him say "Task completed!". 325 | 326 | 327 | 328 | Instead of using a scoreboard with the `minecraft.killed:minecraft.zombie` criteria and checking if the score changed, we are going to use advancements. 329 | 330 | 331 | First, let's setup our advancement: 332 | `foo/advancement/triggerkillszombie.json` 333 | ```json 334 | { 335 | "criteria": { 336 | "requirement": { 337 | "trigger": "minecraft:player_killed_entity", 338 | "conditions": { 339 | "entity": { 340 | "type": "minecraft:zombie" 341 | } 342 | } 343 | } 344 | }, 345 | "rewards": { 346 | "function": "foo:zombiekilled" 347 | } 348 | } 349 | ``` 350 | You can notice the presence in the reward of the function `foo:zombiekilled`. So let's create this function: 351 | ```hs 352 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 353 | # foo:zombiekilled                                  # 354 | #                                                   # 355 | # function executed when a player kills a zombie    # 356 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 357 | 358 | 359 | # execute your commmands here 360 | say Task completed! 361 | 362 | 363 | # revoke the advancement 364 | advancement revoke @s only foo:triggerkillszombie 365 | ``` 366 | NB: When the function is executed, the function is executed in the context of the player who made the advancement. That means that you can use `@s` in order to select the player. 367 | 368 | 369 | As we are revoking the advancements after executing our commands, we are "reactivating" the trigger of the advancement. 370 | 371 | 372 | 373 | ## 3.3. Functions and Structure 374 | Let's talk about the functions and structure. Even if it isn't recommended to put all your functions in the same folder, we will not talk about that here beyond this recommendation because it has no impact on the performance of your code. We are going to talk in this part about the appeal of function in other functions. In fact, you can significantly improve the performance of your data pack. 375 | 376 | 377 | You can use as below a function as a procedure, in order to execute multiple times the same group of commands. 378 | ```hs 379 | function foo:myfunction 380 | ``` 381 | 382 | 383 | Furthermore, the command `/function` keeps the context. So that means that instead of this example: 384 | ```hs 385 | # exemple 1 386 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command1 387 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command2 388 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command3 389 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command4 390 | ``` 391 | 392 | 393 | You can use this version, where you will divide your function into two parts: 394 | 395 | 396 | ```hs 397 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 398 | # foo:exemple2                                      # 399 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 400 | 401 | 402 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run function foo:exemple2-procedure 403 | ``` 404 | ```hs 405 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 406 | # foo:exemple2-procedure                            # 407 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 408 | 409 | 410 | command1 411 | command2 412 | command3 413 | command4 414 | ``` 415 | 416 | 417 | The goal is to check the condition imposed by the selector only once instead of four times. As mcfunction is an imperative programming language, the order of the functions will still be the same, and the result will be exactly the same between examples 1 and 2. 418 | 419 | It is recommended to use this structure in the cases where there is a lot of subcommands, or the selectors/conditions is complex. 420 | If the condition is simple and the number of subcommands is pretty low, the cost of calling a function will be higher than checking two times the condition. 421 | 422 | 423 | # 4. Summary and Important Points 424 | 425 | 426 | - The arguments of selectors are ordered. 427 | - Modifying data with "/data" necessitates a high level of performance. 428 | - Instead of using the same selector multiple times, use a function. 429 | - Use the tick function when it is absolutely necessary. 430 | - Use advancements as events or triggers. 431 | - If possible, use tags checks instead of nbt checks in tick/loop functions. 432 | - Don't forget to run by yourself profiling which will permit you to find what is the most unefficient part of your code. 433 | - Focus in a first time on the laggiest parts of your code. Start by optimizing `as @e if score @s` is unefficient. 434 | 435 | 436 | 437 | # 5. References and sources 438 | All references and credits for anyone who participated directly or indirectly in this gist can be found here. 439 | 440 | 441 | 442 | ### Sources & References 443 | 1. [@Dominexis](https://github.com/Dominexis) selectors efficiency analysis [[ GSheet ](https://docs.google.com/spreadsheets/d/1Z0XVvyfzVSGstmpLSMKnwlxwYg8N2ZFl3Xmh0ZV0yZU/edit#gid=0)] [[ Original post ](https://discord.com/channels/154777837382008833/154777837382008833/1031977637620498462)] 444 | 2. MCP-Reborn [[ Github Repo ](https://github.com/Hexeption/MCP-Reborn)] 445 | 3. Minecraft Commands' Discord server [[ link ](https://discord.gg/QAFXFtZ)] 446 | 4. [@Misode](https://github.com/misode) McMeta repo [[ Github Repo ](https://github.com/misode/mcmeta)] 447 | 5. Minecraft Wiki [[ link ](https://minecraft.wiki/w/Minecraft_Wiki)] 448 | 6. [u/Wooden_chest](https://www.reddit.com/user/Wooden_chest/) performance tests [[ link ](https://www.reddit.com/r/MinecraftCommands/comments/w4vjs3/whenever_i_create_datapacks_i_sometimes_do/)] 449 | 7. [@capitalists#1171](https://discordapp.com/users/217271293668622344) `type` argument is allways checked (message on MinecraftCommands discord) [ link to message ](https://discord.com/channels/154777837382008833/154777837382008833/985503145239142461) 450 | 451 | 452 | ### Special thanks 453 | - [@Dominexis](https://github.com/Dominexis) for his precious tips and advised indications 454 | - [@Misode](https://github.com/misode) for checking the code about `name` selector argument efficiency 455 | - [@capitalists#1171](https://discordapp.com/users/217271293668622344) for checking the code about `type` argument 456 | 457 | #### Edits: 458 | - [@BluePsychoRanger](https://github.com/BluePsychoRanger) edit about `sort` argument, code rectification 459 | - [@CosmicAxolotl](https://github.com/CosmicAxolotl) precision about @a and dead players 460 | - [@ICY105](https://github.com/ICY105) specification about `type` argument usage 461 | 462 | 463 | 464 | [← Back to the README](../README.md) 465 | -------------------------------------------------------------------------------- /docs/lang/fr-fr/advanced.md: -------------------------------------------------------------------------------- 1 | [← Back to the README](../../../README.md) 2 | 3 | #### 🌐 Langue 4 | [[EN]](../../advanced.md) **[FR]** 5 | 6 | Ce fichier n'a pas encore été traduit en français. Si vous souhaitez apporter vore soutient, ouvrez un pull request! 7 | 8 | [← Back to the README](../../../README.md) 9 | -------------------------------------------------------------------------------- /docs/lang/fr-fr/basics.md: -------------------------------------------------------------------------------- 1 | [← Back to the README](../../../README.md) 2 | 3 | #### 🌐 Langue 4 | [[EN]](../../basics.md) **[FR]** 5 | 6 | ### Table of contents 7 | 8 | - [0. Preface](#0-preface) 9 | - [1. Introduction](#1-introduction) 10 | * [Que devons-nous optimiser ?](#que-devons-nous-optimiser-) 11 | * [Comment optimiser par soi-même ?](#comment-optimiser-par-soi-même-) 12 | - [2. Le langage de programmation mcfunction](#2-le-langage-de-programmation-mcfunction) 13 | * [2.1. Sélecteurs](#21-sélecteurs) 14 | + [2.1.1. Quel sélecteur choisir ?](#211-quel-sélecteur-choisir-) 15 | + [2.1.2. Efficacité des arguments des sélecteurs](#212-efficacité-des-arguments-des-sélecteurs) 16 | + [2.1.3. Dans quel ordre placer les arguments ?](#213-dans-quel-ordre-placer-les-arguments-) 17 | * [2.2. Séléction avec `execute if`](#22-séléction-avec-execute-if) 18 | * [2.3. `as @e[scores={}]` or `as @e if score @s`](#23-as-escores-ou-as-e-if-score-s) 19 | * [2.4. Utiliser des tags plutôt que tester un NBT](#24-utiliser-des-tags-plutôt-que-tester-un-nbt) 20 | * [2.5. Predicates](#25-predicates) 21 | - [3. Structure du Datapack](#3-structure-du-datapack) 22 | * [3.1. Executer des fonctions seulement quand il y a besoin](#31-executer-des-fonctions-seulement-quand-il-y-a-besoin) 23 | + [3.1.1. Fonctions Tick](#311-fonctions-tick) 24 | + [3.1.2. Fonctions Schedule](#312-fonctions-schedule) 25 | * [3.2. Advancements et Events](#32-advancements-et-events) 26 | * [3.3. Fonctions et Structure](#33-fonctions-et-structure) 27 | - [4. Récapitulatif et points importants](#4-récapitulatif-et-points-importants) 28 | - [5. Sources et Références](#5-sources-et-références) 29 | 30 | 31 | 32 | 33 | 34 | # 0. Preface 35 | 36 | 37 | Là est la grande question: Comment pouvons nous optimiser notre code dans les datapacks ? C'est une question cruciale qui à le potentiel de pouvoir améliorer des datapacks qui faut crasher le jeu à du contenu très performant. 38 | 39 | 40 | J'ai réalsisé le problème de la faible documentation de l'optimisation des datapacks quand quelqu'un est venu sur le serveur discord Minecraft Commands avec un datapack qui faisait crash son jeu à cause de la mauvaise optimisation. 41 | 42 | 43 | Donc ici je ne parlerais que des bases de l'optimisation. 44 | 45 | 46 | Ce repo github à la but d'êtrre mis a jour dans le futur et d'être amélioré. Donc sentez vous libre de rejoindre le discord de MCCmmds pour en discuter! 47 | 48 | 49 | 50 | # 1. Introduction 51 | 52 | 53 | Il existe différents niveaux d'optimisation, mais comme je l'ai dit précédemment, **je ne parlerai pas ici d'optimisation mathématique**. Je vais diviser ce résumé en deux sections principales : 54 | 55 | * Le langage mcfunction 56 | * La structure du datapack 57 | 58 | 59 | ### Que devons-nous optimiser ? 60 | 61 | L'optimisation est importante car elle permet la création de programmes plus importants sans affecter négativement l'expérience utilisateur. L'objectif n'est pas de viser la perfection, car cela peut entraver la productivité. Au lieu de cela, l'objectif est d'être réaliste concernant les objectifs et de choisir la meilleure option. Différents niveaux d'optimisation offrent plusieurs options à choisir. 62 | 63 | Dans ce document, vous trouverez quelques points clés qui vous fourniront plus d'options. 64 | 65 | ### Comment optimiser par soi-même ? 66 | 67 | Ce résumé donne un aperçu du fonctionnement du langage mcfunction, mais parfois ces directives peuvent ne pas être suffisantes et vous pourriez avoir besoin de mener vos propres tests. Il existe plusieurs outils disponibles à cette fin. Notament Alt + F3, qui affiche l'évolution de votre TPS (ticks par seconde) et MSPT (millisecondes par tick). 68 | 69 | La valeur MSPT est la plus importante, car il représente le temps nécessaire au jeu pour calculer un seul tick. Un MSPT bas est le meilleur. Il est plus importante que le TPS, car le TPS est limité à 20 alors que le MSPT n'a pas de limite finie. 70 | 71 | Vous pouvez également utiliser F3 + L en mode solo pour profiler les performances de votre code. Cela vous permettra d'analyser les résultats avec le [report inspector](https://misode.github.io/report/) fait par misode. Sur le serveur, utilisez la commande /profile, mais les résultats ne seront pas aussi complets qu'en mode solo. 72 | 73 | 74 | # 2. Le langage de programmation mcfunction 75 | 76 | 77 | Dans cette section, vous pouvez trouver l'optimisation en utilisant une approche basée sur le langage. En d'autres termes, il y a des structures à éviter absolument dans votre code. 78 | 79 | 80 | ## 2.1. Sélecteurs 81 | 82 | 83 | Les sélecteurs sont essentiels dans mcfunction. Il y a de nombreuses raisons et justifications pour lesquelles ils sont si importants, mais saviez-vous qu'ils peuvent avoir un impact sur les performances en fonction de la manière dont vous les utilisez ? 84 | 85 | 86 | ### 2.1.1. Quel sélecteur choisir ? 87 | 88 | 89 | `@e` est le sélecteur générique, il sélectionne toutes les entités. À ce sélecteur, vous pouvez ajouter des sélecteurs de cible (vous pouvez les trouver tous [ici sur le wiki [en]](https://minecraft.wiki/w/Target_selectors)). Donc, pour récapituler, @e[type=minecraft:player] sélectionnera uniquement les joueurs. 90 | 91 | 92 | On peut trouver des propriétés de @e dans d'autres sélecteurs, mais ils ont toujours leurs propres propriétés : 93 | 94 | >- `@a`, un équivalent de `@e[type=minecraft:player]` mais est plus rapide et peut séléctionner les joueurs morts, ce que `@e` ne peut pas faire 95 | >- `@p`, un équivalent de `@a[sort=nearest,limit=1]` ou `@e[type=minecraft:player,sort=nearest,limit=1]` mais plus rapide 96 | >- `@r`, un équivalent de `@a[sort=random,limit=1]` ou `@e[type=minecraft:player,sort=random,limit=1]` 97 | >- `@s` est l'exception - bien que similaire à `@p` - elle détecte uniquement la source. Si la source n'est pas une entité (comme un bloc de commande ou la console), aucune entité n'est sélectionnée. 98 | 99 | 100 | 101 | ### 2.1.2. Efficacité des arguments des sélecteurs 102 | 103 | 104 | Revenons à l'optimisation ; le premier point important est de sélectionner une entité **uniquement lorsque vous en avez besoin**. Laissez-moi développer : 105 | 106 | 107 | Les sélecteurs peuvent avoir différents impacts sur les performances en fonction de leurs arguments, il est donc très important de choisir ces derniers en fonction des circonstances. Voici une liste des arguments des sélecteurs organisés par catégories et leur impact afin d'affiner notre sélection et de ne sélectionner que les entités pertinentes. 108 | 109 | 110 | 111 | | Catégorie | Arguments de Sélecteur | Impact sur les performances direct (comparé à `@e`) | Commentaire | Description d'impact global quand utilisé | 112 | |--|--|--|--|--| 113 | | Sélection | `limit` | négligeable | / | améliore les performances | 114 | | Sélection | `sort` | faible | `sort=arbitrary` est implicite dans presque toutes les commandes | améliore les performances | 115 | | Type d'entité | `type` | négligeable | Doit être utilisé chaque fois que vous pouvez le spécifier (*cf. **2.1.3.***) | améliore les performances | 116 | | Position | `x`, `y`, `z`, `dx`, `dy`, `dz`, `distance` | négligeable | Devrait être utilisé sur de grands ensembles d'entités | améliore légèrement les performances | 117 | | Valeurs de scoreboard | `tag`, `team`, `scores` | faible - moyen | De la plus efficace à la moins efficace, ce sont de bons arguments de sélecteur pour affiner votre sélection (*cf. **2.1.3.***), vous devriez les utiliser dès c'est possible | améliore les performances | 118 | | Prédicat | `predicate` | dépend des conditions du prédicat | meilleur que `NBT/Player Data` sauf si utilisation de la condition `NBT` | dépends de son contenu | 119 | | NBT/Player Data | `advancement`, `name`, `nbt` | forte | à éviter (utilise un analyseur de chaînes de caractères, ce qui est plutôt louf) | déteriore les performances | 120 | 121 | 122 | La dernière colonne indique s'il vaut la peine d'utiliser ou non l'argument pour affiner la sélection. Par exemple : Le coût de performance de cet argument est-il préférable à celui impliqué dans l'exécution de la commande suivante ? 123 | 124 | 125 | Ainsi, dans la liste ci-dessus, vous avez la majorité des arguments disponibles, classés des plus légers aux plus lourds. Les catégories `Sélection`, `Position` et `Rotation` n'ont pas d'impact significatif. 126 | 127 | 128 | `Type d'entité` devrait toujours être présent, autant que possible. Il a été démontré que le jeu check toujours le type du mob que l'argument soit spécifé ou non [[lien vers le discord de MCCmmds]](https://discord.com/channels/154777837382008833/154777837382008833/985503145239142461). 129 | 130 | 131 | `Valeurs de scoreboard` devrait être — encore une fois — utilisé le plus possbile pour limiter le nombre d'entitées sélectionnées par notre sélecteur. 132 | 133 | 134 | Si il n'y avais qu'une seule règle à respecter, limitez votre usage des arguments de la catégorie `NBT/Player Data`. Leur utilisation impacte grandement les performances. 135 | 136 | `sort` diminue les performances si défini sur autre chose que `arbitrary`. 137 | 138 | 139 | ### 2.1.3. Dans quel ordre placer les arguments ? 140 | 141 | 142 | Donc je disais que le point le plus important était de limiter les dommages quand on utilisais les NBTs. Mais comment ? Les sélecteurs ont un ordre. Par exemple: `@e[type=minecraft:bee, nbt={HasNectar:1b}]` est bien plus optimisé que `@e[nbt={HasNectar:1b}, type=minecraft:bee]`. C'est parce que dans le premier cas, le jeu ne check le NBT que des abeilles et ignorera les autres entitées; alors que dans le second cas, le jeu regardera le NBT de toutes les entitées de la map, puis sélectionnera parmis elles uniquement les abeilles. 143 | 144 | Mais alors quel est l'ordre? 145 | 146 | L'ordre des arguments des catégories `Sélection`, `Position` et `Rotation` n'a pas d'impact significatif. 147 | 148 | Comme mentionné dans le tableau de la section ***2.1.2.***, `type` devrait être utilisé dès que possible. Pareil pour `tag`. 149 | 150 | 151 | Avec ces détails, on peut déterminer d'un ordre à respecter: 152 | - `type` 153 | - `tag` 154 | - `scores` 155 | - `level` 156 | - `gamemode` 157 | - `name` 158 | - `advancements` 159 | - `predicate` 160 | - `nbt` 161 | 162 | > `type` est toujours à check en premier, quel que soit l'ordre. De plus, cet argument est plus efficace que n'importe quel autre check. *(cf. **5.7.**)* 163 | 164 | ## 2.2. Séléction avec `execute if` 165 | 166 | La commande /execute permets de vérifier des conditions avec l'argument `if` [[ wiki ](https://minecraft.wiki/w/Commands/execute)]. 167 | 168 | En plus du sélécteur d'entitées, vous pouvez — et il est recommandé — d'utiliser l'agument `if`. Il y a une variante `unless` qui est son contraire, un "if not". 169 | 170 | 171 | En 1.19.3, vous pouvez faire des conditions avec: 172 | | condition type | Description | 173 | |--|--| 174 | | biome | Teste un biome | 175 | | block | Teste un bloc | 176 | | blocks | Teste une zone de blocs | 177 | | data | Teste l'existence d'un NBT | 178 | | entity | Teste l'existence d'au minimum une entitée correspondant au sélécteur | 179 | | predicate | Teste si les condtions d'un prédicate est vrai | 180 | | score | Teste si le score est égal un un entier spécifié ou teste une relation entre deux scores | 181 | 182 | 183 | 184 | 185 | ## 2.3. `as @e[scores={}]` ou `as @e if score @s` 186 | 187 | 188 | 189 | Ces deux commandes ci-dessous ont le même effet (elles vont transmettre le même contexte à la fonction suivante). 190 | ```hs 191 | execute if score @s matches run ... 192 | ``` 193 | ```hs 194 | execute as @e[scores=] run ... 195 | ``` 196 | Alors laquelle des deux est la plus efficace ? 197 | 198 | 199 | En se référent à l'analyse postée il y a quelques mois sur le reddit de MCCmmds par u/Wooden_chest (*cf. reference 6*), le delta de performances entre les deux cas est trop petite pour être considérée. Mais parfois la syntaxe `@e[scores={}]` est préférable car cela peut permettre d'éviter de check les arguments suivants. 200 | 201 | _exemple:_ 202 | ```hs 203 | execute as @e[type=item,scores={objective=10..},nbt={custom:1b}] run ... 204 | ``` 205 | 206 | Ici, comme expliqué dans la section 2.1.3., l'ordre des arguments est important. Donc utiliser la syntaxe `if score` va ralenir notre code. 207 | 208 | Pour résumer, `as @e[scores={}]` est plus efficace que `as @e if score @s` mais en réalitée, l'optimisation est si minimale ccomparée au reste que vous n'en verrez jamais l'impacte (excépté le cas mentionné ci dessus). 209 | 210 | 211 | ## 2.4. Utiliser des tags plutôt que tester un NBT 212 | 213 | 214 | Dans certains cas, il est possible de ne pas check un NBT pour chaque tick et d'utiliser un tag. Ce sont des cas vraiment spécifiques et doivent être ajustés en fonction des besoins dans chaque situation. 215 | 216 | Voyons un exemple: 217 | 218 | Nous souhaitons créer des particules sur certains items custom qui on un NBT custom `{customItem:1b}`. 219 | 220 | Créons notre tick function, (nous devrions idéalement utiliser une fonction loop plutôt, mais pour garder cet exemple simple, on garde ici une fonction tick). Pour les fonctions loop, *cf **3.1.***. 221 | 222 | ```hs 223 | # # # # # # # # # # # # # # # # # # # # # # # 224 | # foo:tick                                  # 225 | #                                           # 226 | # function executed each tick               # 227 | # # # # # # # # # # # # # # # # # # # # # # # 228 | 229 | 230 | # check les items qui n'ont JAMAIS été check. Si ils n'ont pas de tag custom, ils sont tagués avec foo_ignore pour ne plus jamais être check. 231 | execute as @e[type=item,tag=!foo_ignore,tag=!foo_custom,nbt=!{Item:{tag:{customItem:1b}}}] run tag @s add foo_ignore 232 | 233 | 234 | # Alors, tous les items pas encore taggués avec foo_ignore sont tous des items ayant le tag custom recherché. 235 | # Donc on peut les taguer avec foo_custom 236 | tag @e[type=item,tag=!foo_ignore] add foo_custom 237 | 238 | 239 | # Désormais, nous somme capables de séléctionner les entités avec un tag et plus avec un NBT 240 | execute as @e[type=item,tag=foo_custom] at @s run particle ... 241 | ``` 242 | 243 | Le but de cette méthode est de limiter l'usage de l'argument de sélecteur `nbt` à 1 itération par item et d'utiliser à la place des tags. Cette technique devrait être utilisée en lien avec celle décrite dans la section ***3.1.1.***. 244 | 245 | 246 | ## 2.5. Predicates 247 | 248 | 249 | L'impact de performance indiquée dans la section ***2.1.2.*** est une généralisation qui doit être expliquée. Le contenu des prédicats est aussi variable que leur impact sur les performances. En fait, l'utilisation du champ `nbt` dans le prédicat est moins efficace que l'utilisation de l'argument sélectif `nbt=`. 250 | 251 | Ainsi, si une vérification nbt peut être remplacée par un prédicat, l'utilisation d'autres champs que `nbt` pour remplacer un argument `nbt` peut s'avérer plus efficace dans la plupart des cas. 252 | 253 | Par exemple, l'utilisation d'un prédicat pour vérifier l'item dans la main principale est préférable à l'utilisation de l'argument nbt `SelectedItem`. 254 | 255 | 256 | # 3. Structure du Datapack 257 | 258 | Les datapacks ne sont pas seulement des fichiers mcfunction; leur structure est importante, et les datapacks présentent d'autres caractéristiques qui permettent une certaine optimisation sans utiliser de fonctions. 259 | 260 | 261 | ## 3.1. Executer des fonctions seulement quand il y a besoin 262 | 263 | 264 | ### 3.1.1. Fonctions Tick 265 | 266 | Ces fonctions sont exécutées à chaque tick, il faut donc faire attention lorsque l'on met des commandes dans ces fichiers. La majorité des lags dus aux datapacks proviennent de (trop) nombreuses fonctions inutiles placées dans ces fichiers. 267 | 268 | ### 3.1.2. Fonctions Schedule 269 | 270 | Les fonctions schedule sont extraordinaires ! 271 | Elles peuvent vous permettre d'exécuter des boucles plus lentes que les fonctions tick. Même si les règles applicables pour les fonctions tick s'appliquent aussi. 272 | 273 | 274 | En fait, de nombreuses fonctions n'ont pas besoin d'être exécutées à chaque tick et peuvent se contenter d'être exécutées une fois tous les deux ticks. En utilisant cette méthode, vous pouvez diviser votre lag presque par deux. Bien entendu, vous pouvez utiliser des fonctions encore plus lentes pour réduire encore davantage le lag. 275 | 276 | 277 | #### Comment planifier des boucles ? 278 | ```hs 279 | # # # # # # # # # # # # # # # # # # # # # # # 280 | # foo:load                                  # 281 | #                                           # 282 | # fonction éxcutée quand le pack est chargé # 283 | # # # # # # # # # # # # # # # # # # # # # # # 284 | 285 | 286 | schedule function foo:loop2t 2t 287 | ``` 288 | ```hs 289 | # # # # # # # # # # # # # # # # # # # # # # # 290 | # foo:loop2t                                # 291 | #                                           # 292 | # fonction loop éxécutée tous les 2 ticks   # 293 | # # # # # # # # # # # # # # # # # # # # # # # 294 | 295 | 296 | # éxecutez vos commandes ici 297 | 298 | 299 | 300 | # schedule la prochaine itération 301 | schedule function foo:loop2t 2t 302 | ``` 303 | Le concept est plutôt simple; nous planifions (schedule) une fonction la première fois au début de notre datapack, puis la fonction se replanifiera elle-même quand éxécutée. 304 | 305 | 306 | 307 | ## 3.2. Advancements et Events 308 | Une autre façon de remplacer les commandes tournant depuis `minecraft/tags/tick.json` est d'utiliser les advancements de Minecraft. On peut les considérer comme un équivalent des events dans d'autres languages de programmation. En effet, les advancements nous permettent d'éxécuter des fonctions lors de leurs activations. 309 | 310 | Nous pouvons donc utiliser cette fonctionalitée pour les conditions existantes des advancements au lieu de check une condition tous les ticks. 311 | 312 | 313 | Vous pouvez trouver une liste complète des différents critères sur [cette page](https://minecraft.wiki/w/Advancement/JSON_format#List_of_triggers) du wiki. 314 | 315 | ⚠️ Utiliser le critère `minecraft:tick` **n'optimisera pas** votre code du tout. 316 | 317 | 318 | #### Exemple: 319 | Détectons lorsqu'un joueur tue un zombie et faisons-lui dire "Tâche accomplie!". 320 | 321 | Au lieu d'utiliser un scoreboard avec le critère `minecraft.killed:minecraft.zombie` et de vérifier si le score a changé, nous allons utiliser un advancement. 322 | 323 | 324 | Premièrement, mettons en place notre advancement: 325 | `foo/advancement/triggerkillszombie.json` 326 | ```json 327 | { 328 | "criteria": { 329 | "requirement": { 330 | "trigger": "minecraft:player_killed_entity", 331 | "conditions": { 332 | "entity": { 333 | "type": "minecraft:zombie" 334 | } 335 | } 336 | } 337 | }, 338 | "rewards": { 339 | "function": "foo:zombiekilled" 340 | } 341 | } 342 | ``` 343 | Vous pouvez remarquer la présence en récompense de la fonctions `foo:zombiekilled`. Donc créons cette fonction: 344 | ```hs 345 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 346 | # foo:zombiekilled                                  # 347 | #                                                   # 348 | # fonction éxécutée quand un joueur tue unn zombie  # 349 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 350 | 351 | 352 | # éxécutez vos commandes ici 353 | say Tâche accomplie! 354 | 355 | 356 | # révoquer l'advancement 357 | advancement revoke @s only foo:triggerkillszombie 358 | ``` 359 | NB: Quand la fonction est éxécutée, celle-ci est éxécutée dans le context du joueur qui a accompli l'advncement. Cela veut dire que vous pouvez utiliser `@s` pour séléctionner le joueur. 360 | 361 | Comme nous révoquons l'advancement après avoir éxécuté nos commandes, nous "réarmons" la détéction de l'advancement. 362 | 363 | 364 | 365 | 366 | ## 3.3. Fonctions et Structure 367 | Parlons des fonctions et de la structure du datapack. Même s'il n'est pas recommandé de mettre toutes ses fonctions dans le même dossier, nous n'en parlerons pas ici au-delà de cette recommandation car cela n'a pas d'impact sur la performance de votre code. Nous allons parler dans cette partie de l'appel de la fonction dans d'autres fonctions. En effet, vous pouvez améliorer de manière significative les performances de votre datapack. On parle alors de "wrap". 368 | 369 | 370 | Vous pouvez utiliser la fonction ci-dessous comme une procédure, de façon à éxécuter plusieurs fois le même groupe de commandes. 371 | ```hs 372 | function foo:myfunction 373 | ``` 374 | 375 | 376 | De plus, la commande `/function` conserve le contexte. Cela signifie donc qu'au lieu de cet exemple : 377 | ```hs 378 | # exemple 1 379 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command1 380 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command2 381 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command3 382 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run command4 383 | ``` 384 | 385 | 386 | Vous pouvez utiliser cette version, dans laquelle vous diviserez votre fonction en deux parties : 387 | ```hs 388 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 389 | # foo:exemple2                                      # 390 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 391 | 392 | 393 | execute as @e[type=item,tag=Tag1,tag=Tag2,nbt={custom:1}] run function foo:exemple2-procedure 394 | ``` 395 | ```hs 396 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 397 | # foo:exemple2-procedure                            # 398 | # # # # # # # # # # # # # # # # # # # # # # # # # # # 399 | 400 | 401 | command1 402 | command2 403 | command3 404 | command4 405 | ``` 406 | 407 | 408 | L'objectif est de vérifier la condition imposée par le sélecteur une seule fois au lieu de quatre. Comme le mcfunction est un langage de programmation impératif, l'ordre des fonctions sera toujours le même, et le résultat sera exactement le même entre les exemples 1 et 2. 409 | 410 | Il est recommandé d'utiliser cette structure dans les cas où il y a beaucoup de sous-commandes, ou lorsque les sélecteurs/conditions sont complexes. 411 | Si la condition est simple et que le nombre de sous-commandes est assez faible, le coût d'appel d'une fonction sera plus élevé que la vérification de deux fois la condition. 412 | 413 | 414 | # 4. Récapitulatif et points importants 415 | 416 | 417 | - Les arguments de sélécteurs ont un ordre 418 | - Modifier les données avec `/data` impacte beaucoup les performances. 419 | - Au lieu d'utiliseer plusieurs fois le même sélécteur, appeler une fonction qui éxécutera les commandes. 420 | - Utiliser les fonctions "tick" seulement quand nécéssaire. 421 | - Utiliser les advancements comme des events. 422 | - Si possible, utiliser des séléctions par tags plutôt que des séléctionner avec des nbt à chaque tick. 423 | - Ne pas oublier de faire ces propres tests pour déterminer le plus optimal en fonction de la situation. 424 | - Se focus dans un premier temps sur les parties les plus lourdes de votre code. Commencer par optimiser `as @e if score @s` est inefficace. 425 | 426 | 427 | 428 | # 5. Sources et Références 429 | Toutes les références et les crédits pour toute personne ayant participé directement ou indirectement à ce projet peuvent être trouvés ici. 430 | 431 | 432 | ### Sources & References 433 | 1. [@Dominexis](https://github.com/Dominexis) selectors efficiency analysis [[ GSheet ](https://docs.google.com/spreadsheets/d/1Z0XVvyfzVSGstmpLSMKnwlxwYg8N2ZFl3Xmh0ZV0yZU/edit#gid=0)] [[ Original post ](https://discord.com/channels/154777837382008833/154777837382008833/1031977637620498462)] 434 | 2. MCP-Reborn [[ Github Repo ](https://github.com/Hexeption/MCP-Reborn)] 435 | 3. Minecraft Commands' Discord server [[ link ](https://discord.gg/QAFXFtZ)] 436 | 4. [@Misode](https://github.com/misode) McMeta repo [[ Github Repo ](https://github.com/misode/mcmeta)] 437 | 5. Minecraft Wiki [[ link ](https://minecraft.wiki/w/Minecraft_Wiki)] 438 | 6. [u/Wooden_chest](https://www.reddit.com/user/Wooden_chest/) performance tests [[ link ](https://www.reddit.com/r/MinecraftCommands/comments/w4vjs3/whenever_i_create_datapacks_i_sometimes_do/)] 439 | 7. [@capitalists#1171](https://discordapp.com/users/217271293668622344) `type` argument is allways checked (message on MinecraftCommands discord) [ link to message ](https://discord.com/channels/154777837382008833/154777837382008833/985503145239142461) 440 | 441 | 442 | ### Special thanks 443 | - [@Dominexis](https://github.com/Dominexis) for his precious tips and advised indications 444 | - [@Misode](https://github.com/misode) for checking the code about `name` selector argument efficiency 445 | - [@capitalists#1171](https://discordapp.com/users/217271293668622344) for checking the code about `type` argument 446 | 447 | #### Edits: 448 | - [@BluePsychoRanger](https://github.com/BluePsychoRanger) edit about `sort` argument, code rectification 449 | - [@CosmicAxolotl](https://github.com/CosmicAxolotl) precision about @a and dead players 450 | - [@ICY105](https://github.com/ICY105) specification about `type` argument usage 451 | 452 | 453 | 454 | [← Back to the README](../../../README.md) 455 | --------------------------------------------------------------------------------