[{"data":1,"prerenderedAt":3171},["ShallowReactive",2],{"wiki-nav":3,"wiki-\u002Fwiki\u002Fdevelopers\u002Fapi-usage":238,"wiki-surround-\u002Fwiki\u002Fdevelopers\u002Fapi-usage":3100,"wiki-meta:\u002Fwiki\u002Fdevelopers\u002Fapi-usage":3103,"wiki-sidebar":3105},[4],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"Wiki","i-lucide-book","\u002Fwiki","wiki",[10,13,40,84,88,110,114,155,159,197,212,234],{"title":11,"path":7,"stem":12},"Wiki home","wiki\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":39},"About","i-lucide-info","\u002Fwiki\u002Fabout","wiki\u002Fabout",[19,23,27,31,35],{"title":20,"path":21,"stem":22},"Credits","\u002Fwiki\u002Fabout\u002Fcredits","wiki\u002Fabout\u002Fcredits",{"title":24,"path":25,"stem":26},"External connections","\u002Fwiki\u002Fabout\u002Fexternal-connections","wiki\u002Fabout\u002Fexternal-connections",{"title":28,"path":29,"stem":30},"Locale and translations","\u002Fwiki\u002Fabout\u002Flocale","wiki\u002Fabout\u002Flocale",{"title":32,"path":33,"stem":34},"Placeholders","\u002Fwiki\u002Fabout\u002Fplaceholders","wiki\u002Fabout\u002Fplaceholders",{"title":36,"path":37,"stem":38},"Why LuckPerms?","\u002Fwiki\u002Fabout\u002Fwhy-luckperms","wiki\u002Fabout\u002Fwhy-luckperms",false,{"title":41,"icon":42,"path":43,"stem":44,"children":45},"Commands","i-lucide-terminal","\u002Fwiki\u002Fcommands","wiki\u002Fcommands\u002Findex",[46,48,52,56,60,64,68,72,76,80],{"title":47,"path":43,"stem":44},"Command usage",{"title":49,"path":50,"stem":51},"General commands","\u002Fwiki\u002Fcommands\u002Fgeneral","wiki\u002Fcommands\u002Fgeneral",{"title":53,"path":54,"stem":55},"Group commands","\u002Fwiki\u002Fcommands\u002Fgroup","wiki\u002Fcommands\u002Fgroup",{"title":57,"path":58,"stem":59},"Log commands","\u002Fwiki\u002Fcommands\u002Flog","wiki\u002Fcommands\u002Flog",{"title":61,"path":62,"stem":63},"Meta commands","\u002Fwiki\u002Fcommands\u002Fmeta","wiki\u002Fcommands\u002Fmeta",{"title":65,"path":66,"stem":67},"Parent commands","\u002Fwiki\u002Fcommands\u002Fparent","wiki\u002Fcommands\u002Fparent",{"title":69,"path":70,"stem":71},"Permission commands","\u002Fwiki\u002Fcommands\u002Fpermission","wiki\u002Fcommands\u002Fpermission",{"title":73,"path":74,"stem":75},"Permissions reference","\u002Fwiki\u002Fcommands\u002Fpermissions","wiki\u002Fcommands\u002Fpermissions",{"title":77,"path":78,"stem":79},"Track commands","\u002Fwiki\u002Fcommands\u002Ftrack","wiki\u002Fcommands\u002Ftrack",{"title":81,"path":82,"stem":83},"User commands","\u002Fwiki\u002Fcommands\u002Fuser","wiki\u002Fcommands\u002Fuser",{"title":85,"path":86,"stem":87},"Configuration","\u002Fwiki\u002Fconfiguration","wiki\u002Fconfiguration",{"title":89,"icon":90,"path":91,"stem":92,"children":93,"page":39},"Developers","i-lucide-code","\u002Fwiki\u002Fdevelopers","wiki\u002Fdevelopers",[94,98,102,106],{"title":95,"path":96,"stem":97},"Developer API","\u002Fwiki\u002Fdevelopers\u002Fapi","wiki\u002Fdevelopers\u002Fapi",{"title":99,"path":100,"stem":101},"API usage","\u002Fwiki\u002Fdevelopers\u002Fapi-usage","wiki\u002Fdevelopers\u002Fapi-usage",{"title":103,"path":104,"stem":105},"Contributing","\u002Fwiki\u002Fdevelopers\u002Fcontributing","wiki\u002Fdevelopers\u002Fcontributing",{"title":107,"path":108,"stem":109},"Standalone app and REST API","\u002Fwiki\u002Fdevelopers\u002Fstandalone-rest","wiki\u002Fdevelopers\u002Fstandalone-rest",{"title":111,"path":112,"stem":113},"FAQ","\u002Fwiki\u002Ffaq","wiki\u002Ffaq",{"title":115,"icon":116,"path":117,"stem":118,"children":119,"page":39},"Features","i-lucide-sparkles","\u002Fwiki\u002Ffeatures","wiki\u002Ffeatures",[120,124,128,132,136,140,151],{"title":121,"path":122,"stem":123},"Context","\u002Fwiki\u002Ffeatures\u002Fcontext","wiki\u002Ffeatures\u002Fcontext",{"title":125,"path":126,"stem":127},"Default groups","\u002Fwiki\u002Ffeatures\u002Fdefault-groups","wiki\u002Ffeatures\u002Fdefault-groups",{"title":129,"path":130,"stem":131},"Prefixes, suffixes and meta","\u002Fwiki\u002Ffeatures\u002Fprefixes-suffixes-meta","wiki\u002Ffeatures\u002Fprefixes-suffixes-meta",{"title":133,"path":134,"stem":135},"Tracks","\u002Fwiki\u002Ffeatures\u002Ftracks","wiki\u002Ffeatures\u002Ftracks",{"title":137,"path":138,"stem":139},"Verbose","\u002Fwiki\u002Ffeatures\u002Fverbose","wiki\u002Ffeatures\u002Fverbose",{"title":141,"path":142,"stem":143,"children":144},"Web Editor","\u002Fwiki\u002Ffeatures\u002Fweb-editor","wiki\u002Ffeatures\u002Fweb-editor\u002Findex",[145,147],{"title":146,"path":142,"stem":143},"Web editor",{"title":148,"path":149,"stem":150},"Web editor technical details","\u002Fwiki\u002Ffeatures\u002Fweb-editor\u002Ftechnical","wiki\u002Ffeatures\u002Fweb-editor\u002Ftechnical",{"title":152,"path":153,"stem":154},"Weight","\u002Fwiki\u002Ffeatures\u002Fweight","wiki\u002Ffeatures\u002Fweight",{"title":156,"path":157,"stem":158},"Getting started","\u002Fwiki\u002Fgetting-started","wiki\u002Fgetting-started",{"title":160,"icon":161,"path":162,"stem":163,"children":164,"page":39},"Guides","i-lucide-map","\u002Fwiki\u002Fguides","wiki\u002Fguides",[165,169,173,177,181,185,189,193],{"title":166,"path":167,"stem":168},"Argument-based command permissions","\u002Fwiki\u002Fguides\u002Fargument-permissions","wiki\u002Fguides\u002Fargument-permissions",{"title":170,"path":171,"stem":172},"Bulk editing","\u002Fwiki\u002Fguides\u002Fbulk-editing","wiki\u002Fguides\u002Fbulk-editing",{"title":174,"path":175,"stem":176},"Extensions","\u002Fwiki\u002Fguides\u002Fextensions","wiki\u002Fguides\u002Fextensions",{"title":178,"path":179,"stem":180},"Migration","\u002Fwiki\u002Fguides\u002Fmigration","wiki\u002Fguides\u002Fmigration",{"title":182,"path":183,"stem":184},"Prefix and suffix stacking","\u002Fwiki\u002Fguides\u002Fprefix-stacking","wiki\u002Fguides\u002Fprefix-stacking",{"title":186,"path":187,"stem":188},"Storage system errors","\u002Fwiki\u002Fguides\u002Fstorage-errors","wiki\u002Fguides\u002Fstorage-errors",{"title":190,"path":191,"stem":192},"Switching storage types","\u002Fwiki\u002Fguides\u002Fswitching-storage","wiki\u002Fguides\u002Fswitching-storage",{"title":194,"path":195,"stem":196},"Syncing data between servers","\u002Fwiki\u002Fguides\u002Fsyncing","wiki\u002Fguides\u002Fsyncing",{"title":198,"icon":199,"path":200,"stem":201,"children":202},"Installation","i-lucide-download","\u002Fwiki\u002Finstall","wiki\u002Finstall\u002Findex",[203,204,208],{"title":198,"path":200,"stem":201},{"title":205,"path":206,"stem":207},"Hytale","\u002Fwiki\u002Finstall\u002Fhytale","wiki\u002Finstall\u002Fhytale",{"title":209,"path":210,"stem":211},"Network installation","\u002Fwiki\u002Finstall\u002Fnetwork","wiki\u002Finstall\u002Fnetwork",{"title":213,"icon":214,"path":215,"stem":216,"children":217,"page":39},"Reference","i-lucide-library","\u002Fwiki\u002Freference","wiki\u002Freference",[218,222,226,230],{"title":219,"path":220,"stem":221},"Migrating from GroupManager or PermissionsEx","\u002Fwiki\u002Freference\u002Fmigrating-gm-pex","wiki\u002Freference\u002Fmigrating-gm-pex",{"title":223,"path":224,"stem":225},"How permission calculation works","\u002Fwiki\u002Freference\u002Fpermission-calculation","wiki\u002Freference\u002Fpermission-calculation",{"title":227,"path":228,"stem":229},"Self-hosting the web interfaces","\u002Fwiki\u002Freference\u002Fself-hosting","wiki\u002Freference\u002Fself-hosting",{"title":231,"path":232,"stem":233},"Upgrading from v4 to v5","\u002Fwiki\u002Freference\u002Fupgrade-v4-v5","wiki\u002Freference\u002Fupgrade-v4-v5",{"title":235,"path":236,"stem":237},"Storage types","\u002Fwiki\u002Fstorage","wiki\u002Fstorage",{"id":239,"title":99,"body":240,"description":3094,"extension":3095,"meta":3096,"navigation":530,"path":100,"seo":3097,"stem":101,"updatedAt":3098,"__hash__":3099},"wiki_en\u002Fwiki\u002Fdevelopers\u002Fapi-usage.md",{"type":241,"value":242,"toc":3047},"minimark",[243,252,262,267,270,300,307,310,314,317,366,373,375,379,386,391,394,397,419,422,426,431,434,437,452,459,468,474,478,481,497,508,511,552,556,559,566,574,577,610,613,616,619,695,702,705,793,796,835,837,841,851,854,897,907,917,919,923,926,964,971,1009,1012,1014,1018,1025,1028,1031,1036,1039,1053,1059,1109,1111,1115,1125,1131,1319,1321,1325,1334,1337,1346,1348,1352,1364,1370,1377,1383,1386,1395,1410,1413,1416,1446,1449,1467,1470,1510,1513,1519,1521,1530,1548,1554,1556,1565,1584,1593,1595,1599,1612,1615,1624,1632,1634,1638,1644,1647,1667,1671,1677,1680,1697,1700,1705,1711,1718,1798,1808,1832,1837,1842,1901,1907,1931,1935,1938,1949,1952,2042,2047,2054,2138,2141,2150,2154,2160,2163,2186,2192,2207,2210,2263,2265,2269,2289,2298,2307,2319,2327,2330,2350,2363,2391,2399,2438,2441,2445,2474,2484,2503,2507,2522,2526,2535,2541,2543,2547,2550,2554,2560,2563,2643,2647,2650,2693,2695,2699,2702,2725,2729,2747,2756,2779,2782,2921,2924,2928,2938,2941,2945,2956,2989,3000,3003,3028,3034,3043],[244,245,246,247,251],"p",{},"This page shows some sample usages of the LuckPerms API, which is introduced ",[248,249,250],"a",{"href":96},"here",".",[244,253,254,255,261],{},"As well as this documentation, we also have the ",[248,256,260],{"href":257,"rel":258},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002Fapi-cookbook",[259],"nofollow","api-cookbook",". This is an example Bukkit plugin which uses the API to perform certain common functions.",[263,264,266],"h2",{"id":265},"checking-if-a-player-is-in-a-group","Checking if a player is in a group",[244,268,269],{},"Checking for group membership can be most easily achieved using hasPermission checks.",[271,272,277],"pre",{"className":273,"code":274,"language":275,"meta":276,"style":276},"language-java shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","public static boolean isPlayerInGroup(Player player, String group) {\n    return player.hasPermission(\"group.\" + group);\n}\n","java","",[278,279,280,288,294],"code",{"__ignoreMap":276},[281,282,285],"span",{"class":283,"line":284},"line",1,[281,286,287],{},"public static boolean isPlayerInGroup(Player player, String group) {\n",[281,289,291],{"class":283,"line":290},2,[281,292,293],{},"    return player.hasPermission(\"group.\" + group);\n",[281,295,297],{"class":283,"line":296},3,[281,298,299],{},"}\n",[244,301,302,303,306],{},"However, keep in mind that anyone with server operator status or ",[278,304,305],{},"*"," permissions will also have these permissions.",[308,309],"hr",{},[263,311,313],{"id":312},"finding-a-players-group","Finding a players group",[244,315,316],{},"We can use the method above with a list of \"possible\" groups in order to find a player's group.",[271,318,320],{"className":273,"code":319,"language":275,"meta":276,"style":276},"public static String getPlayerGroup(Player player, Collection\u003CString> possibleGroups) {\n    for (String group : possibleGroups) {\n        if (player.hasPermission(\"group.\" + group)) {\n            return group;\n        }\n    }\n    return null;\n}\n",[278,321,322,327,332,337,343,349,355,361],{"__ignoreMap":276},[281,323,324],{"class":283,"line":284},[281,325,326],{},"public static String getPlayerGroup(Player player, Collection\u003CString> possibleGroups) {\n",[281,328,329],{"class":283,"line":290},[281,330,331],{},"    for (String group : possibleGroups) {\n",[281,333,334],{"class":283,"line":296},[281,335,336],{},"        if (player.hasPermission(\"group.\" + group)) {\n",[281,338,340],{"class":283,"line":339},4,[281,341,342],{},"            return group;\n",[281,344,346],{"class":283,"line":345},5,[281,347,348],{},"        }\n",[281,350,352],{"class":283,"line":351},6,[281,353,354],{},"    }\n",[281,356,358],{"class":283,"line":357},7,[281,359,360],{},"    return null;\n",[281,362,364],{"class":283,"line":363},8,[281,365,299],{},[244,367,368,369,372],{},"Remember to order your ",[278,370,371],{},"possibleGroups"," list by priority. e.g. owner first, member last.",[308,374],{},[263,376,378],{"id":377},"obtaining-a-user-instance","Obtaining a User instance",[244,380,381,382,385],{},"A ",[278,383,384],{},"User"," in LuckPerms is simply an object which represents a player on the server, and their associated permission data.",[387,388,390],"h3",{"id":389},"distinction-between-online-offline-players","Distinction between online & offline players",[244,392,393],{},"In order to conserve memory usage, LuckPerms will only load User data when it absolutely needs to.",[244,395,396],{},"Meaning:",[398,399,400,408],"ul",{},[401,402,403,407],"li",{},[404,405,406],"strong",{},"Online"," players are guaranteed to have an associated User object loaded already.",[401,409,410,413,414,418],{},[404,411,412],{},"Offline"," players ",[415,416,417],"em",{},"may"," have an associated User object loaded, but they most likely will not.",[244,420,421],{},"This makes getting a User instance a little complicated, depending on if the Player is online or not.",[387,423,425],{"id":424},"loading-data-for-players","Loading data for players",[427,428,430],"h4",{"id":429},"if-the-player-is-already-online","If the player is already online",[244,432,433],{},"If we know the player is connected, LuckPerms will already have data in memory for them.",[244,435,436],{},"It's as simple as...",[271,438,440],{"className":273,"code":439,"language":275,"meta":276,"style":276},"Player player = ...;\nUser user = luckPerms.getPlayerAdapter(Player.class).getUser(player);\n",[278,441,442,447],{"__ignoreMap":276},[281,443,444],{"class":283,"line":284},[281,445,446],{},"Player player = ...;\n",[281,448,449],{"class":283,"line":290},[281,450,451],{},"User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);\n",[244,453,454,455,458],{},"Or if you only have a ",[278,456,457],{},"UUID","...",[271,460,462],{"className":273,"code":461,"language":275,"meta":276,"style":276},"User user = luckPerms.getUserManager().getUser(uuid);\n",[278,463,464],{"__ignoreMap":276},[281,465,466],{"class":283,"line":284},[281,467,461],{},[244,469,470,471,473],{},"However, remember that this instance ",[415,472,417],{}," not represent the user's most up-to-date state. If you want to make changes, it's a good idea to request for the user's data to be loaded again (read on...).",[427,475,477],{"id":476},"if-the-player-isnt-or-might-not-be-online","If the player isn't (or might not be) online",[244,479,480],{},"Let's assume we want to load some data about a user - but we only have their unique id.",[244,482,483,484,487,488,490,491,493,494,251],{},"The first thing we need to do is obtain the ",[278,485,486],{},"UserManager",". This object is responsible for handling all operations relating to ",[278,489,384],{},"s. The user manager provides a method which lets us load a ",[278,492,384],{}," instance, appropriately named ",[278,495,496],{},"loadUser",[244,498,499,500,503,504,507],{},"The method returns a ",[278,501,502],{},"CompletableFuture"," (explained ",[248,505,250],{"href":506},"\u002Fwiki\u002Fdevelopers\u002Fapi#using-completablefutures",").",[244,509,510],{},"We can simply attach a callback onto the future to apply the action.",[271,512,514],{"className":273,"code":513,"language":275,"meta":276,"style":276},"UserManager userManager = luckPerms.getUserManager();\nCompletableFuture\u003CUser> userFuture = userManager.loadUser(uniqueId);\n\nuserFuture.thenAcceptAsync(user -> {\n    \u002F\u002F Now we have a user which we can query.\n    \u002F\u002F ...\n});\n",[278,515,516,521,526,532,537,542,547],{"__ignoreMap":276},[281,517,518],{"class":283,"line":284},[281,519,520],{},"UserManager userManager = luckPerms.getUserManager();\n",[281,522,523],{"class":283,"line":290},[281,524,525],{},"CompletableFuture\u003CUser> userFuture = userManager.loadUser(uniqueId);\n",[281,527,528],{"class":283,"line":296},[281,529,531],{"emptyLinePlaceholder":530},true,"\n",[281,533,534],{"class":283,"line":339},[281,535,536],{},"userFuture.thenAcceptAsync(user -> {\n",[281,538,539],{"class":283,"line":345},[281,540,541],{},"    \u002F\u002F Now we have a user which we can query.\n",[281,543,544],{"class":283,"line":351},[281,545,546],{},"    \u002F\u002F ...\n",[281,548,549],{"class":283,"line":357},[281,550,551],{},"});\n",[427,553,555],{"id":554},"if-the-player-isnt-or-might-not-be-online-we-want-to-return-something","If the player isn't (or might not be) online & we want to return something",[244,557,558],{},"The callback approach works well if you don't need to actually \"return\" anything. It performs all of the nasty i\u002Fo away from the main server thread, and handles everything in the background.",[244,560,561,562,565],{},"But what if we need data ",[415,563,564],{},"now","? Well, that's where it gets fun. Unfortunately, there's no straightforward answer - but you effectively have two options.",[398,567,568,571],{},[401,569,570],{},"Define a blocking method, which will be (kind of) simple, but will lag the server if it's not called async",[401,572,573],{},"Embrace CompletableFutures and callbacks",[244,575,576],{},"The first option effectively comes down to this...",[271,578,580],{"className":273,"code":579,"language":275,"meta":276,"style":276},"public User giveMeADamnUser(UUID uniqueId) {\n    UserManager userManager = luckPerms.getUserManager();\n    CompletableFuture\u003CUser> userFuture = userManager.loadUser(uniqueId);\n\n    return userFuture.join(); \u002F\u002F ouch! (block until the User is loaded)\n}\n",[278,581,582,587,592,597,601,606],{"__ignoreMap":276},[281,583,584],{"class":283,"line":284},[281,585,586],{},"public User giveMeADamnUser(UUID uniqueId) {\n",[281,588,589],{"class":283,"line":290},[281,590,591],{},"    UserManager userManager = luckPerms.getUserManager();\n",[281,593,594],{"class":283,"line":296},[281,595,596],{},"    CompletableFuture\u003CUser> userFuture = userManager.loadUser(uniqueId);\n",[281,598,599],{"class":283,"line":339},[281,600,531],{"emptyLinePlaceholder":530},[281,602,603],{"class":283,"line":345},[281,604,605],{},"    return userFuture.join(); \u002F\u002F ouch! (block until the User is loaded)\n",[281,607,608],{"class":283,"line":351},[281,609,299],{},[244,611,612],{},"You can then do whatever you want with the user instance - but remember, this should only ever be called from an async task!",[244,614,615],{},"The other option is to embrace callbacks.",[244,617,618],{},"In an ideal world, we'd be able to do something like this, without any consequences.",[271,620,622],{"className":273,"code":621,"language":275,"meta":276,"style":276},"public boolean isAdmin(UUID who) {\n    User user = luckPerms.getUserManager().loadUser(who);\n\n    Collection\u003CGroup> inheritedGroups = user.getInheritedGroups(user.getQueryOptions());\n    return inheritedGroups.stream().anyMatch(g -> g.getName().equals(\"admin\"));\n}\n\npublic void informIfAdmin(CommandSender sender, UUID who) {\n    if (isAdmin(who)) {\n        sender.sendMessage(\"Yes! That player is an admin!\");\n    } else {\n        sender.sendMessage(\"No, that player isn't an admin.\");\n    }\n}\n",[278,623,624,629,634,638,643,648,652,656,661,667,673,679,685,690],{"__ignoreMap":276},[281,625,626],{"class":283,"line":284},[281,627,628],{},"public boolean isAdmin(UUID who) {\n",[281,630,631],{"class":283,"line":290},[281,632,633],{},"    User user = luckPerms.getUserManager().loadUser(who);\n",[281,635,636],{"class":283,"line":296},[281,637,531],{"emptyLinePlaceholder":530},[281,639,640],{"class":283,"line":339},[281,641,642],{},"    Collection\u003CGroup> inheritedGroups = user.getInheritedGroups(user.getQueryOptions());\n",[281,644,645],{"class":283,"line":345},[281,646,647],{},"    return inheritedGroups.stream().anyMatch(g -> g.getName().equals(\"admin\"));\n",[281,649,650],{"class":283,"line":351},[281,651,299],{},[281,653,654],{"class":283,"line":357},[281,655,531],{"emptyLinePlaceholder":530},[281,657,658],{"class":283,"line":363},[281,659,660],{},"public void informIfAdmin(CommandSender sender, UUID who) {\n",[281,662,664],{"class":283,"line":663},9,[281,665,666],{},"    if (isAdmin(who)) {\n",[281,668,670],{"class":283,"line":669},10,[281,671,672],{},"        sender.sendMessage(\"Yes! That player is an admin!\");\n",[281,674,676],{"class":283,"line":675},11,[281,677,678],{},"    } else {\n",[281,680,682],{"class":283,"line":681},12,[281,683,684],{},"        sender.sendMessage(\"No, that player isn't an admin.\");\n",[281,686,688],{"class":283,"line":687},13,[281,689,354],{},[281,691,693],{"class":283,"line":692},14,[281,694,299],{},[244,696,697,698,701],{},"However, we can't, because ",[278,699,700],{},"#loadUser"," returns a CompletableFuture - as it performs lots of expensive database queries to produce a result.",[244,703,704],{},"The solution? More futures!",[271,706,708],{"className":273,"code":707,"language":275,"meta":276,"style":276},"public CompletableFuture\u003CBoolean> isAdmin(UUID who) {\n    return luckPerms.getUserManager().loadUser(who)\n        .thenApplyAsync(user -> {\n            Collection\u003CGroup> inheritedGroups = user.getInheritedGroups(user.getQueryOptions());\n            return inheritedGroups.stream().anyMatch(g -> g.getName().equals(\"admin\"));\n        });\n}\n\npublic void informIfAdmin(CommandSender sender, UUID who) {\n    isAdmin(who).thenAcceptAsync(result -> {\n        if (result) {\n            sender.sendMessage(\"Yes! That player is an admin!\");\n        } else {\n            sender.sendMessage(\"No, that player isn't an admin.\");\n        }\n    });\n}\n",[278,709,710,715,720,725,730,735,740,744,748,752,757,762,767,772,777,782,788],{"__ignoreMap":276},[281,711,712],{"class":283,"line":284},[281,713,714],{},"public CompletableFuture\u003CBoolean> isAdmin(UUID who) {\n",[281,716,717],{"class":283,"line":290},[281,718,719],{},"    return luckPerms.getUserManager().loadUser(who)\n",[281,721,722],{"class":283,"line":296},[281,723,724],{},"        .thenApplyAsync(user -> {\n",[281,726,727],{"class":283,"line":339},[281,728,729],{},"            Collection\u003CGroup> inheritedGroups = user.getInheritedGroups(user.getQueryOptions());\n",[281,731,732],{"class":283,"line":345},[281,733,734],{},"            return inheritedGroups.stream().anyMatch(g -> g.getName().equals(\"admin\"));\n",[281,736,737],{"class":283,"line":351},[281,738,739],{},"        });\n",[281,741,742],{"class":283,"line":357},[281,743,299],{},[281,745,746],{"class":283,"line":363},[281,747,531],{"emptyLinePlaceholder":530},[281,749,750],{"class":283,"line":663},[281,751,660],{},[281,753,754],{"class":283,"line":669},[281,755,756],{},"    isAdmin(who).thenAcceptAsync(result -> {\n",[281,758,759],{"class":283,"line":675},[281,760,761],{},"        if (result) {\n",[281,763,764],{"class":283,"line":681},[281,765,766],{},"            sender.sendMessage(\"Yes! That player is an admin!\");\n",[281,768,769],{"class":283,"line":687},[281,770,771],{},"        } else {\n",[281,773,774],{"class":283,"line":692},[281,775,776],{},"            sender.sendMessage(\"No, that player isn't an admin.\");\n",[281,778,780],{"class":283,"line":779},15,[281,781,348],{},[281,783,785],{"class":283,"line":784},16,[281,786,787],{},"    });\n",[281,789,791],{"class":283,"line":790},17,[281,792,299],{},[244,794,795],{},"To summarise, there are two ways to obtain a user.",[398,797,798,819],{},[401,799,800,801,804,805,808],{},"Using ",[278,802,803],{},"UserManager#getUser"," or ",[278,806,807],{},"PlayerAdapter#getUser",[398,809,810,813,816],{},[401,811,812],{},"Always returns a result for online players",[401,814,815],{},"Is \"main thread friendly\" (can be called sync)",[401,817,818],{},"Will sometimes (but usually not) return a result of offline players",[401,820,800,821,824],{},[278,822,823],{},"UserManager#loadUser",[398,825,826,829,832],{},[401,827,828],{},"Returns a future",[401,830,831],{},"Can either be used alongside callbacks, or as part of a blocking method which is only ever called async",[401,833,834],{},"Always works for both offline\u002Fonline users",[308,836],{},[263,838,840],{"id":839},"obtaining-a-grouptrack-instance","Obtaining a Group\u002FTrack instance",[244,842,843,844,804,847,850],{},"Grabbing a ",[278,845,846],{},"Group",[278,848,849],{},"Track"," is much more simple, as they are always kept loaded in memory.",[244,852,853],{},"Simply...",[271,855,857],{"className":273,"code":856,"language":275,"meta":276,"style":276},"Group group = luckPerms.getGroupManager().getGroup(groupName);\nif (group == null) {\n    \u002F\u002F group doesn't exist.\n    return;\n}\n\n\u002F\u002F now we have a group, and can apply whatever action we want.\ngroup.doSomething(...);\n",[278,858,859,864,869,874,879,883,887,892],{"__ignoreMap":276},[281,860,861],{"class":283,"line":284},[281,862,863],{},"Group group = luckPerms.getGroupManager().getGroup(groupName);\n",[281,865,866],{"class":283,"line":290},[281,867,868],{},"if (group == null) {\n",[281,870,871],{"class":283,"line":296},[281,872,873],{},"    \u002F\u002F group doesn't exist.\n",[281,875,876],{"class":283,"line":339},[281,877,878],{},"    return;\n",[281,880,881],{"class":283,"line":345},[281,882,299],{},[281,884,885],{"class":283,"line":351},[281,886,531],{"emptyLinePlaceholder":530},[281,888,889],{"class":283,"line":357},[281,890,891],{},"\u002F\u002F now we have a group, and can apply whatever action we want.\n",[281,893,894],{"class":283,"line":363},[281,895,896],{},"group.doSomething(...);\n",[244,898,899,900,902,903,906],{},"You can do exactly the same for ",[278,901,849],{},"s using the ",[278,904,905],{},"TrackManager#getTrack"," method.",[244,908,909,910,804,913,916],{},"If you need to get up-to-date data (a good idea if you're making changes), then just call ",[278,911,912],{},"loadGroup",[278,914,915],{},"loadTrack"," instead, respectively.",[308,918],{},[263,920,922],{"id":921},"saving-changes","Saving changes",[244,924,925],{},"After making changes to a user\u002Fgroup\u002Ftrack, you have to save the changes back to the storage provider. It's pretty easy.",[271,927,929],{"className":273,"code":928,"language":275,"meta":276,"style":276},"public void addPermission(User user, String permission) {\n    \u002F\u002F Add the permission\n    user.data().add(Node.builder(permission).build());\n\n    \u002F\u002F Now we need to save changes.\n    luckPerms.getUserManager().saveUser(user);\n}\n",[278,930,931,936,941,946,950,955,960],{"__ignoreMap":276},[281,932,933],{"class":283,"line":284},[281,934,935],{},"public void addPermission(User user, String permission) {\n",[281,937,938],{"class":283,"line":290},[281,939,940],{},"    \u002F\u002F Add the permission\n",[281,942,943],{"class":283,"line":296},[281,944,945],{},"    user.data().add(Node.builder(permission).build());\n",[281,947,948],{"class":283,"line":339},[281,949,531],{"emptyLinePlaceholder":530},[281,951,952],{"class":283,"line":345},[281,953,954],{},"    \u002F\u002F Now we need to save changes.\n",[281,956,957],{"class":283,"line":351},[281,958,959],{},"    luckPerms.getUserManager().saveUser(user);\n",[281,961,962],{"class":283,"line":357},[281,963,299],{},[244,965,966,967,970],{},"There is also a handy ",[278,968,969],{},"modify*"," method which handles loading and saving for you.",[271,972,974],{"className":273,"code":973,"language":275,"meta":276,"style":276},"public void addPermission(UUID userUuid, String permission) {\n    \u002F\u002F Load, modify, then save\n    luckPerms.getUserManager().modifyUser(userUuid, user -> {\n        \u002F\u002F Add the permission\n        user.data().add(Node.builder(permission).build());\n    });\n}\n",[278,975,976,981,986,991,996,1001,1005],{"__ignoreMap":276},[281,977,978],{"class":283,"line":284},[281,979,980],{},"public void addPermission(UUID userUuid, String permission) {\n",[281,982,983],{"class":283,"line":290},[281,984,985],{},"    \u002F\u002F Load, modify, then save\n",[281,987,988],{"class":283,"line":296},[281,989,990],{},"    luckPerms.getUserManager().modifyUser(userUuid, user -> {\n",[281,992,993],{"class":283,"line":339},[281,994,995],{},"        \u002F\u002F Add the permission\n",[281,997,998],{"class":283,"line":345},[281,999,1000],{},"        user.data().add(Node.builder(permission).build());\n",[281,1002,1003],{"class":283,"line":351},[281,1004,787],{},[281,1006,1007],{"class":283,"line":357},[281,1008,299],{},[244,1010,1011],{},"The same methods also exist for groups and tracks.",[308,1013],{},[263,1015,1017],{"id":1016},"the-basics-of-node","The basics of Node",[244,1019,1020,1021,1024],{},"The ",[278,1022,1023],{},"Node"," interface is the core data class in LuckPerms.",[244,1026,1027],{},"Most simply, it represents a \"permission node\". However, it actually encapsulates far more than just permission assignments. Nodes are used to store data about inherited groups, as well as assigned prefixes, suffixes and meta values.",[244,1029,1030],{},"Combining these various states into one object (a \"node\") means that a holder only has to have one type of data set (a set of nodes) in order to take on various properties.",[244,1032,1020,1033,1035],{},[278,1034,1023],{}," interface provides a number of methods to read the attributes of the node, as well as methods to query and extract additional state and properties from these settings.",[244,1037,1038],{},"Nodes have the following attributes:",[398,1040,1041,1044,1047,1050],{},[401,1042,1043],{},"key - the key of the node",[401,1045,1046],{},"value - the value of the node (false for negated)",[401,1048,1049],{},"context - the contexts required for this node to apply",[401,1051,1052],{},"expiry - the time when this node should expire",[244,1054,1055,1056,1058],{},"There are a number of node types, all of which are extensions of the base ",[278,1057,1023],{}," class.",[398,1060,1061,1067,1073,1079,1085,1091,1097,1103],{},[401,1062,1063,1066],{},[278,1064,1065],{},"PermissionNode"," - represents an assigned permission",[401,1068,1069,1072],{},[278,1070,1071],{},"RegexPermissionNode"," - represents an assigned regex permission",[401,1074,1075,1078],{},[278,1076,1077],{},"InheritanceNode"," - marks that the holder should inherit data from another group",[401,1080,1081,1084],{},[278,1082,1083],{},"PrefixNode"," - represents an assigned prefix",[401,1086,1087,1090],{},[278,1088,1089],{},"SuffixNode"," - represents an assigned suffix",[401,1092,1093,1096],{},[278,1094,1095],{},"MetaNode"," - represents an assigned meta option",[401,1098,1099,1102],{},[278,1100,1101],{},"WeightNode"," - marks the weight of the object holding the node",[401,1104,1105,1108],{},[278,1106,1107],{},"DisplayNameNode"," - marks the display name of the object holding the node",[308,1110],{},[263,1112,1114],{"id":1113},"creating-new-node-instances","Creating new node instances",[244,1116,1117,1118,1120,1121,1124],{},"To obtain a ",[278,1119,1023],{},", you use ",[278,1122,1123],{},"NodeBuilder","s.",[244,1126,1127,1128,251],{},"If you just have a \"key\" and are unsure which category of node it falls into, you can simply use ",[278,1129,1130],{},"Node.builder()",[271,1132,1134],{"className":273,"code":1133,"language":275,"meta":276,"style":276},"\u002F\u002F build any type of node\nNode node = Node.builder(\"some.node.key\").build();\n\n\u002F\u002F and with extra properties!\nNode node = Node.builder(\"some.node.key\")\n        .value(false)\n        .expiry(Duration.ofHours(1))\n        .withContext(DefaultContextKeys.SERVER_KEY, \"survival\")\n        .build();\n\n\u002F\u002F note: all of the following classes extend from Node\n\n\u002F\u002F build a permission node\nPermissionNode node = PermissionNode.builder(\"my.permission\").build();\n\n\u002F\u002F build a regex permission node\nRegexPermissionNode node = RegexPermissionNode.builder(pattern).build();\n\n\u002F\u002F build an inheritance node\nInheritanceNode node = InheritanceNode.builder(group).build();\n\n\u002F\u002F build a prefix node\nPrefixNode node = PrefixNode.builder(\"[Some Prefix]\", 100).build();\n\n\u002F\u002F build a suffix node\nSuffixNode node = SuffixNode.builder(\"[Some Suffix]\", 150).build();\n\n\u002F\u002F build a metadata node\nMetaNode node = MetaNode.builder(\"some-key\", \"some-value\").build();\n\n\u002F\u002F build a weight node\nWeightNode node = WeightNode.builder(25).build();\n\n\u002F\u002F build a display name node\nDisplayNameNode node = DisplayNameNode.builder(\"SeniorModerator\").build();\n",[278,1135,1136,1141,1146,1150,1155,1160,1165,1170,1175,1180,1184,1189,1193,1198,1203,1207,1212,1217,1222,1228,1234,1239,1245,1251,1256,1262,1268,1273,1279,1285,1290,1296,1302,1307,1313],{"__ignoreMap":276},[281,1137,1138],{"class":283,"line":284},[281,1139,1140],{},"\u002F\u002F build any type of node\n",[281,1142,1143],{"class":283,"line":290},[281,1144,1145],{},"Node node = Node.builder(\"some.node.key\").build();\n",[281,1147,1148],{"class":283,"line":296},[281,1149,531],{"emptyLinePlaceholder":530},[281,1151,1152],{"class":283,"line":339},[281,1153,1154],{},"\u002F\u002F and with extra properties!\n",[281,1156,1157],{"class":283,"line":345},[281,1158,1159],{},"Node node = Node.builder(\"some.node.key\")\n",[281,1161,1162],{"class":283,"line":351},[281,1163,1164],{},"        .value(false)\n",[281,1166,1167],{"class":283,"line":357},[281,1168,1169],{},"        .expiry(Duration.ofHours(1))\n",[281,1171,1172],{"class":283,"line":363},[281,1173,1174],{},"        .withContext(DefaultContextKeys.SERVER_KEY, \"survival\")\n",[281,1176,1177],{"class":283,"line":663},[281,1178,1179],{},"        .build();\n",[281,1181,1182],{"class":283,"line":669},[281,1183,531],{"emptyLinePlaceholder":530},[281,1185,1186],{"class":283,"line":675},[281,1187,1188],{},"\u002F\u002F note: all of the following classes extend from Node\n",[281,1190,1191],{"class":283,"line":681},[281,1192,531],{"emptyLinePlaceholder":530},[281,1194,1195],{"class":283,"line":687},[281,1196,1197],{},"\u002F\u002F build a permission node\n",[281,1199,1200],{"class":283,"line":692},[281,1201,1202],{},"PermissionNode node = PermissionNode.builder(\"my.permission\").build();\n",[281,1204,1205],{"class":283,"line":779},[281,1206,531],{"emptyLinePlaceholder":530},[281,1208,1209],{"class":283,"line":784},[281,1210,1211],{},"\u002F\u002F build a regex permission node\n",[281,1213,1214],{"class":283,"line":790},[281,1215,1216],{},"RegexPermissionNode node = RegexPermissionNode.builder(pattern).build();\n",[281,1218,1220],{"class":283,"line":1219},18,[281,1221,531],{"emptyLinePlaceholder":530},[281,1223,1225],{"class":283,"line":1224},19,[281,1226,1227],{},"\u002F\u002F build an inheritance node\n",[281,1229,1231],{"class":283,"line":1230},20,[281,1232,1233],{},"InheritanceNode node = InheritanceNode.builder(group).build();\n",[281,1235,1237],{"class":283,"line":1236},21,[281,1238,531],{"emptyLinePlaceholder":530},[281,1240,1242],{"class":283,"line":1241},22,[281,1243,1244],{},"\u002F\u002F build a prefix node\n",[281,1246,1248],{"class":283,"line":1247},23,[281,1249,1250],{},"PrefixNode node = PrefixNode.builder(\"[Some Prefix]\", 100).build();\n",[281,1252,1254],{"class":283,"line":1253},24,[281,1255,531],{"emptyLinePlaceholder":530},[281,1257,1259],{"class":283,"line":1258},25,[281,1260,1261],{},"\u002F\u002F build a suffix node\n",[281,1263,1265],{"class":283,"line":1264},26,[281,1266,1267],{},"SuffixNode node = SuffixNode.builder(\"[Some Suffix]\", 150).build();\n",[281,1269,1271],{"class":283,"line":1270},27,[281,1272,531],{"emptyLinePlaceholder":530},[281,1274,1276],{"class":283,"line":1275},28,[281,1277,1278],{},"\u002F\u002F build a metadata node\n",[281,1280,1282],{"class":283,"line":1281},29,[281,1283,1284],{},"MetaNode node = MetaNode.builder(\"some-key\", \"some-value\").build();\n",[281,1286,1288],{"class":283,"line":1287},30,[281,1289,531],{"emptyLinePlaceholder":530},[281,1291,1293],{"class":283,"line":1292},31,[281,1294,1295],{},"\u002F\u002F build a weight node\n",[281,1297,1299],{"class":283,"line":1298},32,[281,1300,1301],{},"WeightNode node = WeightNode.builder(25).build();\n",[281,1303,1305],{"class":283,"line":1304},33,[281,1306,531],{"emptyLinePlaceholder":530},[281,1308,1310],{"class":283,"line":1309},34,[281,1311,1312],{},"\u002F\u002F build a display name node\n",[281,1314,1316],{"class":283,"line":1315},35,[281,1317,1318],{},"DisplayNameNode node = DisplayNameNode.builder(\"SeniorModerator\").build();\n",[308,1320],{},[263,1322,1324],{"id":1323},"modifying-existing-nodes","Modifying existing nodes",[244,1326,1327,1329,1330,1333],{},[278,1328,1023],{},"s are immutable - meaning their attributes cannot be changed. However, we can easily create a ",[415,1331,1332],{},"new"," node based upon the properties of an existing one.",[244,1335,1336],{},"e.g.",[271,1338,1340],{"className":273,"code":1339,"language":275,"meta":276,"style":276},"Node negated = node.toBuilder().value(false).build();\n",[278,1341,1342],{"__ignoreMap":276},[281,1343,1344],{"class":283,"line":284},[281,1345,1339],{},[308,1347],{},[263,1349,1351],{"id":1350},"reading-usergroup-data","Reading user\u002Fgroup data",[244,1353,1354,1356,1357,1359,1360,1363],{},[278,1355,384],{},"s and ",[278,1358,846],{},"s both inherit from a super interface called ",[278,1361,1362],{},"PermissionHolder",". This interface defines most of the shared permission functionality in users and groups.",[244,1365,1366,1367,1369],{},"As explained above, most data held by users\u002Fgroups are contained within ",[278,1368,1023],{}," instances. This means that there are only a few methods to think about. However, they all do slightly different things!",[244,1371,1372,1373,1376],{},"Importantly, all of the methods below return ",[404,1374,1375],{},"immutable collections",". You cannot make changes to the returned connections.",[387,1378,1380],{"id":1379},"getnodes",[278,1381,1382],{},".getNodes()",[244,1384,1385],{},"The method signature is:",[271,1387,1389],{"className":273,"code":1388,"language":275,"meta":276,"style":276},"Collection\u003CNode> getNodes()\n",[278,1390,1391],{"__ignoreMap":276},[281,1392,1393],{"class":283,"line":284},[281,1394,1388],{},[398,1396,1397,1400,1403],{},[401,1398,1399],{},"This method returns an un-flattened (or squashed) collection of the user\u002Fgroups nodes.",[401,1401,1402],{},"Entries nearer the start of the collection (index zero) have priority over nodes at the end.",[401,1404,1405,1406,1409],{},"This view does ",[404,1407,1408],{},"not"," consider inherited data.",[244,1411,1412],{},"It's a relatively cheap call, and will return quite quickly.",[244,1414,1415],{},"You can use the Stream API to easily filter the returned data to find what you need. For example, if you wanted to get a list of groups a holder inherits from, you could use something like this:",[271,1417,1419],{"className":273,"code":1418,"language":275,"meta":276,"style":276},"Set\u003CString> groups = user.getNodes().stream()\n    .filter(NodeType.INHERITANCE::matches)\n    .map(NodeType.INHERITANCE::cast)\n    .map(InheritanceNode::getGroupName)\n    .collect(Collectors.toSet());\n",[278,1420,1421,1426,1431,1436,1441],{"__ignoreMap":276},[281,1422,1423],{"class":283,"line":284},[281,1424,1425],{},"Set\u003CString> groups = user.getNodes().stream()\n",[281,1427,1428],{"class":283,"line":290},[281,1429,1430],{},"    .filter(NodeType.INHERITANCE::matches)\n",[281,1432,1433],{"class":283,"line":296},[281,1434,1435],{},"    .map(NodeType.INHERITANCE::cast)\n",[281,1437,1438],{"class":283,"line":339},[281,1439,1440],{},"    .map(InheritanceNode::getGroupName)\n",[281,1442,1443],{"class":283,"line":345},[281,1444,1445],{},"    .collect(Collectors.toSet());\n",[244,1447,1448],{},"You can make this a bit simpler by passing the node type as a parameter!",[271,1450,1452],{"className":273,"code":1451,"language":275,"meta":276,"style":276},"Set\u003CString> groups = user.getNodes(NodeType.INHERITANCE).stream()\n    .map(InheritanceNode::getGroupName)\n    .collect(Collectors.toSet());\n",[278,1453,1454,1459,1463],{"__ignoreMap":276},[281,1455,1456],{"class":283,"line":284},[281,1457,1458],{},"Set\u003CString> groups = user.getNodes(NodeType.INHERITANCE).stream()\n",[281,1460,1461],{"class":283,"line":290},[281,1462,1440],{},[281,1464,1465],{"class":283,"line":296},[281,1466,1445],{},[244,1468,1469],{},"Or even more complicated queries, like finding the max priority of a temporary prefix held on a specific server.",[271,1471,1473],{"className":273,"code":1472,"language":275,"meta":276,"style":276},"int maxWeight = user.getNodes(NodeType.PREFIX).stream()\n    .filter(Node::hasExpiry)\n    .filter(n -> n.getContexts().getAnyValue(DefaultContextKeys.SERVER_KEY)\n        .map(v -> v.equals(\"factions\")).orElse(false))\n    .mapToInt(ChatMetaNode::getPriority)\n    .max()\n    .orElse(0);\n",[278,1474,1475,1480,1485,1490,1495,1500,1505],{"__ignoreMap":276},[281,1476,1477],{"class":283,"line":284},[281,1478,1479],{},"int maxWeight = user.getNodes(NodeType.PREFIX).stream()\n",[281,1481,1482],{"class":283,"line":290},[281,1483,1484],{},"    .filter(Node::hasExpiry)\n",[281,1486,1487],{"class":283,"line":296},[281,1488,1489],{},"    .filter(n -> n.getContexts().getAnyValue(DefaultContextKeys.SERVER_KEY)\n",[281,1491,1492],{"class":283,"line":339},[281,1493,1494],{},"        .map(v -> v.equals(\"factions\")).orElse(false))\n",[281,1496,1497],{"class":283,"line":345},[281,1498,1499],{},"    .mapToInt(ChatMetaNode::getPriority)\n",[281,1501,1502],{"class":283,"line":351},[281,1503,1504],{},"    .max()\n",[281,1506,1507],{"class":283,"line":357},[281,1508,1509],{},"    .orElse(0);\n",[244,1511,1512],{},"If you need to do a more specific lookup or check, prefer using one of the other methods (described later) to avoid iterating over the whole collection of nodes.",[387,1514,1516],{"id":1515},"getdistinctnodes",[278,1517,1518],{},".getDistinctNodes()",[244,1520,1385],{},[271,1522,1524],{"className":273,"code":1523,"language":275,"meta":276,"style":276},"SortedSet\u003CNode> getDistinctNodes();\n",[278,1525,1526],{"__ignoreMap":276},[281,1527,1528],{"class":283,"line":284},[281,1529,1523],{},[398,1531,1532,1541,1544],{},[401,1533,1534,1535,1538,1539,251],{},"This method returns a sorted view of ",[278,1536,1537],{},"#getNodes",". If you are not worried about ordering, it's faster to use ",[278,1540,1537],{},[401,1542,1543],{},"The nodes are sorted according to \"priority order\". As the returned type is a set, duplicate elements may be missing.",[401,1545,1405,1546,1409],{},[404,1547,1408],{},[387,1549,1551],{"id":1550},"resolveinheritednodes",[278,1552,1553],{},".resolveInheritedNodes()",[244,1555,1385],{},[271,1557,1559],{"className":273,"code":1558,"language":275,"meta":276,"style":276},"Collection\u003CNode> resolveInheritedNodes(QueryOptions queryOptions)\n",[278,1560,1561],{"__ignoreMap":276},[281,1562,1563],{"class":283,"line":284},[281,1564,1558],{},[398,1566,1567,1570,1573],{},[401,1568,1569],{},"This method returns an un-flattened (or squashed) list of the user\u002Fgroups nodes, and all nodes they inherit from their parents.",[401,1571,1572],{},"Entries nearer the start of the list (index zero) have priority over nodes at the end.",[401,1574,1575,1576,1579,1580,1583],{},"This view ",[404,1577,1578],{},"does"," consider inherited data. If you don't need this, use the ",[278,1581,1582],{},"getNodes"," method instead.",[244,1585,1020,1586,1589,1590,251],{},[278,1587,1588],{},"QueryOptions"," parameter encapsulates the lookup settings for the query. This class is explained in a later section. If you're not worried particularly about filtering by context, simply use ",[278,1591,1592],{},"QueryOptions.nonContextual()",[308,1594],{},[263,1596,1598],{"id":1597},"modifying-usergroup-data","Modifying user\u002Fgroup data",[244,1600,1601,1602,1604,1605,1608,1609,251],{},"User\u002Fgroup data can be modified by adding and removing ",[278,1603,1023],{},"s from the holders data. This can be done by calling ",[278,1606,1607],{},"#data"," and calling the methods on the returned ",[278,1610,1611],{},"NodeMap",[244,1613,1614],{},"Here is an example of adding a permission to a user:",[271,1616,1618],{"className":273,"code":1617,"language":275,"meta":276,"style":276},"DataMutateResult result = user.data().add(Node.builder(\"your.node.here\").build());\n",[278,1619,1620],{"__ignoreMap":276},[281,1621,1622],{"class":283,"line":284},[281,1623,1617],{},[244,1625,1626,1627,1631],{},"Don't forget to ",[248,1628,1630],{"href":1629},"#saving-changes","save"," your changes!",[308,1633],{},[263,1635,1637],{"id":1636},"the-basics-of-context","The basics of Context",[244,1639,1640,1641,1643],{},"Contexts are an important concept in LuckPerms, and are introduced ",[248,1642,250],{"href":122},". They are encapsulated within the API by a few important classes.",[244,1645,1646],{},"A very basic overview is that:",[1648,1649,1650,1658,1661,1664],"blockquote",{},[244,1651,1652,1654,1655,251],{},[404,1653,121],{}," in the most basic sense simply means the ",[404,1656,1657],{},"circumstances where something will apply",[244,1659,1660],{},"A single \"context\" consists of a key and a value, both strings. The key represents the type of context, and the value represents the setting of the context key.",[244,1662,1663],{},"Contexts can be combined with each other to form so called \"context sets\" - simply a collection of context pairs.",[244,1665,1666],{},"Context keys are case-insensitive, and will be converted to lowercase by all implementations. Values however are case-sensitive. Context keys and values may not be null or empty. A key\u002Fvalue will be deemed empty if it's length is zero, or if it consists of only space characters.",[387,1668,1670],{"id":1669},"important-classes","Important classes",[387,1672,1674],{"id":1673},"contextset",[278,1675,1676],{},"ContextSet",[244,1678,1679],{},"A \"context set\" is simply a set of contexts.",[244,1681,1682,1683,1686,1687,1690,1691,1693,1694,251],{},"Internally, a context set is effectively a ",[278,1684,1685],{},"Multimap\u003CString, String>",", or a ",[278,1688,1689],{},"\u003CMap\u003CString, Collection\u003CString>>",", but importantly, it is ",[404,1692,1408],{}," a ",[278,1695,1696],{},"Map\u003CString, String>",[244,1698,1699],{},"Keys can be mapped to multiple values.",[244,1701,1020,1702,1704],{},[278,1703,1676],{}," interface defines a number of methods which can be used to interact with context set implementations. These methods should be fairly self explanatory - and are sufficiently explained in the Javadocs.",[387,1706,1708],{"id":1707},"immutablecontextset",[278,1709,1710],{},"ImmutableContextSet",[244,1712,1713,1714,1717],{},"An ",[415,1715,1716],{},"immutable"," implementation of ContextSet. You can obtain an instance in a number of ways.",[271,1719,1721],{"className":273,"code":1720,"language":275,"meta":276,"style":276},"ImmutableContextSet set1 = ImmutableContextSet.empty();  \n\nImmutableContextSet set2 = ImmutableContextSet.of(\"world\", \"world_nether\");\n\nImmutableContextSet set3 = ImmutableContextSet.builder()  \n    .add(\"world\", \"world_nether\")\n    .add(\"server\", \"survival\")\n    .build();\n\nMap\u003CString, String> map = new HashMap\u003C>();\nmap.put(\"region\", \"something\");\n\nImmutableContextSet.Builder builder = ImmutableContextSet.builder();\nmap.forEach(builder::add);\n\nImmutableContextSet set4 = builder.build();\n",[278,1722,1723,1728,1732,1737,1741,1746,1751,1756,1761,1765,1770,1775,1779,1784,1789,1793],{"__ignoreMap":276},[281,1724,1725],{"class":283,"line":284},[281,1726,1727],{},"ImmutableContextSet set1 = ImmutableContextSet.empty();  \n",[281,1729,1730],{"class":283,"line":290},[281,1731,531],{"emptyLinePlaceholder":530},[281,1733,1734],{"class":283,"line":296},[281,1735,1736],{},"ImmutableContextSet set2 = ImmutableContextSet.of(\"world\", \"world_nether\");\n",[281,1738,1739],{"class":283,"line":339},[281,1740,531],{"emptyLinePlaceholder":530},[281,1742,1743],{"class":283,"line":345},[281,1744,1745],{},"ImmutableContextSet set3 = ImmutableContextSet.builder()  \n",[281,1747,1748],{"class":283,"line":351},[281,1749,1750],{},"    .add(\"world\", \"world_nether\")\n",[281,1752,1753],{"class":283,"line":357},[281,1754,1755],{},"    .add(\"server\", \"survival\")\n",[281,1757,1758],{"class":283,"line":363},[281,1759,1760],{},"    .build();\n",[281,1762,1763],{"class":283,"line":663},[281,1764,531],{"emptyLinePlaceholder":530},[281,1766,1767],{"class":283,"line":669},[281,1768,1769],{},"Map\u003CString, String> map = new HashMap\u003C>();\n",[281,1771,1772],{"class":283,"line":675},[281,1773,1774],{},"map.put(\"region\", \"something\");\n",[281,1776,1777],{"class":283,"line":681},[281,1778,531],{"emptyLinePlaceholder":530},[281,1780,1781],{"class":283,"line":687},[281,1782,1783],{},"ImmutableContextSet.Builder builder = ImmutableContextSet.builder();\n",[281,1785,1786],{"class":283,"line":692},[281,1787,1788],{},"map.forEach(builder::add);\n",[281,1790,1791],{"class":283,"line":779},[281,1792,531],{"emptyLinePlaceholder":530},[281,1794,1795],{"class":283,"line":784},[281,1796,1797],{},"ImmutableContextSet set4 = builder.build();\n",[244,1799,1800,1801,1803,1804,1807],{},"You can of course also create an ",[278,1802,1710],{}," by first creating (or obtaining) a ",[278,1805,1806],{},"MutableContextSet"," and converting it.",[271,1809,1811],{"className":273,"code":1810,"language":275,"meta":276,"style":276},"MutableContextSet set = MutableContextSet.create();\nset.add(\"something\", \"something\");\n\nImmutableContextSet immutableSet = set.immutableCopy();\n",[278,1812,1813,1818,1823,1827],{"__ignoreMap":276},[281,1814,1815],{"class":283,"line":284},[281,1816,1817],{},"MutableContextSet set = MutableContextSet.create();\n",[281,1819,1820],{"class":283,"line":290},[281,1821,1822],{},"set.add(\"something\", \"something\");\n",[281,1824,1825],{"class":283,"line":296},[281,1826,531],{"emptyLinePlaceholder":530},[281,1828,1829],{"class":283,"line":339},[281,1830,1831],{},"ImmutableContextSet immutableSet = set.immutableCopy();\n",[387,1833,1835],{"id":1834},"mutablecontextset",[278,1836,1806],{},[244,1838,381,1839,1717],{},[415,1840,1841],{},"mutable",[271,1843,1845],{"className":273,"code":1844,"language":275,"meta":276,"style":276},"MutableContextSet set1 = MutableContextSet.create();\nset1.add(\"world\", \"text\");\n\nMutableContextSet set2 = MutableContextSet.of(\"world\", \"world_nether\");\n\nMap\u003CString, String> map = new HashMap\u003C>();\nmap.put(\"region\", \"something\");\n\nMutableContextSet set3 = MutableContextSet.create();\nmap.forEach(set3::add);\n\nset3.removeAll(\"region\");\n",[278,1846,1847,1852,1857,1861,1866,1870,1874,1878,1882,1887,1892,1896],{"__ignoreMap":276},[281,1848,1849],{"class":283,"line":284},[281,1850,1851],{},"MutableContextSet set1 = MutableContextSet.create();\n",[281,1853,1854],{"class":283,"line":290},[281,1855,1856],{},"set1.add(\"world\", \"text\");\n",[281,1858,1859],{"class":283,"line":296},[281,1860,531],{"emptyLinePlaceholder":530},[281,1862,1863],{"class":283,"line":339},[281,1864,1865],{},"MutableContextSet set2 = MutableContextSet.of(\"world\", \"world_nether\");\n",[281,1867,1868],{"class":283,"line":345},[281,1869,531],{"emptyLinePlaceholder":530},[281,1871,1872],{"class":283,"line":351},[281,1873,1769],{},[281,1875,1876],{"class":283,"line":357},[281,1877,1774],{},[281,1879,1880],{"class":283,"line":363},[281,1881,531],{"emptyLinePlaceholder":530},[281,1883,1884],{"class":283,"line":663},[281,1885,1886],{},"MutableContextSet set3 = MutableContextSet.create();\n",[281,1888,1889],{"class":283,"line":669},[281,1890,1891],{},"map.forEach(set3::add);\n",[281,1893,1894],{"class":283,"line":675},[281,1895,531],{"emptyLinePlaceholder":530},[281,1897,1898],{"class":283,"line":681},[281,1899,1900],{},"set3.removeAll(\"region\");\n",[244,1902,1903,1904,1906],{},"To edit an ",[278,1905,1710],{},", you can make a \"mutable copy\" of it.",[271,1908,1910],{"className":273,"code":1909,"language":275,"meta":276,"style":276},"ImmutableContextSet set = ImmutableContextSet.of(\"something\", \"something\");\n\nMutableContextSet mutableCopy = set.mutableCopy();\nmutableCopy.add(\"something\", \"something-else\");\n",[278,1911,1912,1917,1921,1926],{"__ignoreMap":276},[281,1913,1914],{"class":283,"line":284},[281,1915,1916],{},"ImmutableContextSet set = ImmutableContextSet.of(\"something\", \"something\");\n",[281,1918,1919],{"class":283,"line":290},[281,1920,531],{"emptyLinePlaceholder":530},[281,1922,1923],{"class":283,"line":296},[281,1924,1925],{},"MutableContextSet mutableCopy = set.mutableCopy();\n",[281,1927,1928],{"class":283,"line":339},[281,1929,1930],{},"mutableCopy.add(\"something\", \"something-else\");\n",[387,1932,1934],{"id":1933},"registering-contextcalculators","Registering ContextCalculators",[244,1936,1937],{},"A \"subject\" (a player in most cases) is just an object which can have contexts applied to them.",[244,1939,1940,1941,1944,1945,1948],{},"In other words, a \"subject\" is an object which has an ",[404,1942,1943],{},"active context set",". A ",[278,1946,1947],{},"ContextCalculator"," is an object which determines the \"active\" contexts for a given type of Subject.",[244,1950,1951],{},"The subject type varies between platforms.",[1953,1954,1955,1968],"table",{},[1956,1957,1958],"thead",{},[1959,1960,1961,1965],"tr",{},[1962,1963,1964],"th",{},"Platform",[1962,1966,1967],{},"Subject type",[1969,1970,1971,1982,1992,2002,2012,2022,2032],"tbody",{},[1959,1972,1973,1977],{},[1974,1975,1976],"td",{},"Bukkit",[1974,1978,1979],{},[278,1980,1981],{},"org.bukkit.entity.Player",[1959,1983,1984,1987],{},[1974,1985,1986],{},"BungeeCord",[1974,1988,1989],{},[278,1990,1991],{},"net.md_5.bungee.api.connection.ProxiedPlayer",[1959,1993,1994,1997],{},[1974,1995,1996],{},"Sponge",[1974,1998,1999],{},[278,2000,2001],{},"org.spongepowered.api.service.permission.Subject",[1959,2003,2004,2007],{},[1974,2005,2006],{},"Fabric",[1974,2008,2009],{},[278,2010,2011],{},"net.minecraft.server.network.ServerPlayerEntity",[1959,2013,2014,2017],{},[1974,2015,2016],{},"Forge",[1974,2018,2019],{},[278,2020,2021],{},"net.minecraft.server.level.ServerPlayer",[1959,2023,2024,2027],{},[1974,2025,2026],{},"Nukkit",[1974,2028,2029],{},[278,2030,2031],{},"cn.nukkit.Player",[1959,2033,2034,2037],{},[1974,2035,2036],{},"Velocity",[1974,2038,2039],{},[278,2040,2041],{},"com.velocitypowered.api.proxy.Player",[244,2043,2044,2045,251],{},"In order to provide your own context, you need to create and register a ",[278,2046,1947],{},[244,2048,2049,2050,2053],{},"For example, if I wanted to provide a context for the player's gamemode, in order to set permissions for players only when they are in creative, I'd create a calculator as follows.\nThe ",[278,2051,2052],{},"estimatePotentialContexts"," method can be added, but is not necessary, to show context suggestions in the tab completion.",[271,2055,2057],{"className":273,"code":2056,"language":275,"meta":276,"style":276},"public class CustomCalculator implements ContextCalculator\u003CPlayer> {\n\n    @Override  \n    public void calculate(Player target, ContextConsumer contextConsumer) {\n        contextConsumer.accept(\"gamemode\", target.getGameMode().name());\n    }\n    \n    @Override\n    public ContextSet estimatePotentialContexts() {\n        ImmutableContextSet.Builder builder = ImmutableContextSet.builder();\n        for (GameMode gameMode : GameMode.values()) {\n            builder.add(\"gamemode\", gameMode.name().toLowerCase());\n        }\n        return builder.build();\n    }\n    \n}\n",[278,2058,2059,2064,2068,2073,2078,2083,2087,2092,2097,2102,2107,2112,2117,2121,2126,2130,2134],{"__ignoreMap":276},[281,2060,2061],{"class":283,"line":284},[281,2062,2063],{},"public class CustomCalculator implements ContextCalculator\u003CPlayer> {\n",[281,2065,2066],{"class":283,"line":290},[281,2067,531],{"emptyLinePlaceholder":530},[281,2069,2070],{"class":283,"line":296},[281,2071,2072],{},"    @Override  \n",[281,2074,2075],{"class":283,"line":339},[281,2076,2077],{},"    public void calculate(Player target, ContextConsumer contextConsumer) {\n",[281,2079,2080],{"class":283,"line":345},[281,2081,2082],{},"        contextConsumer.accept(\"gamemode\", target.getGameMode().name());\n",[281,2084,2085],{"class":283,"line":351},[281,2086,354],{},[281,2088,2089],{"class":283,"line":357},[281,2090,2091],{},"    \n",[281,2093,2094],{"class":283,"line":363},[281,2095,2096],{},"    @Override\n",[281,2098,2099],{"class":283,"line":663},[281,2100,2101],{},"    public ContextSet estimatePotentialContexts() {\n",[281,2103,2104],{"class":283,"line":669},[281,2105,2106],{},"        ImmutableContextSet.Builder builder = ImmutableContextSet.builder();\n",[281,2108,2109],{"class":283,"line":675},[281,2110,2111],{},"        for (GameMode gameMode : GameMode.values()) {\n",[281,2113,2114],{"class":283,"line":681},[281,2115,2116],{},"            builder.add(\"gamemode\", gameMode.name().toLowerCase());\n",[281,2118,2119],{"class":283,"line":687},[281,2120,348],{},[281,2122,2123],{"class":283,"line":692},[281,2124,2125],{},"        return builder.build();\n",[281,2127,2128],{"class":283,"line":779},[281,2129,354],{},[281,2131,2132],{"class":283,"line":784},[281,2133,2091],{},[281,2135,2136],{"class":283,"line":790},[281,2137,299],{},[244,2139,2140],{},"Then register it using",[271,2142,2144],{"className":273,"code":2143,"language":275,"meta":276,"style":276},"luckPerms.getContextManager().registerCalculator(new CustomCalculator());\n",[278,2145,2146],{"__ignoreMap":276},[281,2147,2148],{"class":283,"line":284},[281,2149,2143],{},[387,2151,2153],{"id":2152},"querying-active-contextsquery-options","Querying active contexts\u002Fquery options",[244,2155,2156,2157,251],{},"You can query the \"active\" contexts\u002Fquery options of a Subject using the ",[278,2158,2159],{},"ContextManager",[244,2161,2162],{},"If you already have an instance of the subject type, you can query directly using this.",[271,2164,2166],{"className":273,"code":2165,"language":275,"meta":276,"style":276},"Player player = ...;\n\nImmutableContextSet contextSet = luckPerms.getContextManager().getContext(player);\nQueryOptions queryOptions = luckPerms.getContextManager().getQueryOptions(player);\n",[278,2167,2168,2172,2176,2181],{"__ignoreMap":276},[281,2169,2170],{"class":283,"line":284},[281,2171,446],{},[281,2173,2174],{"class":283,"line":290},[281,2175,531],{"emptyLinePlaceholder":530},[281,2177,2178],{"class":283,"line":296},[281,2179,2180],{},"ImmutableContextSet contextSet = luckPerms.getContextManager().getContext(player);\n",[281,2182,2183],{"class":283,"line":339},[281,2184,2185],{},"QueryOptions queryOptions = luckPerms.getContextManager().getQueryOptions(player);\n",[244,2187,2188,2189,2191],{},"If you only have a ",[278,2190,384],{},", you can still perform a lookup, however, a result will only be returned if the corresponding subject (player) is online.",[271,2193,2195],{"className":273,"code":2194,"language":275,"meta":276,"style":276},"Optional\u003CImmutableContextSet> contextSet = luckPerms.getContextManager().getContext(user);\nOptional\u003CQueryOptions> queryOptions = luckPerms.getContextManager().getQueryOptions(user);\n",[278,2196,2197,2202],{"__ignoreMap":276},[281,2198,2199],{"class":283,"line":284},[281,2200,2201],{},"Optional\u003CImmutableContextSet> contextSet = luckPerms.getContextManager().getContext(user);\n",[281,2203,2204],{"class":283,"line":290},[281,2205,2206],{},"Optional\u003CQueryOptions> queryOptions = luckPerms.getContextManager().getQueryOptions(user);\n",[244,2208,2209],{},"If you absolutely need to obtain an instance, you can fallback to the server's \"static\" context\u002Fquery option. (these are formed using calculators which provide contexts\u002Fquery options regardless of the passed subject.)",[271,2211,2213],{"className":273,"code":2212,"language":275,"meta":276,"style":276},"User user = ...;\n\n\u002F\u002F This is the easy way...\nImmutableContextSet contextSet = user.getQueryOptions().context();\nQueryOptions queryOptions = user.getQueryOptions();\n\n\u002F\u002F But is equivalent to this...\nContextManager cm = luckPerms.getContextManager();\nImmutableContextSet contextSet = cm.getContext(user).orElse(cm.getStaticContext());\nQueryOptions queryOptions = cm.getQueryOptions(user).orElse(cm.getStaticQueryOptions());\n",[278,2214,2215,2220,2224,2229,2234,2239,2243,2248,2253,2258],{"__ignoreMap":276},[281,2216,2217],{"class":283,"line":284},[281,2218,2219],{},"User user = ...;\n",[281,2221,2222],{"class":283,"line":290},[281,2223,531],{"emptyLinePlaceholder":530},[281,2225,2226],{"class":283,"line":296},[281,2227,2228],{},"\u002F\u002F This is the easy way...\n",[281,2230,2231],{"class":283,"line":339},[281,2232,2233],{},"ImmutableContextSet contextSet = user.getQueryOptions().context();\n",[281,2235,2236],{"class":283,"line":345},[281,2237,2238],{},"QueryOptions queryOptions = user.getQueryOptions();\n",[281,2240,2241],{"class":283,"line":351},[281,2242,531],{"emptyLinePlaceholder":530},[281,2244,2245],{"class":283,"line":357},[281,2246,2247],{},"\u002F\u002F But is equivalent to this...\n",[281,2249,2250],{"class":283,"line":363},[281,2251,2252],{},"ContextManager cm = luckPerms.getContextManager();\n",[281,2254,2255],{"class":283,"line":663},[281,2256,2257],{},"ImmutableContextSet contextSet = cm.getContext(user).orElse(cm.getStaticContext());\n",[281,2259,2260],{"class":283,"line":669},[281,2261,2262],{},"QueryOptions queryOptions = cm.getQueryOptions(user).orElse(cm.getStaticQueryOptions());\n",[308,2264],{},[263,2266,2268],{"id":2267},"the-basics-of-cacheddata","The basics of CachedData",[244,2270,2271,2272,1356,2274,2276,2277,2280,2281,2283,2284,2286,2287,251],{},"All ",[278,2273,384],{},[278,2275,846],{},"s also have an extra object attached to them called ",[278,2278,2279],{},"CachedData",". This is the name of the caching class used by LuckPerms to store easily query-able data for all permission holders. The lookup methods provided by this class are very fast. If you're doing frequent data lookups, it is highly recommended that you use ",[278,2282,2279],{}," over the methods in ",[278,2285,384],{}," and ",[278,2288,846],{},[244,2290,2291,2292,2294,2295,2297],{},"Everything in ",[278,2293,2279],{}," is indexed by ",[278,2296,1588],{},", as this is how LuckPerms processes all lookups internally.",[244,2299,2300,2301,2286,2304,251],{},"The contained data is split into two separate sections: ",[278,2302,2303],{},"CachedPermissionData",[278,2305,2306],{},"CachedMetaData",[398,2308,2309,2314],{},[401,2310,2311,2313],{},[278,2312,2303],{}," contains the user\u002Fgroups fully resolved map of permissions, and allows you to run permission checks in exactly the same way as you would using the Player class provided by the platform.",[401,2315,2316,2318],{},[278,2317,2306],{}," contains information about a user\u002Fgroups prefixes, suffixes, and meta values.",[387,2320,2322,2323,2286,2325],{"id":2321},"obtaining-cachedpermissiondata-and-cachedmetadata","Obtaining ",[278,2324,2303],{},[278,2326,2306],{},[244,2328,2329],{},"You need either:",[398,2331,2332,2339],{},[401,2333,2334,2335,2338],{},"A platform ",[278,2336,2337],{},"Player"," instance",[401,2340,2341,2342,804,2344,2346,2347,2349],{},"A LuckPerms ",[278,2343,384],{},[278,2345,846],{}," instance + optionally some ",[278,2348,1588],{}," (see above for how to obtain this)",[244,2351,2352,2353,2355,2356,2358,2359,2362],{},"If you have a ",[278,2354,2337],{}," platform instance (like ",[415,2357,1981],{},"), you can use the ",[278,2360,2361],{},"PlayerAdapter"," to obtain cached data.",[271,2364,2366],{"className":273,"code":2365,"language":275,"meta":276,"style":276},"Player player = ...;\nPlayerAdapter\u003CPlayer> adapter = luckperms.getPlayerAdapter(Player.class);\n\nCachedPermissionData permissionData = adapter.getPermissionData(player);\nCachedMetaData metaData = adapter.getMetaData(player);\n",[278,2367,2368,2372,2377,2381,2386],{"__ignoreMap":276},[281,2369,2370],{"class":283,"line":284},[281,2371,446],{},[281,2373,2374],{"class":283,"line":290},[281,2375,2376],{},"PlayerAdapter\u003CPlayer> adapter = luckperms.getPlayerAdapter(Player.class);\n",[281,2378,2379],{"class":283,"line":296},[281,2380,531],{"emptyLinePlaceholder":530},[281,2382,2383],{"class":283,"line":339},[281,2384,2385],{},"CachedPermissionData permissionData = adapter.getPermissionData(player);\n",[281,2387,2388],{"class":283,"line":345},[281,2389,2390],{},"CachedMetaData metaData = adapter.getMetaData(player);\n",[244,2392,2393,2394,804,2396,2398],{},"If you already have a LuckPerms ",[278,2395,384],{},[278,2397,846],{}," instance, you can use the following methods to obtain cached data.",[271,2400,2402],{"className":273,"code":2401,"language":275,"meta":276,"style":276},"\u002F\u002F Will attempt to use the most appropriate currect query options for the User\nCachedPermissionData permissionData = user.getCachedData().getPermissionData();\nCachedMetaData metaData = user.getCachedData().getMetaData();\n\n\u002F\u002F You can also manually specify which query options to use\nCachedPermissionData permissionData = user.getCachedData().getPermissionData(queryOptions);\nCachedMetaData metaData = user.getCachedData().getMetaData(queryOptions);\n",[278,2403,2404,2409,2414,2419,2423,2428,2433],{"__ignoreMap":276},[281,2405,2406],{"class":283,"line":284},[281,2407,2408],{},"\u002F\u002F Will attempt to use the most appropriate currect query options for the User\n",[281,2410,2411],{"class":283,"line":290},[281,2412,2413],{},"CachedPermissionData permissionData = user.getCachedData().getPermissionData();\n",[281,2415,2416],{"class":283,"line":296},[281,2417,2418],{},"CachedMetaData metaData = user.getCachedData().getMetaData();\n",[281,2420,2421],{"class":283,"line":339},[281,2422,531],{"emptyLinePlaceholder":530},[281,2424,2425],{"class":283,"line":345},[281,2426,2427],{},"\u002F\u002F You can also manually specify which query options to use\n",[281,2429,2430],{"class":283,"line":351},[281,2431,2432],{},"CachedPermissionData permissionData = user.getCachedData().getPermissionData(queryOptions);\n",[281,2434,2435],{"class":283,"line":357},[281,2436,2437],{},"CachedMetaData metaData = user.getCachedData().getMetaData(queryOptions);\n",[244,2439,2440],{},"Once you have a cached data instance, you can perform lots of different queries.",[387,2442,2444],{"id":2443},"performing-permission-checks","Performing permission checks",[271,2446,2448],{"className":273,"code":2447,"language":275,"meta":276,"style":276},"\u002F\u002F run a permission check!\nTristate checkResult = permissionData.checkPermission(\"some.permission.node\");\n\n\u002F\u002F the same as what Player#hasPermission would return\nboolean checkResultAsBoolean = checkResult.asBoolean();\n",[278,2449,2450,2455,2460,2464,2469],{"__ignoreMap":276},[281,2451,2452],{"class":283,"line":284},[281,2453,2454],{},"\u002F\u002F run a permission check!\n",[281,2456,2457],{"class":283,"line":290},[281,2458,2459],{},"Tristate checkResult = permissionData.checkPermission(\"some.permission.node\");\n",[281,2461,2462],{"class":283,"line":296},[281,2463,531],{"emptyLinePlaceholder":530},[281,2465,2466],{"class":283,"line":339},[281,2467,2468],{},"\u002F\u002F the same as what Player#hasPermission would return\n",[281,2470,2471],{"class":283,"line":345},[281,2472,2473],{},"boolean checkResultAsBoolean = checkResult.asBoolean();\n",[244,2475,2476,2477,2479,2480,2483],{},"We can put all of this together to create a method that can run a \"normal\" permission check when passed a ",[278,2478,384],{}," and a ",[278,2481,2482],{},"String"," (the permission).",[271,2485,2487],{"className":273,"code":2486,"language":275,"meta":276,"style":276},"public boolean hasPermission(User user, String permission) {\n    return user.getCachedData().getPermissionData().checkPermission(permission).asBoolean();\n}\n",[278,2488,2489,2494,2499],{"__ignoreMap":276},[281,2490,2491],{"class":283,"line":284},[281,2492,2493],{},"public boolean hasPermission(User user, String permission) {\n",[281,2495,2496],{"class":283,"line":290},[281,2497,2498],{},"    return user.getCachedData().getPermissionData().checkPermission(permission).asBoolean();\n",[281,2500,2501],{"class":283,"line":296},[281,2502,299],{},[387,2504,2506],{"id":2505},"retrieving-prefixessuffixes","Retrieving prefixes\u002Fsuffixes",[271,2508,2510],{"className":273,"code":2509,"language":275,"meta":276,"style":276},"String prefix = user.getCachedData().getMetaData().getPrefix();\nString suffix = user.getCachedData().getMetaData().getSuffix();\n",[278,2511,2512,2517],{"__ignoreMap":276},[281,2513,2514],{"class":283,"line":284},[281,2515,2516],{},"String prefix = user.getCachedData().getMetaData().getPrefix();\n",[281,2518,2519],{"class":283,"line":290},[281,2520,2521],{},"String suffix = user.getCachedData().getMetaData().getSuffix();\n",[387,2523,2525],{"id":2524},"retrieving-metadata","Retrieving metadata",[271,2527,2529],{"className":273,"code":2528,"language":275,"meta":276,"style":276},"String metaValue = user.getCachedData().getMetaData().getMetaValue(\"some-key\");\n",[278,2530,2531],{"__ignoreMap":276},[281,2532,2533],{"class":283,"line":284},[281,2534,2528],{},[244,2536,2537,2538,2540],{},"These methods work with ",[278,2539,846],{},"s too!",[308,2542],{},[263,2544,2546],{"id":2545},"store-and-query-custom-metadata","Store and query custom metadata",[244,2548,2549],{},"The metadata stored by LuckPerms isn't limited to only a few types. You use the API to easily store any sort of data about players, whilst also taking advantage of the storage \u002F caching systems built into LP.",[387,2551,2553],{"id":2552},"setting-metadata","Setting metadata",[244,2555,2556,2557,2559],{},"You can set metadata by creating & adding a ",[278,2558,1095],{}," to a user.",[244,2561,2562],{},"To illustrate this, let's store a player \"level\" meta value.",[271,2564,2566],{"className":273,"code":2565,"language":275,"meta":276,"style":276},"public void setLevel(Player player, int level) {\n    \u002F\u002F obtain a User instance (by any means! see above for other ways)\n    User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);\n\n    \u002F\u002F create a new MetaNode holding the level value\n    \u002F\u002F of course, this can have context\u002Fexpiry\u002Fetc too!\n    MetaNode node = MetaNode.builder(\"level\", Integer.toString(level)).build();\n\n    \u002F\u002F clear any existing meta nodes with the same key - we want to override\n    user.data().clear(NodeType.META.predicate(mn -> mn.getMetaKey().equals(\"level\")));\n    \u002F\u002F add the new node\n    user.data().add(node);\n\n    \u002F\u002F save!\n    luckPerms.getUserManager().saveUser(user);\n}\n",[278,2567,2568,2573,2578,2583,2587,2592,2597,2602,2606,2611,2616,2621,2626,2630,2635,2639],{"__ignoreMap":276},[281,2569,2570],{"class":283,"line":284},[281,2571,2572],{},"public void setLevel(Player player, int level) {\n",[281,2574,2575],{"class":283,"line":290},[281,2576,2577],{},"    \u002F\u002F obtain a User instance (by any means! see above for other ways)\n",[281,2579,2580],{"class":283,"line":296},[281,2581,2582],{},"    User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);\n",[281,2584,2585],{"class":283,"line":339},[281,2586,531],{"emptyLinePlaceholder":530},[281,2588,2589],{"class":283,"line":345},[281,2590,2591],{},"    \u002F\u002F create a new MetaNode holding the level value\n",[281,2593,2594],{"class":283,"line":351},[281,2595,2596],{},"    \u002F\u002F of course, this can have context\u002Fexpiry\u002Fetc too!\n",[281,2598,2599],{"class":283,"line":357},[281,2600,2601],{},"    MetaNode node = MetaNode.builder(\"level\", Integer.toString(level)).build();\n",[281,2603,2604],{"class":283,"line":363},[281,2605,531],{"emptyLinePlaceholder":530},[281,2607,2608],{"class":283,"line":663},[281,2609,2610],{},"    \u002F\u002F clear any existing meta nodes with the same key - we want to override\n",[281,2612,2613],{"class":283,"line":669},[281,2614,2615],{},"    user.data().clear(NodeType.META.predicate(mn -> mn.getMetaKey().equals(\"level\")));\n",[281,2617,2618],{"class":283,"line":675},[281,2619,2620],{},"    \u002F\u002F add the new node\n",[281,2622,2623],{"class":283,"line":681},[281,2624,2625],{},"    user.data().add(node);\n",[281,2627,2628],{"class":283,"line":687},[281,2629,531],{"emptyLinePlaceholder":530},[281,2631,2632],{"class":283,"line":692},[281,2633,2634],{},"    \u002F\u002F save!\n",[281,2636,2637],{"class":283,"line":779},[281,2638,959],{},[281,2640,2641],{"class":283,"line":784},[281,2642,299],{},[387,2644,2646],{"id":2645},"querying-metadata","Querying metadata",[244,2648,2649],{},"Once the metadata is set, querying it is easy!",[271,2651,2653],{"className":273,"code":2652,"language":275,"meta":276,"style":276},"public int getLevel(Player player) {\n    \u002F\u002F obtain CachedMetaData - the easiest way is via the PlayerAdapter\n    \u002F\u002F of course, you can get it via a User too if the player is offline.\n    CachedMetaData metaData = luckPerms.getPlayerAdapter(Player.class).getMetaData(player);\n\n    \u002F\u002F query & parse the meta value\n    return metaData.getMetaValue(\"level\", Integer::parseInt).orElse(0);\n}\n",[278,2654,2655,2660,2665,2670,2675,2679,2684,2689],{"__ignoreMap":276},[281,2656,2657],{"class":283,"line":284},[281,2658,2659],{},"public int getLevel(Player player) {\n",[281,2661,2662],{"class":283,"line":290},[281,2663,2664],{},"    \u002F\u002F obtain CachedMetaData - the easiest way is via the PlayerAdapter\n",[281,2666,2667],{"class":283,"line":296},[281,2668,2669],{},"    \u002F\u002F of course, you can get it via a User too if the player is offline.\n",[281,2671,2672],{"class":283,"line":339},[281,2673,2674],{},"    CachedMetaData metaData = luckPerms.getPlayerAdapter(Player.class).getMetaData(player);\n",[281,2676,2677],{"class":283,"line":345},[281,2678,531],{"emptyLinePlaceholder":530},[281,2680,2681],{"class":283,"line":351},[281,2682,2683],{},"    \u002F\u002F query & parse the meta value\n",[281,2685,2686],{"class":283,"line":357},[281,2687,2688],{},"    return metaData.getMetaValue(\"level\", Integer::parseInt).orElse(0);\n",[281,2690,2691],{"class":283,"line":363},[281,2692,299],{},[308,2694],{},[263,2696,2698],{"id":2697},"events","Events",[244,2700,2701],{},"LuckPerms uses it's own event system, completely separate from the event systems used by platforms (e.g. Bukkit or Sponge). This means that instead of registering your listener with the server, you must register it directly with LuckPerms.",[244,2703,2704,2705,2708,2709,2716,2717,2724],{},"The events supported by LuckPerms are defined as ",[278,2706,2707],{},"interface","s that extend from ",[248,2710,2713],{"href":2711,"rel":2712},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Fblob\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent\u002FLuckPermsEvent.java",[259],[278,2714,2715],{},"LuckPermsEvent",". They can be found in the ",[248,2718,2721],{"href":2719,"rel":2720},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Ftree\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent",[259],[278,2722,2723],{},"net.luckperms.api.event"," package.",[387,2726,2728],{"id":2727},"event-listeners","Event listeners",[244,2730,2731,2732,2739,2740,2743,2744,906],{},"To listen to events, you first need to obtain the ",[248,2733,2736],{"href":2734,"rel":2735},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Fblob\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent\u002FEventBus.java",[259],[278,2737,2738],{},"EventBus"," instance using ",[278,2741,2742],{},"LuckPerms#getEventBus",", then register each listener using the ",[278,2745,2746],{},"subscribe",[244,2748,1020,2749,2751,2752,2755],{},[278,2750,2746],{}," method accepts a ",[278,2753,2754],{},"java.util.function.Consumer"," object - which allows listeners to be defined as:",[2757,2758,2759,2766,2772],"ol",{},[401,2760,2761],{},[248,2762,2765],{"href":2763,"rel":2764},"https:\u002F\u002Fdocs.oracle.com\u002Fjavase\u002Ftutorial\u002Fjava\u002FjavaOO\u002Flambdaexpressions.html#syntax",[259],"Expression lambdas",[401,2767,2768],{},[248,2769,2771],{"href":2763,"rel":2770},[259],"Statement lambdas",[401,2773,2774],{},[248,2775,2778],{"href":2776,"rel":2777},"https:\u002F\u002Fdocs.oracle.com\u002Fjavase\u002Ftutorial\u002Fjava\u002FjavaOO\u002Fmethodreferences.html",[259],"Method references",[244,2780,2781],{},"It's usually a good idea to create a separate class for your listeners. Here's a short example class demonstrating how to subscribe to events.",[271,2783,2785],{"className":273,"code":2784,"language":275,"meta":276,"style":276},"import net.luckperms.api.event.EventBus;\nimport net.luckperms.api.event.log.LogPublishEvent;\nimport net.luckperms.api.event.user.UserLoadEvent;\nimport net.luckperms.api.event.user.track.UserPromoteEvent;\n\npublic class MyListener {\n    private final MyPlugin plugin;\n\n    public MyListener(MyPlugin plugin, LuckPerms luckPerms) {\n        this.plugin = plugin;\n\n        EventBus eventBus = luckPerms.getEventBus();\n\n        \u002F\u002F 1. Subscribe to an event using an expression lambda\n        eventBus.subscribe(this.plugin, LogPublishEvent.class, e -> \u002F* ... *\u002F);\n\n        \u002F\u002F 2. Subscribe to an event using a statement lambda\n        eventBus.subscribe(this.plugin, UserLoadEvent.class, e -> {\n            \u002F\u002F ...\n        });\n\n        \u002F\u002F 3. Subscribe to an event using a method reference\n        eventBus.subscribe(this.plugin, UserPromoteEvent.class, this::onUserPromote);\n    }\n\n    private void onUserPromote(UserPromoteEvent event) {\n        \u002F\u002F ...\n    }\n}\n",[278,2786,2787,2792,2797,2802,2807,2811,2816,2821,2825,2830,2835,2839,2844,2848,2853,2858,2862,2867,2872,2877,2881,2885,2890,2895,2899,2903,2908,2913,2917],{"__ignoreMap":276},[281,2788,2789],{"class":283,"line":284},[281,2790,2791],{},"import net.luckperms.api.event.EventBus;\n",[281,2793,2794],{"class":283,"line":290},[281,2795,2796],{},"import net.luckperms.api.event.log.LogPublishEvent;\n",[281,2798,2799],{"class":283,"line":296},[281,2800,2801],{},"import net.luckperms.api.event.user.UserLoadEvent;\n",[281,2803,2804],{"class":283,"line":339},[281,2805,2806],{},"import net.luckperms.api.event.user.track.UserPromoteEvent;\n",[281,2808,2809],{"class":283,"line":345},[281,2810,531],{"emptyLinePlaceholder":530},[281,2812,2813],{"class":283,"line":351},[281,2814,2815],{},"public class MyListener {\n",[281,2817,2818],{"class":283,"line":357},[281,2819,2820],{},"    private final MyPlugin plugin;\n",[281,2822,2823],{"class":283,"line":363},[281,2824,531],{"emptyLinePlaceholder":530},[281,2826,2827],{"class":283,"line":663},[281,2828,2829],{},"    public MyListener(MyPlugin plugin, LuckPerms luckPerms) {\n",[281,2831,2832],{"class":283,"line":669},[281,2833,2834],{},"        this.plugin = plugin;\n",[281,2836,2837],{"class":283,"line":675},[281,2838,531],{"emptyLinePlaceholder":530},[281,2840,2841],{"class":283,"line":681},[281,2842,2843],{},"        EventBus eventBus = luckPerms.getEventBus();\n",[281,2845,2846],{"class":283,"line":687},[281,2847,531],{"emptyLinePlaceholder":530},[281,2849,2850],{"class":283,"line":692},[281,2851,2852],{},"        \u002F\u002F 1. Subscribe to an event using an expression lambda\n",[281,2854,2855],{"class":283,"line":779},[281,2856,2857],{},"        eventBus.subscribe(this.plugin, LogPublishEvent.class, e -> \u002F* ... *\u002F);\n",[281,2859,2860],{"class":283,"line":784},[281,2861,531],{"emptyLinePlaceholder":530},[281,2863,2864],{"class":283,"line":790},[281,2865,2866],{},"        \u002F\u002F 2. Subscribe to an event using a statement lambda\n",[281,2868,2869],{"class":283,"line":1219},[281,2870,2871],{},"        eventBus.subscribe(this.plugin, UserLoadEvent.class, e -> {\n",[281,2873,2874],{"class":283,"line":1224},[281,2875,2876],{},"            \u002F\u002F ...\n",[281,2878,2879],{"class":283,"line":1230},[281,2880,739],{},[281,2882,2883],{"class":283,"line":1236},[281,2884,531],{"emptyLinePlaceholder":530},[281,2886,2887],{"class":283,"line":1241},[281,2888,2889],{},"        \u002F\u002F 3. Subscribe to an event using a method reference\n",[281,2891,2892],{"class":283,"line":1247},[281,2893,2894],{},"        eventBus.subscribe(this.plugin, UserPromoteEvent.class, this::onUserPromote);\n",[281,2896,2897],{"class":283,"line":1253},[281,2898,354],{},[281,2900,2901],{"class":283,"line":1258},[281,2902,531],{"emptyLinePlaceholder":530},[281,2904,2905],{"class":283,"line":1264},[281,2906,2907],{},"    private void onUserPromote(UserPromoteEvent event) {\n",[281,2909,2910],{"class":283,"line":1270},[281,2911,2912],{},"        \u002F\u002F ...\n",[281,2914,2915],{"class":283,"line":1275},[281,2916,354],{},[281,2918,2919],{"class":283,"line":1281},[281,2920,299],{},[244,2922,2923],{},"If your listener is simple, then an expression or statement lambda is best. If your listener is complex, then method references are probably going to be more organised.",[387,2925,2927],{"id":2926},"listening-for-changes-to-user-cached-data","Listening for changes to user cached data",[244,2929,2930,2931,251],{},"If you have a system that depends on a users cached data (e.g. their prefix or permission state), then you may find it necessary to perform some action in your plugin when the data changes (e.g. invalidate or update a cache). The best & most simple event to use to achieve this is the ",[248,2932,2935],{"href":2933,"rel":2934},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Fblob\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent\u002Fuser\u002FUserDataRecalculateEvent.java",[259],[278,2936,2937],{},"UserDataRecalculateEvent",[244,2939,2940],{},"This is a simple event that is \"called when a User's cached data is refreshed\". It doesn't give any information about what caused the refresh - just that it happened!",[387,2942,2944],{"id":2943},"listening-for-changes-to-permissionsparent-groupsetc","Listening for changes to permissions\u002Fparent groups\u002Fetc",[244,2946,2947,2948,2955],{},"Recall from earlier that ",[248,2949,2951,2952,2954],{"href":2950},"#the-basics-of-node","all user\u002Fgroup data is stored as ",[278,2953,1023],{},"s"," - introducing:",[398,2957,2958,2969,2979],{},[401,2959,2960,2961,2968],{},"the ",[248,2962,2965],{"href":2963,"rel":2964},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Fblob\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent\u002Fnode\u002FNodeAddEvent.java",[259],[278,2966,2967],{},"NodeAddEvent"," - called when a node is added to a user\u002Fgroup",[401,2970,2960,2971,2978],{},[248,2972,2975],{"href":2973,"rel":2974},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Fblob\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent\u002Fnode\u002FNodeRemoveEvent.java",[259],[278,2976,2977],{},"NodeRemoveEvent"," - called when a node is removed from a user\u002Fgroup",[401,2980,2960,2981,2988],{},[248,2982,2985],{"href":2983,"rel":2984},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Fblob\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent\u002Fnode\u002FNodeClearEvent.java",[259],[278,2986,2987],{},"NodeClearEvent"," - called when a user\u002Fgroup has all\u002Fsome their existing nodes removed",[244,2990,2991,2992,2999],{},"All of these events extend from ",[248,2993,2996],{"href":2994,"rel":2995},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002FLuckPerms\u002Fblob\u002Fmaster\u002Fapi\u002Fsrc\u002Fmain\u002Fjava\u002Fnet\u002Fluckperms\u002Fapi\u002Fevent\u002Fnode\u002FNodeMutateEvent.java",[259],[278,2997,2998],{},"NodeMutateEvent"," which defines the base properties.",[244,3001,3002],{},"These events cover all possible changes that could be made to a user\u002Fgroups LuckPerms data. The trick is to figure out which event you need, and how to filter down to only catch the desired changes.",[244,3004,3005,3006,3009,3010,3013,3014,3016,3017,3020,3021,2286,3024,3027],{},"For example, to catch ",[415,3007,3008],{},"prefixes"," being added to ",[415,3011,3012],{},"groups",", you would need to listen to the ",[278,3015,2967],{},", then check if ",[278,3018,3019],{},"e.isGroup() && e.getNode().getType() == NodeType.PREFIX",". Of course, afterwards, you could then cast ",[278,3022,3023],{},"((Group) e.getTarget())",[278,3025,3026],{},"((PrefixNode) node)"," to extract further information.",[244,3029,3030,3031,3033],{},"To catch both additions and removals, you can either subscribe to the generic ",[278,3032,2998],{},", or to both the add and remove events separately.",[244,3035,3036,3037,3042],{},"There is an ",[248,3038,3041],{"href":3039,"rel":3040},"https:\u002F\u002Fgithub.com\u002FLuckPerms\u002Fapi-cookbook\u002Fblob\u002Fmaster\u002Fsrc\u002Fmain\u002Fjava\u002Fme\u002Flucko\u002Flpcookbook\u002Flistener\u002FPlayerNodeChangeListener.java",[259],"example listener in the API Cookbook"," which demonstrates this nicely.",[3044,3045,3046],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":276,"searchDepth":296,"depth":296,"links":3048},[3049,3050,3051,3059,3060,3061,3062,3063,3064,3069,3070,3078,3085,3089],{"id":265,"depth":290,"text":266},{"id":312,"depth":290,"text":313},{"id":377,"depth":290,"text":378,"children":3052},[3053,3054],{"id":389,"depth":296,"text":390},{"id":424,"depth":296,"text":425,"children":3055},[3056,3057,3058],{"id":429,"depth":339,"text":430},{"id":476,"depth":339,"text":477},{"id":554,"depth":339,"text":555},{"id":839,"depth":290,"text":840},{"id":921,"depth":290,"text":922},{"id":1016,"depth":290,"text":1017},{"id":1113,"depth":290,"text":1114},{"id":1323,"depth":290,"text":1324},{"id":1350,"depth":290,"text":1351,"children":3065},[3066,3067,3068],{"id":1379,"depth":296,"text":1382},{"id":1515,"depth":296,"text":1518},{"id":1550,"depth":296,"text":1553},{"id":1597,"depth":290,"text":1598},{"id":1636,"depth":290,"text":1637,"children":3071},[3072,3073,3074,3075,3076,3077],{"id":1669,"depth":296,"text":1670},{"id":1673,"depth":296,"text":1676},{"id":1707,"depth":296,"text":1710},{"id":1834,"depth":296,"text":1806},{"id":1933,"depth":296,"text":1934},{"id":2152,"depth":296,"text":2153},{"id":2267,"depth":290,"text":2268,"children":3079},[3080,3082,3083,3084],{"id":2321,"depth":296,"text":3081},"Obtaining CachedPermissionData and CachedMetaData",{"id":2443,"depth":296,"text":2444},{"id":2505,"depth":296,"text":2506},{"id":2524,"depth":296,"text":2525},{"id":2545,"depth":290,"text":2546,"children":3086},[3087,3088],{"id":2552,"depth":296,"text":2553},{"id":2645,"depth":296,"text":2646},{"id":2697,"depth":290,"text":2698,"children":3090},[3091,3092,3093],{"id":2727,"depth":296,"text":2728},{"id":2926,"depth":296,"text":2927},{"id":2943,"depth":296,"text":2944},"Examples for the LuckPerms API.","md",{},{"title":99,"description":3094},null,"xgyBhxoEG-ivHtt7yN5hvMViUciZbJMyOBNcT98g-Ek",[3101,3102],{"title":95,"path":96,"stem":97,"children":-1},{"title":103,"path":104,"stem":105,"children":-1},{"editUrl":3104},"https:\u002F\u002Fgithub.com\u002FTurbotailz\u002Fwiki\u002Fedit\u002Fv3-structure\u002Fen\u002Fdevelopers\u002Fapi-usage.md",[3106],{"title":5,"icon":6,"path":7,"stem":8,"children":3107},[3108,3109,3116,3128,3129,3135,3136,3148,3149,3159,3164,3170],{"title":11,"path":7,"stem":12},{"title":14,"icon":15,"path":16,"stem":17,"children":3110,"page":39},[3111,3112,3113,3114,3115],{"title":20,"path":21,"stem":22},{"title":24,"path":25,"stem":26},{"title":28,"path":29,"stem":30},{"title":32,"path":33,"stem":34},{"title":36,"path":37,"stem":38},{"title":41,"icon":42,"path":43,"stem":44,"children":3117},[3118,3119,3120,3121,3122,3123,3124,3125,3126,3127],{"title":47,"path":43,"stem":44},{"title":49,"path":50,"stem":51},{"title":53,"path":54,"stem":55},{"title":57,"path":58,"stem":59},{"title":61,"path":62,"stem":63},{"title":65,"path":66,"stem":67},{"title":69,"path":70,"stem":71},{"title":73,"path":74,"stem":75},{"title":77,"path":78,"stem":79},{"title":81,"path":82,"stem":83},{"title":85,"path":86,"stem":87},{"title":89,"icon":90,"path":91,"stem":92,"children":3130,"page":39},[3131,3132,3133,3134],{"title":95,"path":96,"stem":97},{"title":99,"path":100,"stem":101},{"title":103,"path":104,"stem":105},{"title":107,"path":108,"stem":109},{"title":111,"path":112,"stem":113},{"title":115,"icon":116,"path":117,"stem":118,"children":3137,"page":39},[3138,3139,3140,3141,3142,3143,3147],{"title":121,"path":122,"stem":123},{"title":125,"path":126,"stem":127},{"title":129,"path":130,"stem":131},{"title":133,"path":134,"stem":135},{"title":137,"path":138,"stem":139},{"title":141,"path":142,"stem":143,"children":3144},[3145,3146],{"title":146,"path":142,"stem":143},{"title":148,"path":149,"stem":150},{"title":152,"path":153,"stem":154},{"title":156,"path":157,"stem":158},{"title":160,"icon":161,"path":162,"stem":163,"children":3150,"page":39},[3151,3152,3153,3154,3155,3156,3157,3158],{"title":166,"path":167,"stem":168},{"title":170,"path":171,"stem":172},{"title":174,"path":175,"stem":176},{"title":178,"path":179,"stem":180},{"title":182,"path":183,"stem":184},{"title":186,"path":187,"stem":188},{"title":190,"path":191,"stem":192},{"title":194,"path":195,"stem":196},{"title":198,"icon":199,"path":200,"stem":201,"children":3160},[3161,3162,3163],{"title":198,"path":200,"stem":201},{"title":205,"path":206,"stem":207},{"title":209,"path":210,"stem":211},{"title":213,"icon":214,"path":215,"stem":216,"children":3165,"page":39},[3166,3167,3168,3169],{"title":219,"path":220,"stem":221},{"title":223,"path":224,"stem":225},{"title":227,"path":228,"stem":229},{"title":231,"path":232,"stem":233},{"title":235,"path":236,"stem":237},1789086424723]