mirror of
https://github.com/EssentialsX/Essentials.git
synced 2026-11-05 17:12:42 -05:00
Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8724e26ef | ||
|
|
e70bdb817c | ||
|
|
2dbd63adf8 | ||
|
|
cfb6f12da3 | ||
|
|
49a2f10071 | ||
|
|
43cb76a888 | ||
|
|
4600c72fd9 | ||
|
|
d5dc7e3a64 | ||
|
|
8013feb70c | ||
|
|
83aaf8cb37 | ||
|
|
966bc999e3 | ||
|
|
382de4ff3b | ||
|
|
f6f13fdee8 | ||
|
|
d2a7e0f99d | ||
|
|
64cfb10954 | ||
|
|
ee9f6f944a | ||
|
|
776f7094a8 | ||
|
|
28eb89fb02 | ||
|
|
ff54e64909 | ||
|
|
15ec9a2589 | ||
|
|
38f1b4ddde | ||
|
|
37ce087a71 | ||
|
|
e604c9f2ae | ||
|
|
49cc72ced2 | ||
|
|
5e95c87fdf | ||
|
|
c28c654b1e | ||
|
|
26a54475ad | ||
|
|
b303bf90d6 | ||
|
|
100f575293 | ||
|
|
c5a25bb79c | ||
|
|
5baf2391b3 | ||
|
|
c38c9256e9 | ||
|
|
79d67e98c3 | ||
|
|
5db483b548 | ||
|
|
39bc0fe224 | ||
|
|
1a9cba913b | ||
|
|
24b7285e42 | ||
|
|
5c45ee2169 | ||
|
|
87bde72678 | ||
|
|
97fd5609b4 | ||
|
|
936a2c0c62 | ||
|
|
3d03cf35df | ||
|
|
57a8c596f8 | ||
|
|
b594358545 | ||
|
|
e9f3677b95 | ||
|
|
678bacfee5 | ||
|
|
7f37b5e0f3 | ||
|
|
f39bc91ef5 | ||
|
|
63e7c4d259 | ||
|
|
1038b3a597 | ||
|
|
fb669aa9fd | ||
|
|
add5eb7cbf | ||
|
|
ddaadf2099 | ||
|
|
4db09f2a2e | ||
|
|
00b4a5b3bd | ||
|
|
73aa65733e | ||
|
|
86621cd90e | ||
|
|
236ce8837e | ||
|
|
f5fc4305c9 | ||
|
|
08b7fe3bb8 | ||
|
|
0845ea6f08 | ||
|
|
5a78df5494 | ||
|
|
716a61eb16 | ||
|
|
637ac8f4ee | ||
|
|
218e004108 | ||
|
|
c26909e2b8 | ||
|
|
5d535dcbe4 | ||
|
|
8e3b24fdba | ||
|
|
d7452bfcc7 | ||
|
|
482175e6c2 | ||
|
|
302653a77c | ||
|
|
9d70fdb263 | ||
|
|
297d0e4606 | ||
|
|
9c833336fb | ||
|
|
5abe8b61ef | ||
|
|
b8acd9c3ab | ||
|
|
f080f815fa | ||
|
|
18549a718a |
No files matched your search
@@ -0,0 +1,270 @@
|
||||
---
|
||||
name: mc-update
|
||||
description: Update EssentialsX for a new Minecraft version. Use when the user asks to update to, bump, or support a new Minecraft or Paper version (for example "update to 26.4", "add 26.3 mobs", "bump supported version"), or asks what must be checked for new mobs, items, wood types, armor, enchantments, potions, or Bukkit API changes.
|
||||
---
|
||||
|
||||
# Minecraft update
|
||||
|
||||
This checklist comes from every Minecraft update PR from 1.14 to 26.3, and from the fix PRs that followed them.
|
||||
|
||||
Paths are relative to the repository root. `ESS` means `Essentials/src/main/java/com/earth2me/essentials`.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. Find the old and new `paper-api` jars in `~/.gradle/caches/modules-2/files-2.1/io.papermc.paper/paper-api/`. If the new jar is not there, set `paperVersion` in `build-logic/src/main/kotlin/essentials.base-conventions.gradle.kts` and run `./gradlew :EssentialsX:compileJava` to download it.
|
||||
2. Diff the constants of these classes between the two jars: `org.bukkit.entity.EntityType`, `org.bukkit.Material`, `org.bukkit.enchantments.Enchantment`, `org.bukkit.potion.PotionEffectType`, `org.bukkit.potion.PotionType`, `org.bukkit.TreeType`, and each `*$Variant` class under `org.bukkit.entity`. Use this command for each class:
|
||||
|
||||
```sh
|
||||
unzip -p <jar> org/bukkit/entity/EntityType.class > /tmp/x.class && javap -p /tmp/x.class | grep 'public static final'
|
||||
```
|
||||
|
||||
3. Sort each added, removed, or renamed constant into a section of part 2 or part 3 below.
|
||||
4. Do part 1. Then do each section that applies.
|
||||
5. Apply part 4 to every line you write.
|
||||
6. Run `./gradlew :EssentialsX:compileJava`. Fix errors.
|
||||
7. Do not commit unless the user asks.
|
||||
8. Report per section: what changed, and what you skipped and why.
|
||||
|
||||
---
|
||||
|
||||
## 1. Every update
|
||||
|
||||
### 1.1 Version constants
|
||||
|
||||
1. Open `ESS/utils/VersionUtil.java`.
|
||||
2. Add a constant `vXX_Y_R01 = BukkitVersion.fromString("XX.Y-R0.1-SNAPSHOT")`.
|
||||
- For a patch release, change the string of the existing minor constant. Example: `v26_1_R01` holds `26.1.2`.
|
||||
- For a minor release, add a new constant. Do not rename or delete an old constant. Old constants gate features. Deleting `v1_20_4_R01` caused #6460.
|
||||
3. Add the constant to `supportedVersions`. Keep the `ImmutableSet.of(...)` syntax. `build-logic/src/main/kotlin/VersionDataTask.kt` parses it with a regex.
|
||||
4. Start a server. Confirm `BukkitVersion.fromString(Bukkit.getBukkitVersion())` parses. Spigot 1.14.3 and Paper 26.1 both changed the string format.
|
||||
|
||||
### 1.2 Documentation
|
||||
|
||||
1. Update the supported versions sentence in `README.md`.
|
||||
2. Regenerate `items.json` with ItemDbGenerator.
|
||||
|
||||
---
|
||||
|
||||
## 2. New content
|
||||
|
||||
### 2.1 New mob
|
||||
|
||||
1. Add an entry to `ESS/Mob.java`: `NAME("CamelName", Enemies.X, "ENTITY_TYPE")`.
|
||||
- Use the string constructor. Do not use `EntityType.X`. The constant does not exist on old servers.
|
||||
- Set the plural suffix if it is not `s`. Fish use `""`. Fox uses `es`.
|
||||
- The display name must not contain a space.
|
||||
- Boats and rafts are also `Mob` entries.
|
||||
2. Add `EntityType` constant to `ESS/MobCompat.java` only if `MobData` needs it.
|
||||
3. Add the mob to `protect.prevent.spawn` in `Essentials/src/main/resources/config.yml`.
|
||||
4. Check `entityToDisplayName` in `providers/BaseProviders/.../SpawnerItemProvider.java` if the enum name does not title-case well.
|
||||
|
||||
### 2.2 New mob variant or data
|
||||
|
||||
1. Add entries to `ESS/MobData.java`. Use the string form `"kind:VARIANT"`. Example: `"wolf:PALE"`.
|
||||
2. Add a `case "kind":` to the `setData` switch.
|
||||
3. Add `setKindVariant(Entity, String)` to `MobCompat.java`. Gate it with `VersionUtil.getServerBukkitVersion().isLowerThan(vXX_Y_R01)`.
|
||||
4. Use `RegistryUtil.valueOf(Variant.class, name)` for registry types. Do not use `Enum.valueOf`.
|
||||
5. Do not call `NewClass.Variant.values()` in an enum initializer. Axolotl did this. It threw `NoClassDefFoundError` on 1.16 (#4376).
|
||||
6. Catch `Throwable` around reflective lookups. Cow variant threw NPE on old servers (#6121).
|
||||
7. For a boolean flag, add a `Data` constant and a branch in `setData`. Example: `GOAT_SCREAMING`.
|
||||
|
||||
### 2.3 New rideable or saddle mob
|
||||
|
||||
1. Add `SADDLE_<MOB>` to `MobData.java` with a `Data.<MOB>SADDLE` constant.
|
||||
2. Add `set<Mob>Saddle(Entity, Player)` to `MobCompat.java`. Tame the mob. Set the owner. Set the saddle.
|
||||
3. Check `ESS/craftbukkit/Inventories.java` if the mob adds a player inventory slot. Body and saddle slots (41, 42) broke `/clearinventory` in 1.21.6 (#6218).
|
||||
|
||||
### 2.4 New item or block
|
||||
|
||||
1. Check each `MaterialUtil` set that lists names by hand. See table.
|
||||
2. Check `ESS/utils/LocationUtil.java`:
|
||||
- Add a block that hurts a standing player to `DAMAGING_TYPES`. Magma, campfire, berry bush and wither rose were missed for 2 years (#3537).
|
||||
- `HOLLOW_MATERIALS` comes from `Material.isTransparent()`. Add a passable block that is not transparent. Example: `LIGHT` (#4601). Remove a transparent block that is solid. Examples: `BARRIER`, `DIRT_PATH`, `FARMLAND`.
|
||||
3. Check for a new event that reveals player position. Sculk sensor needed vanish handling in `EssentialsPlayerListener` (#5262).
|
||||
|
||||
| `ESS/utils/MaterialUtil.java` set | Add when |
|
||||
|---|---|
|
||||
| `HELMETS`, `CHESTPLATES`, `LEGGINGS`, `BOOTS` | New armor tier |
|
||||
| `LEATHER_ARMOR` | New dyeable armor |
|
||||
| `BEDS` | New bed. `STRAW_BED` in 26.3. |
|
||||
| `BANNERS` | New banner colour |
|
||||
| `SIGN_POSTS`, `WALL_SIGNS`, `HANGING_SIGNS`, `HANGING_WALL_SIGNS` | New wood type |
|
||||
| `MOB_HEADS` | New head. `PIGLIN_HEAD` in 1.20. |
|
||||
| `POTIONS` | New potion container |
|
||||
| `EDITABLE_BOOKS` | New book type |
|
||||
| `FIREWORKS`, `FIREWORK_CHARGE` | New firework item |
|
||||
|
||||
`EnumUtil.getAllMatching` skips a name that does not exist. Adding a name is safe on old servers.
|
||||
|
||||
### 2.5 Renamed item, block or entity
|
||||
|
||||
1. Grep `ESS` for `Material.OLD_NAME` and `EntityType.OLD_NAME`. Replace with `EnumUtil.getMaterial("NEW", "OLD")` or a `MobCompat` constant. `EntityType.PIG_ZOMBIE` in a static map broke 1.16 (#3412). `EntityType.FIREWORK` broke `/firework` in 1.20.6.
|
||||
2. For a renamed `EntityType`, add `getEntityType("NEW", "OLD")` to `MobCompat.java`. Update `Mob.java`, `MobData.java` and `SpawnMob.java`.
|
||||
|
||||
### 2.6 New wood type
|
||||
|
||||
1. Add `<WOOD>_SIGN`, `<WOOD>_WALL_SIGN`, `<WOOD>_HANGING_SIGN`, `<WOOD>_WALL_HANGING_SIGN` to the four sign sets in `MaterialUtil.java`. Crimson and warped were missed in 1.16 (#3487).
|
||||
2. Add `<WOOD>_BOAT` to `Mob.java`. Boats are separate entity types since 1.21.3.
|
||||
3. Add `<WOOD>` to `MobCompat.BoatVariant` for servers before 1.21.3.
|
||||
4. Add `<WOOD>_BOAT` to `MobData.java`.
|
||||
5. Add `<WOOD>_CHEST_BOAT` to `Mob.java`.
|
||||
|
||||
### 2.7 New tree type
|
||||
|
||||
1. `Commandtree` reads `TreeType.values()`. It needs no code change.
|
||||
2. Add the name to `treeCommandUsage` and `treeCommandUsage1` in `messages.properties`.
|
||||
3. `Commandbigtree` has four hard-coded types. Add a case if the tree has a big variant.
|
||||
4. Bukkit adds `TreeType` late. 26.3-pre-2 has no `POPLAR`.
|
||||
|
||||
### 2.8 New armor tier
|
||||
|
||||
1. Add the four pieces to `HELMETS`, `CHESTPLATES`, `LEGGINGS`, `BOOTS` in `MaterialUtil.java`.
|
||||
2. Add a branch to the armor keyword chain in `ESS/SpawnMob.java`. The chain has `netherite`, `copper`, `diamond`, `gold`, `leather`, `iron`.
|
||||
3. For horse armor, add `<TIER>_HORSE_ARMOR` to `MobData.java`. Example: `NETHERITE_HORSE_ARMOR`.
|
||||
|
||||
### 2.9 New enchantment
|
||||
|
||||
1. Add a `try { // XX.Y }` block to `ESS/Enchantments.java`.
|
||||
2. Look up by name: `Enchantment.getByName("NAME")`. Check for null. Do not use `Enchantment.NAME`.
|
||||
3. Add `ENCHANTMENTS.put("name", e)` and `ALIASENCHANTMENTS.put("alias", e)`.
|
||||
4. Swift sneak (1.19) and lunge (1.21.11) were missed. Contributors added them months later.
|
||||
5. Display and permission names come from `Enchantments.getRealName()`. It uses the key on 1.13+.
|
||||
|
||||
### 2.10 New potion effect or potion type
|
||||
|
||||
1. Add a `try { // XX.Y }` block to `ESS/Potions.java`. Catch `Throwable`.
|
||||
2. Add `POTIONS.put("name", PotionEffectType.NAME)` and `ALIASPOTIONS.put(...)`.
|
||||
3. `ModernPotionMetaProvider` maps `LONG_` and `STRONG_` prefixes. Check a new potion follows that pattern.
|
||||
|
||||
### 2.11 New banner pattern
|
||||
|
||||
No code change. `PatternTypeProvider` reads the registry.
|
||||
|
||||
### 2.12 New colour family
|
||||
|
||||
Check `MaterialUtil.getColorOf` for a colour name that is not a `DyeColor`.
|
||||
|
||||
### 2.13 New spawn egg
|
||||
|
||||
No code change. `FlatSpawnEggProvider` derives the egg from `EntityType.name() + "_SPAWN_EGG"`.
|
||||
|
||||
### 2.14 New command for new content
|
||||
|
||||
1. Gate the command with `VersionUtil`. Return `tl("unsupportedFeature")` on old servers. Examples: `/beezooka`, `/ice`.
|
||||
2. Add the command to `plugin.yml` and `messages.properties`. Check message parameters. `iceOther` shipped without `{0}`.
|
||||
|
||||
---
|
||||
|
||||
## 3. API changes
|
||||
|
||||
### 3.1 Enum becomes a registry
|
||||
|
||||
Enchantment, PotionEffectType, Sound, PatternType, Villager.Profession, Cat.Type, Biome and Boat.Type all changed.
|
||||
|
||||
1. Replace `EnumUtil.valueOf` with `RegistryUtil.valueOf(Class, names...)`. Replace `.values()` with `RegistryUtil.values(Class)`.
|
||||
2. If a static method moved from an enum to an interface, code compiled against the new API throws `IncompatibleClassChangeError` on old servers. `catch (Exception)` does not catch it. `PatternType.getByIdentifier` did this (#6553).
|
||||
3. For such a case, add a provider pair:
|
||||
- Interface in `providers/BaseProviders/src/main/java/net/ess3/provider/`.
|
||||
- `Modern*Provider` in BaseProviders with `@ProviderData(weight = 1)` and a `@ProviderTest`.
|
||||
- `Legacy*Provider` in `providers/1_12Provider` or `providers/1_8Provider`. These compile against the old API.
|
||||
- Register with `providerFactory.registerProvider(Legacy.class, Modern.class)` in `Essentials.java`.
|
||||
4. In a `@ProviderTest`, use `getMethod`, not `getDeclaredMethod`. 1.21 moved spawner methods to a super-interface (80bdc5269).
|
||||
|
||||
### 3.2 Removed API class
|
||||
|
||||
`PotionData`, `Potion`, `BannerMeta.setBaseColor` and `Achievement` were removed.
|
||||
|
||||
1. Move code that uses the removed class to a provider in `1_12Provider` or `1_8Provider`.
|
||||
2. Add a modern provider in BaseProviders.
|
||||
3. Remove the method from `OfflinePlayer`, `FakeWorld` and `FakeServer` if it was an override.
|
||||
|
||||
### 3.3 Changed method signature
|
||||
|
||||
1. Read the Bukkit diff for changed return types on methods EssentialsX calls.
|
||||
2. A changed return type changes the call descriptor. The call throws `NoSuchMethodError` on old servers. `PlayerPickupArrowEvent#getArrow` did this (#3175). `InventoryView` becoming an interface did this (#5851).
|
||||
3. Use a provider or reflection for the changed call.
|
||||
|
||||
### 3.4 New API methods on Player, World, Server
|
||||
|
||||
1. Add stubs to `providers/1_8Provider/.../OfflinePlayer.java` and `providers/1_12Provider/.../FakeWorld.java` if they fail to compile.
|
||||
2. Tests use MockBukkit since 1.21.6. `FakeServer` no longer needs updates.
|
||||
|
||||
### 3.5 New event
|
||||
|
||||
1. Put the handler in a separate `*Listener_XX_Y` class.
|
||||
2. Register it behind a `VersionUtil` check or `Class.forName`.
|
||||
3. Do not put a new event in a listener that loads on old servers. `EntityTransformEvent` failed on 1.13.1 (#2942).
|
||||
|
||||
### 3.6 Spigot NMS obfuscation
|
||||
|
||||
1. Add `VXX_Y_RN` to `providers/NMSReflectionProvider/.../ReflUtil.java` if Spigot bumps the NMS revision.
|
||||
2. Update the obfuscated `MinecraftServer#isRunning` name table in `ReflServerStateProvider.java`. This broke on 1.18, 1.19, 1.19.3, 1.20.6 and 1.21.10.
|
||||
3. Paper uses `PaperServerStateProvider`. Test on Spigot to see this fail.
|
||||
|
||||
### 3.7 Paper internal classes
|
||||
|
||||
Paper config classes move. `ReflOnlineModeProvider` broke on 1.19 when config moved to `io.papermc.paper.configuration.GlobalConfiguration`. Check every reflective Paper access.
|
||||
|
||||
### 3.8 World height
|
||||
|
||||
1. Do not hard-code `0`, `-1`, `128` or `256` as a Y bound.
|
||||
2. Use `WorldInfoProvider.getMinHeight`, `getMaxHeight` and `getLogicalHeight`.
|
||||
3. Grep `LocationUtil`, `RandomTeleport`, `Commandtop`, `Commandsethome`, `Teleport`, `AsyncTeleport` and `SpawnMob`. 1.18 needed three fix PRs (#4657, #4667, #4715).
|
||||
|
||||
### 3.9 Item data components
|
||||
|
||||
1. Check `MetaItemStack.parseStringMeta`. The `[...]` component branch calls `UnsafeValues#modifyItemStack`. Its argument shape changed in 1.20.5 and again in 26.1 (#6570).
|
||||
2. Check `Commandbook`. Writable and written books have separate meta since 1.20.6 (#6064).
|
||||
3. Check `Commandskull`. Skull owner needs a resolved profile since 1.20.4 (#6188).
|
||||
4. Check `MetaItemStack.canSpawn()`.
|
||||
|
||||
### 3.10 Vanilla messages
|
||||
|
||||
Check the vanilla changelog for a new client message that duplicates an EssentialsX message. 1.15 added "Respawn point set". `EssentialsPlayerListener` now gates `bedSet` (#2943).
|
||||
|
||||
### 3.11 Version string format
|
||||
|
||||
1. `BukkitVersion.VERSION_PATTERN` in `VersionUtil.java` must parse the new string.
|
||||
2. Supported suffixes: `-snapshot-N`, `-pre-N`, `-rc-N`, `.build.N-channel`, `.local`, `-R0.1-SNAPSHOT`.
|
||||
3. `compareDevSpecifier` orders `snapshot < pre < rc < release`.
|
||||
4. `isSupportedVersion` compares major, minor and patch. A patch release needs a constant change.
|
||||
5. 26.1 needed four fix PRs for this (#6480, #6483, #6500, #6501).
|
||||
|
||||
### 3.12 Adventure and Paper native paths
|
||||
|
||||
Each Paper API shift produced a gate:
|
||||
- `Essentials.initAdventureFacet` gates the Paper facet at `v1_18_2_R01` (#6562).
|
||||
- `EssentialsSpawnPlayerListener` gates `getRespawnLocation` at `v1_20_4_R01` (#6460).
|
||||
- `EssentialsPlayerListener` gates configuration events at `v1_21_8_R01`.
|
||||
|
||||
Check each gate against the new version.
|
||||
|
||||
---
|
||||
|
||||
## 4. Compatibility rules
|
||||
|
||||
EssentialsX runs on 1.8.8 through the current version. Every update PR must keep that.
|
||||
|
||||
1. Do not reference a new `Material`, `EntityType`, `Enchantment` or `PotionEffectType` constant in a static field or enum initializer. Use `EnumUtil.getMaterial`, `EnumUtil.getEntityType` or a name lookup.
|
||||
2. Catch `Throwable`, not `Exception`, around reflective lookups and provider selection. A swallowed `NoSuchFieldError` selected the wrong provider (7d6195375).
|
||||
3. Use only Guava and Gson methods that exist in 1.8.8. `Ints.constrainToRange` and `JsonArray#add(String)` do not (#4708, 576866540).
|
||||
4. Compile legacy providers in `1_8Provider` or `1_12Provider`. Code compiled against the new API can carry the wrong bytecode.
|
||||
5. Gate with `isLowerThan(<next major>)`, not `isLowerThanOrEqualTo(<patch>)`. Forks use nonstandard revision numbers (43eff69a2).
|
||||
6. Do not delete a `VersionUtil` constant.
|
||||
7. Test on Paper latest, Spigot latest, 1.12.2 and 1.8.8. Most follow-up fixes in this history came from old servers.
|
||||
|
||||
---
|
||||
|
||||
## 5. Test
|
||||
|
||||
1. Run `./gradlew test`.
|
||||
2. Run `./gradlew runServer`.
|
||||
3. Run `/ess version`. Confirm the support status is FULL.
|
||||
4. Run `/spawnmob <newmob>`, `/spawnmob <mob>:<variant>`.
|
||||
5. Run `/give @s <newitem>`, `/give @s minecraft:<newitem>`.
|
||||
6. Run `/enchant <newenchant>`, `/potion <neweffect>`.
|
||||
7. Place an EssentialsX sign on each new sign type.
|
||||
8. Run `/tree <newtree>` if Bukkit has the `TreeType`.
|
||||
9. Run `/kit` with the new armor. Confirm auto-equip.
|
||||
10. Teleport onto each new damaging block. Confirm `/tp` refuses.
|
||||
11. Repeat on Spigot for `ReflServerStateProvider`.
|
||||
@@ -80,7 +80,7 @@ Java version:
|
||||
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
|
||||
environments, add a new line with relevant details.
|
||||
-->
|
||||
- [ ] Most recent Paper version (1.XX.Y, git-Paper-BUILD)
|
||||
- [ ] Most recent Paper version (XX.YY.Z, git-Paper-BUILD)
|
||||
- [ ] CraftBukkit/Spigot/Paper 1.12.2
|
||||
- [ ] CraftBukkit 1.8.8
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ Java version:
|
||||
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
|
||||
environments, add a new line with relevant details.
|
||||
-->
|
||||
- [ ] Most recent Paper version (1.XX.Y, git-Paper-BUILD)
|
||||
- [ ] Most recent Paper version (XX.YY.Z, git-Paper-BUILD)
|
||||
- [ ] CraftBukkit/Spigot/Paper 1.12.2
|
||||
- [ ] CraftBukkit 1.8.8
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 17
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 25
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
@@ -41,11 +41,65 @@ jobs:
|
||||
report_paths: '**/build/test-results/test*/TEST-*.xml'
|
||||
|
||||
- name: Archive plugin jars on GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: EssentialsX plugin jars
|
||||
path: jars/
|
||||
|
||||
- name: Upload EssentialsX jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsX-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXAntiBuild jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXAntiBuild-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXChat jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXChat-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscord jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscord-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscordLink jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscordLink-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXGeoIP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXGeoIP-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXProtect jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXProtect-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXSpawn jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXSpawn-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXXMPP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXXMPP-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Deploy to Maven repo
|
||||
if: ${{ success() && github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
|
||||
env:
|
||||
@@ -67,7 +121,7 @@ jobs:
|
||||
cp -r EssentialsXMPP/build/docs/javadoc/ javadocs/EssentialsXMPP/
|
||||
|
||||
- name: Archive Javadocs
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: javadocs
|
||||
path: javadocs/
|
||||
@@ -85,7 +139,7 @@ jobs:
|
||||
node-version: 22
|
||||
|
||||
- name: Download Javadocs
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: javadocs
|
||||
path: javadocs/
|
||||
|
||||
@@ -27,11 +27,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 17
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 25
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
@@ -48,7 +48,61 @@ jobs:
|
||||
report_paths: '**/build/test-results/test*/TEST-*.xml'
|
||||
|
||||
- name: Archive plugin jars on GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: EssentialsX plugin jars
|
||||
path: jars/
|
||||
|
||||
- name: Upload EssentialsX jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsX-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXAntiBuild jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXAntiBuild-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXChat jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXChat-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscord jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscord-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscordLink jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscordLink-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXGeoIP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXGeoIP-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXProtect jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXProtect-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXSpawn jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXSpawn-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXXMPP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXXMPP-*.jar
|
||||
archive: false
|
||||
@@ -21,6 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Mirror dump to a private Gist
|
||||
id: mirror
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GIST_TOKEN }}
|
||||
@@ -46,13 +47,19 @@ jobs:
|
||||
public: false
|
||||
});
|
||||
|
||||
const link = `${process.env.GIST_LINK_FMT}${gist.id}`;
|
||||
core.setOutput('link', `${process.env.GIST_LINK_FMT}${gist.id}`);
|
||||
core.setOutput('issue_number', issue.number);
|
||||
|
||||
- name: Comment gist link on issue
|
||||
if: steps.mirror.outputs.link != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: `📋 Essentials Dump Backup → ${link}`
|
||||
issue_number: ${{ steps.mirror.outputs.issue_number }},
|
||||
body: `📋 Essentials Dump Backup → ${{ steps.mirror.outputs.link }}`
|
||||
});
|
||||
|
||||
cleanup-gist:
|
||||
|
||||
Generated
+2
-2
@@ -11,8 +11,8 @@
|
||||
</option>
|
||||
<option name="locations">
|
||||
<list>
|
||||
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
|
||||
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
|
||||
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">bundled-sun-checks</ConfigurationLocation>
|
||||
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">bundled-google-checks</ConfigurationLocation>
|
||||
<ConfigurationLocation id="321e5753-4201-4d2c-bf31-6232011122fd" type="PROJECT_RELATIVE" scope="All" description="EssentialsX">
|
||||
$PROJECT_DIR$/.checkstyle/checkstyle.xml
|
||||
<option name="properties">
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly('com.github.milkbowl:VaultAPI:1.7') {
|
||||
compileOnly('net.essentialsx.deps:VaultAPI:1.7') {
|
||||
exclude group: "org.bukkit", module: "bukkit"
|
||||
}
|
||||
compileOnly 'net.luckperms:api:5.3'
|
||||
@@ -25,7 +25,6 @@ dependencies {
|
||||
api(project(path: ':providers:PaperProvider', configuration: 'shadow')){
|
||||
exclude(module: 'paper-api')
|
||||
}
|
||||
api project(':providers:FoliaProvider')
|
||||
api(project(':providers:NMSReflectionProvider')) {
|
||||
exclude(module: 'bukkit')
|
||||
}
|
||||
@@ -74,7 +73,6 @@ shadowJar {
|
||||
include (dependency('net.kyori:adventure-text-minimessage'))
|
||||
include (project(':providers:BaseProviders'))
|
||||
include (project(path: ':providers:PaperProvider', configuration: 'shadow'))
|
||||
include (project(':providers:FoliaProvider'))
|
||||
include (project(':providers:NMSReflectionProvider'))
|
||||
include (project(':providers:1_8Provider'))
|
||||
include (project(':providers:1_12Provider'))
|
||||
|
||||
@@ -116,6 +116,8 @@ public class AlternativeCommandsHandler {
|
||||
}
|
||||
|
||||
// return the first command that is not an alias
|
||||
Command firstAlias = null;
|
||||
|
||||
final Iterator<WeakReference<Command>> iterator = commands.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final Command cmd = iterator.next().get();
|
||||
@@ -127,10 +129,14 @@ public class AlternativeCommandsHandler {
|
||||
if (cmd.getName().equalsIgnoreCase(label)) {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
if (firstAlias == null) {
|
||||
firstAlias = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
// return the first alias
|
||||
return commands.get(0).get();
|
||||
// return the first alias, or null if every reference in this bucket has been collected
|
||||
return firstAlias;
|
||||
}
|
||||
|
||||
public void executed(final String label, final Command pc) {
|
||||
|
||||
@@ -139,6 +139,19 @@ public class AsyncTeleport implements IAsyncTeleport {
|
||||
paperFuture.exceptionally(future::completeExceptionally);
|
||||
}
|
||||
|
||||
private void runOnMain(final Runnable runnable) throws ExecutionException, InterruptedException {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
|
||||
Bukkit.getScheduler().runTask(ess, () -> {
|
||||
runnable.run();
|
||||
taskLock.complete(new Object());
|
||||
});
|
||||
taskLock.get();
|
||||
}
|
||||
|
||||
protected void nowAsync(final IUser teleportee, final ITarget target, final TeleportCause cause, final CompletableFuture<Boolean> future) {
|
||||
cancel(false);
|
||||
|
||||
@@ -156,8 +169,8 @@ public class AsyncTeleport implements IAsyncTeleport {
|
||||
}
|
||||
|
||||
try {
|
||||
ess.ensureEntity(teleportee.getBase(), () -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context.
|
||||
} catch (final RuntimeException e) {
|
||||
runOnMain(() -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context.
|
||||
} catch (final ExecutionException | InterruptedException e) {
|
||||
future.completeExceptionally(e);
|
||||
return;
|
||||
}
|
||||
@@ -177,7 +190,8 @@ public class AsyncTeleport implements IAsyncTeleport {
|
||||
if (LocationUtil.isBlockUnsafeForUser(ess, teleportee, chunk.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
|
||||
if (ess.getSettings().isTeleportSafetyEnabled()) {
|
||||
if (ess.getSettings().isForceDisableTeleportSafety()) {
|
||||
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
|
||||
//The chunk we're teleporting to is 100% going to be loaded here, no need to teleport async.
|
||||
teleportee.getBase().teleport(loc, cause);
|
||||
} else {
|
||||
try {
|
||||
//There's a chance the safer location is outside the loaded chunk so still teleport async here.
|
||||
@@ -193,7 +207,8 @@ public class AsyncTeleport implements IAsyncTeleport {
|
||||
}
|
||||
} else {
|
||||
if (ess.getSettings().isForceDisableTeleportSafety()) {
|
||||
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
|
||||
//The chunk we're teleporting to is 100% going to be loaded here, no need to teleport async.
|
||||
teleportee.getBase().teleport(loc, cause);
|
||||
} else {
|
||||
if (ess.getSettings().isTeleportToCenterLocation()) {
|
||||
loc = LocationUtil.getRoundedDestination(loc);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.IUser;
|
||||
import net.ess3.provider.SchedulingProvider;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.IUser;
|
||||
import net.essentialsx.api.v2.events.TeleportWarmupCancelledEvent;
|
||||
import net.essentialsx.api.v2.events.TeleportWarmupCancelledEvent.CancelReason;
|
||||
|
||||
@@ -32,7 +31,7 @@ public class AsyncTimedTeleport implements Runnable {
|
||||
private final boolean timer_canMove;
|
||||
private final Trade timer_chargeFor;
|
||||
private final TeleportCause timer_cause;
|
||||
private SchedulingProvider.EssentialsTask timer_task;
|
||||
private int timer_task;
|
||||
private double timer_health;
|
||||
|
||||
AsyncTimedTeleport(final IUser user, final IEssentials ess, final AsyncTeleport teleport, final long delay, final IUser teleportUser, final ITarget target, final Trade chargeFor, final TeleportCause cause, final boolean respawn) {
|
||||
@@ -56,7 +55,7 @@ public class AsyncTimedTeleport implements Runnable {
|
||||
this.timer_respawn = respawn;
|
||||
this.timer_canMove = user.isAuthorized("essentials.teleport.timer.move");
|
||||
|
||||
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20);
|
||||
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20).getTaskId();
|
||||
|
||||
if (future != null) {
|
||||
this.parentFuture = future;
|
||||
@@ -143,16 +142,16 @@ public class AsyncTimedTeleport implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleEntityDelayedTask(teleportOwner.getBase(), new DelayedTeleportTask());
|
||||
ess.scheduleSyncDelayedTask(new DelayedTeleportTask());
|
||||
}
|
||||
|
||||
//If we need to cancelTimer a pending teleportPlayer call this method
|
||||
void cancelTimer(final boolean notifyUser) {
|
||||
if (timer_task == null) {
|
||||
if (timer_task == -1) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
timer_task.cancel();
|
||||
ess.getServer().getScheduler().cancelTask(timer_task);
|
||||
|
||||
final IUser teleportUser = ess.getUser(this.timer_teleportee);
|
||||
if (teleportUser != null && teleportUser.getBase() != null) {
|
||||
@@ -168,7 +167,7 @@ public class AsyncTimedTeleport implements Runnable {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
timer_task = null;
|
||||
timer_task = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.provider.SchedulingProvider;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@@ -19,7 +18,7 @@ public class Backup implements Runnable {
|
||||
private transient final IEssentials ess;
|
||||
private final AtomicBoolean pendingShutdown = new AtomicBoolean(false);
|
||||
private transient boolean running = false;
|
||||
private transient SchedulingProvider.EssentialsTask task = null;
|
||||
private transient int taskId = -1;
|
||||
private transient boolean active = false;
|
||||
private transient CompletableFuture<Object> taskLock = null;
|
||||
|
||||
@@ -37,10 +36,10 @@ public class Backup implements Runnable {
|
||||
|
||||
public synchronized void stopTask() {
|
||||
running = false;
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
if (taskId != -1) {
|
||||
server.getScheduler().cancelTask(taskId);
|
||||
}
|
||||
task = null;
|
||||
taskId = -1;
|
||||
}
|
||||
|
||||
private synchronized void startTask() {
|
||||
@@ -49,7 +48,7 @@ public class Backup implements Runnable {
|
||||
if (interval < 1200) {
|
||||
return;
|
||||
}
|
||||
task = ess.scheduleGlobalRepeatingTask(this, interval, interval);
|
||||
taskId = ess.scheduleSyncRepeatingTask(this, interval, interval);
|
||||
running = true;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +123,7 @@ public class Backup implements Runnable {
|
||||
}
|
||||
|
||||
if (!pendingShutdown.get()) {
|
||||
ess.scheduleGlobalDelayedTask(new BackupEnableSaveTask());
|
||||
ess.scheduleSyncDelayedTask(new BackupEnableSaveTask());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -13,7 +13,11 @@ import static com.earth2me.essentials.I18n.tlLiteral;
|
||||
|
||||
public final class Console implements IMessageRecipient {
|
||||
public static final String NAME = "Console";
|
||||
public static final String DISPLAY_NAME = tlLiteral("consoleName");
|
||||
|
||||
public static String displayName() {
|
||||
return tlLiteral("consoleName");
|
||||
}
|
||||
|
||||
private static Console instance; // Set in essentials
|
||||
|
||||
private final IEssentials ess;
|
||||
@@ -56,7 +60,7 @@ public final class Console implements IMessageRecipient {
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Console.DISPLAY_NAME;
|
||||
return displayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,13 +17,13 @@ public final class Enchantments {
|
||||
|
||||
static {
|
||||
final Enchantment SHARPNESS = RegistryUtil.valueOf(Enchantment.class, "DAMAGE_ALL", "SHARPNESS");
|
||||
|
||||
|
||||
ENCHANTMENTS.put("alldamage", SHARPNESS);
|
||||
ALIASENCHANTMENTS.put("alldmg", SHARPNESS);
|
||||
ENCHANTMENTS.put("sharpness", SHARPNESS);
|
||||
ALIASENCHANTMENTS.put("sharp", SHARPNESS);
|
||||
ALIASENCHANTMENTS.put("dal", SHARPNESS);
|
||||
|
||||
|
||||
final Enchantment BANE_OF_ARTHROPODS = RegistryUtil.valueOf(Enchantment.class, "DAMAGE_ARTHROPODS", "BANE_OF_ARTHROPODS");
|
||||
|
||||
ENCHANTMENTS.put("ardmg", BANE_OF_ARTHROPODS);
|
||||
@@ -31,13 +31,13 @@ public final class Enchantments {
|
||||
ALIASENCHANTMENTS.put("baneofarthropod", BANE_OF_ARTHROPODS);
|
||||
ALIASENCHANTMENTS.put("arthropod", BANE_OF_ARTHROPODS);
|
||||
ALIASENCHANTMENTS.put("dar", BANE_OF_ARTHROPODS);
|
||||
|
||||
|
||||
final Enchantment SMITE = RegistryUtil.valueOf(Enchantment.class, "DAMAGE_UNDEAD", "SMITE");
|
||||
|
||||
ENCHANTMENTS.put("undeaddamage", SMITE);
|
||||
ENCHANTMENTS.put("smite", SMITE);
|
||||
ALIASENCHANTMENTS.put("du", SMITE);
|
||||
|
||||
|
||||
final Enchantment EFFICIENCY = RegistryUtil.valueOf(Enchantment.class, "DIG_SPEED", "EFFICIENCY");
|
||||
|
||||
ENCHANTMENTS.put("digspeed", EFFICIENCY);
|
||||
@@ -46,7 +46,7 @@ public final class Enchantments {
|
||||
ALIASENCHANTMENTS.put("cutspeed", EFFICIENCY);
|
||||
ALIASENCHANTMENTS.put("ds", EFFICIENCY);
|
||||
ALIASENCHANTMENTS.put("eff", EFFICIENCY);
|
||||
|
||||
|
||||
final Enchantment UNBREAKING = RegistryUtil.valueOf(Enchantment.class, "DURABILITY", "UNBREAKING");
|
||||
|
||||
ENCHANTMENTS.put("durability", UNBREAKING);
|
||||
@@ -70,21 +70,21 @@ public final class Enchantments {
|
||||
ALIASENCHANTMENTS.put("kback", Enchantment.KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("kb", Enchantment.KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("k", Enchantment.KNOCKBACK);
|
||||
|
||||
|
||||
final Enchantment FORTUNE = RegistryUtil.valueOf(Enchantment.class, "LOOT_BONUS_BLOCKS", "FORTUNE");
|
||||
|
||||
ALIASENCHANTMENTS.put("blockslootbonus", FORTUNE);
|
||||
ENCHANTMENTS.put("fortune", FORTUNE);
|
||||
ALIASENCHANTMENTS.put("fort", FORTUNE);
|
||||
ALIASENCHANTMENTS.put("lbb", FORTUNE);
|
||||
|
||||
|
||||
final Enchantment LOOTING = RegistryUtil.valueOf(Enchantment.class, "LOOT_BONUS_MOBS", "LOOTING");
|
||||
|
||||
ALIASENCHANTMENTS.put("mobslootbonus", LOOTING);
|
||||
ENCHANTMENTS.put("mobloot", LOOTING);
|
||||
ENCHANTMENTS.put("looting", LOOTING);
|
||||
ALIASENCHANTMENTS.put("lbm", LOOTING);
|
||||
|
||||
|
||||
final Enchantment RESPIRATION = RegistryUtil.valueOf(Enchantment.class, "OXYGEN", "RESPIRATION");
|
||||
|
||||
ALIASENCHANTMENTS.put("oxygen", RESPIRATION);
|
||||
@@ -92,14 +92,14 @@ public final class Enchantments {
|
||||
ALIASENCHANTMENTS.put("breathing", RESPIRATION);
|
||||
ENCHANTMENTS.put("breath", RESPIRATION);
|
||||
ALIASENCHANTMENTS.put("o", RESPIRATION);
|
||||
|
||||
|
||||
final Enchantment PROTECTION = RegistryUtil.valueOf(Enchantment.class, "PROTECTION_ENVIRONMENTAL", "PROTECTION");
|
||||
|
||||
ENCHANTMENTS.put("protection", PROTECTION);
|
||||
ALIASENCHANTMENTS.put("prot", PROTECTION);
|
||||
ENCHANTMENTS.put("protect", PROTECTION);
|
||||
ALIASENCHANTMENTS.put("p", PROTECTION);
|
||||
|
||||
|
||||
final Enchantment BLAST_PROTECTION = RegistryUtil.valueOf(Enchantment.class, "PROTECTION_EXPLOSIONS", "BLAST_PROTECTION");
|
||||
|
||||
ALIASENCHANTMENTS.put("explosionsprotection", BLAST_PROTECTION);
|
||||
@@ -110,7 +110,7 @@ public final class Enchantments {
|
||||
ALIASENCHANTMENTS.put("bprotect", BLAST_PROTECTION);
|
||||
ENCHANTMENTS.put("blastprotect", BLAST_PROTECTION);
|
||||
ALIASENCHANTMENTS.put("pe", BLAST_PROTECTION);
|
||||
|
||||
|
||||
final Enchantment FEATHER_FALLING = RegistryUtil.valueOf(Enchantment.class, "PROTECTION_FALL", "FEATHER_FALLING");
|
||||
|
||||
ALIASENCHANTMENTS.put("fallprotection", FEATHER_FALLING);
|
||||
@@ -118,7 +118,7 @@ public final class Enchantments {
|
||||
ENCHANTMENTS.put("featherfall", FEATHER_FALLING);
|
||||
ALIASENCHANTMENTS.put("featherfalling", FEATHER_FALLING);
|
||||
ALIASENCHANTMENTS.put("pfa", FEATHER_FALLING);
|
||||
|
||||
|
||||
final Enchantment FIRE_PROTECTION = RegistryUtil.valueOf(Enchantment.class, "PROTECTION_FIRE", "FIRE_PROTECTION");
|
||||
|
||||
ALIASENCHANTMENTS.put("fireprotection", FIRE_PROTECTION);
|
||||
@@ -128,7 +128,7 @@ public final class Enchantments {
|
||||
ENCHANTMENTS.put("fireprot", FIRE_PROTECTION);
|
||||
ALIASENCHANTMENTS.put("flameprot", FIRE_PROTECTION);
|
||||
ALIASENCHANTMENTS.put("pf", FIRE_PROTECTION);
|
||||
|
||||
|
||||
final Enchantment PROJECTILE_PROTECTION = RegistryUtil.valueOf(Enchantment.class, "PROTECTION_PROJECTILE", "PROJECTILE_PROTECTION");
|
||||
|
||||
ENCHANTMENTS.put("projectileprotection", PROJECTILE_PROTECTION);
|
||||
@@ -138,28 +138,28 @@ public final class Enchantments {
|
||||
ENCHANTMENTS.put("silktouch", Enchantment.SILK_TOUCH);
|
||||
ALIASENCHANTMENTS.put("softtouch", Enchantment.SILK_TOUCH);
|
||||
ALIASENCHANTMENTS.put("st", Enchantment.SILK_TOUCH);
|
||||
|
||||
|
||||
final Enchantment AQUA_AFFINITY = RegistryUtil.valueOf(Enchantment.class, "WATER_WORKER", "AQUA_AFFINITY");
|
||||
|
||||
ENCHANTMENTS.put("waterworker", AQUA_AFFINITY);
|
||||
ENCHANTMENTS.put("aquaaffinity", AQUA_AFFINITY);
|
||||
ALIASENCHANTMENTS.put("watermine", AQUA_AFFINITY);
|
||||
ALIASENCHANTMENTS.put("ww", AQUA_AFFINITY);
|
||||
|
||||
|
||||
final Enchantment FLAME = RegistryUtil.valueOf(Enchantment.class, "ARROW_FIRE", "FLAME");
|
||||
|
||||
ALIASENCHANTMENTS.put("firearrow", FLAME);
|
||||
ENCHANTMENTS.put("flame", FLAME);
|
||||
ENCHANTMENTS.put("flamearrow", FLAME);
|
||||
ALIASENCHANTMENTS.put("af", FLAME);
|
||||
|
||||
|
||||
final Enchantment POWER = RegistryUtil.valueOf(Enchantment.class, "ARROW_DAMAGE", "POWER");
|
||||
|
||||
ENCHANTMENTS.put("arrowdamage", POWER);
|
||||
ENCHANTMENTS.put("power", POWER);
|
||||
ALIASENCHANTMENTS.put("arrowpower", POWER);
|
||||
ALIASENCHANTMENTS.put("ad", POWER);
|
||||
|
||||
|
||||
final Enchantment PUNCH = RegistryUtil.valueOf(Enchantment.class, "ARROW_KNOCKBACK", "PUNCH");
|
||||
|
||||
ENCHANTMENTS.put("arrowknockback", PUNCH);
|
||||
@@ -167,7 +167,7 @@ public final class Enchantments {
|
||||
ENCHANTMENTS.put("punch", PUNCH);
|
||||
ALIASENCHANTMENTS.put("arrowpunch", PUNCH);
|
||||
ALIASENCHANTMENTS.put("ak", PUNCH);
|
||||
|
||||
|
||||
final Enchantment INFINITY = RegistryUtil.valueOf(Enchantment.class, "ARROW_INFINITE", "INFINITY");
|
||||
|
||||
ALIASENCHANTMENTS.put("infinitearrows", INFINITY);
|
||||
@@ -177,7 +177,7 @@ public final class Enchantments {
|
||||
ALIASENCHANTMENTS.put("unlimited", INFINITY);
|
||||
ALIASENCHANTMENTS.put("unlimitedarrows", INFINITY);
|
||||
ALIASENCHANTMENTS.put("ai", INFINITY);
|
||||
|
||||
|
||||
final Enchantment LUCK_OF_THE_SEA = RegistryUtil.valueOf(Enchantment.class, "LUCK", "LUCK_OF_THE_SEA");
|
||||
|
||||
ENCHANTMENTS.put("luck", LUCK_OF_THE_SEA);
|
||||
@@ -313,7 +313,7 @@ public final class Enchantments {
|
||||
ENCHANTMENTS.put("density", density);
|
||||
}
|
||||
final Enchantment windBurst = Enchantment.getByName("WIND_BURST");
|
||||
if (breach != null) {
|
||||
if (windBurst != null) {
|
||||
ENCHANTMENTS.put("windburst", windBurst);
|
||||
ALIASENCHANTMENTS.put("wind", windBurst);
|
||||
ALIASENCHANTMENTS.put("burst", windBurst);
|
||||
@@ -321,6 +321,14 @@ public final class Enchantments {
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
try { // 1.21.11
|
||||
final Enchantment lunge = Enchantment.getByName("LUNGE");
|
||||
if (lunge != null) {
|
||||
ENCHANTMENTS.put("lunge", lunge);
|
||||
}
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
final Class<?> namespacedKeyClass = Class.forName("org.bukkit.NamespacedKey");
|
||||
final Class<?> enchantmentClass = Class.forName("org.bukkit.enchantments.Enchantment");
|
||||
|
||||
@@ -28,7 +28,6 @@ import com.earth2me.essentials.commands.NoChargeException;
|
||||
import com.earth2me.essentials.commands.NotEnoughArgumentsException;
|
||||
import com.earth2me.essentials.commands.PlayerNotFoundException;
|
||||
import com.earth2me.essentials.commands.QuietAbortException;
|
||||
import com.earth2me.essentials.config.EssentialsConfiguration;
|
||||
import com.earth2me.essentials.economy.EconomyLayers;
|
||||
import com.earth2me.essentials.economy.vault.VaultEconomyProvider;
|
||||
import com.earth2me.essentials.items.AbstractItemDb;
|
||||
@@ -47,9 +46,12 @@ import com.earth2me.essentials.textreader.SimpleTextInput;
|
||||
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
||||
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import com.earth2me.essentials.utils.ModernPaperEnvironment;
|
||||
import com.earth2me.essentials.utils.PasteUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.ess3.api.Economy;
|
||||
import com.earth2me.essentials.config.EssentialsConfiguration;
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.IItemDb;
|
||||
import net.ess3.api.IJails;
|
||||
@@ -68,21 +70,19 @@ import net.ess3.provider.InventoryViewProvider;
|
||||
import net.ess3.provider.KnownCommandsProvider;
|
||||
import net.ess3.provider.PlayerLocaleProvider;
|
||||
import net.ess3.provider.ProviderListener;
|
||||
import net.ess3.provider.SchedulingProvider;
|
||||
import net.ess3.provider.ServerStateProvider;
|
||||
import net.ess3.provider.providers.BaseBannerDataProvider;
|
||||
import net.ess3.provider.providers.BaseInventoryViewProvider;
|
||||
import net.ess3.provider.providers.BlockMetaSpawnerItemProvider;
|
||||
import net.ess3.provider.providers.BukkitMaterialTagProvider;
|
||||
import net.ess3.provider.providers.BukkitSchedulingProvider;
|
||||
import net.ess3.provider.providers.BukkitSpawnerBlockProvider;
|
||||
import net.ess3.provider.providers.BukkitTileEntityProvider;
|
||||
import net.ess3.provider.providers.BukkitWorldTileEntityCountProvider;
|
||||
import net.ess3.provider.providers.FixedHeightWorldInfoProvider;
|
||||
import net.ess3.provider.providers.FlatSpawnEggProvider;
|
||||
import net.ess3.provider.providers.FoliaSchedulingProvider;
|
||||
import net.ess3.provider.providers.LegacyBannerDataProvider;
|
||||
import net.ess3.provider.providers.LegacyBiomeNameProvider;
|
||||
import net.ess3.provider.providers.LegacyPatternTypeProvider;
|
||||
import net.ess3.provider.providers.ModernPatternTypeProvider;
|
||||
import net.ess3.provider.providers.LegacyDamageEventProvider;
|
||||
import net.ess3.provider.providers.LegacyInventoryViewProvider;
|
||||
import net.ess3.provider.providers.LegacyItemUnbreakableProvider;
|
||||
@@ -106,12 +106,10 @@ import net.ess3.provider.providers.PaperSerializationProvider;
|
||||
import net.ess3.provider.providers.PaperServerStateProvider;
|
||||
import net.ess3.provider.providers.PaperTickCountProvider;
|
||||
import net.ess3.provider.providers.PaperTileEntityProvider;
|
||||
import net.ess3.provider.providers.PaperWorldTileEntityCountProvider;
|
||||
import net.ess3.provider.providers.PrehistoricPotionMetaProvider;
|
||||
import net.essentialsx.api.v2.services.BalanceTop;
|
||||
import net.essentialsx.api.v2.services.mail.MailService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
@@ -121,7 +119,6 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.PluginIdentifiableCommand;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -136,6 +133,8 @@ import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -148,11 +147,11 @@ import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tlLiteral;
|
||||
import static com.earth2me.essentials.I18n.tlLocale;
|
||||
@@ -263,6 +262,10 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverSnapshot")));
|
||||
}
|
||||
|
||||
if (PaperLib.isPaper() && !PaperLib.isVersion(13) && VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_13_0_R01)) {
|
||||
PaperLib.setCustomEnvironment(new ModernPaperEnvironment());
|
||||
}
|
||||
|
||||
final PluginManager pm = getServer().getPluginManager();
|
||||
for (final Plugin plugin : pm.getPlugins()) {
|
||||
if (plugin.getDescription().getName().startsWith("Essentials") && !plugin.getDescription().getVersion().equals(this.getDescription().getVersion()) && !plugin.getDescription().getName().equals("EssentialsAntiCheat")) {
|
||||
@@ -329,6 +332,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
confList.add(jails);
|
||||
execTimer.mark("Init(Jails)");
|
||||
|
||||
EconomyLayers.onEnable(this);
|
||||
execTimer.mark("Init(EconomyLayers)");
|
||||
|
||||
// Spawner item provider only uses one, but it's here for legacy...
|
||||
providerFactory.registerProvider(BlockMetaSpawnerItemProvider.class);
|
||||
|
||||
@@ -344,6 +350,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
//Banner Meta Provider
|
||||
providerFactory.registerProvider(LegacyBannerDataProvider.class, BaseBannerDataProvider.class);
|
||||
|
||||
// Pattern Type Provider
|
||||
providerFactory.registerProvider(LegacyPatternTypeProvider.class, ModernPatternTypeProvider.class);
|
||||
|
||||
// Server State Provider
|
||||
providerFactory.registerProvider(ReflServerStateProvider.class, PaperServerStateProvider.class);
|
||||
|
||||
@@ -398,16 +407,10 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
// Tile Entity Provider
|
||||
providerFactory.registerProvider(BukkitTileEntityProvider.class, PaperTileEntityProvider.class);
|
||||
|
||||
// World Tile Entity Count Provider
|
||||
providerFactory.registerProvider(BukkitWorldTileEntityCountProvider.class, PaperWorldTileEntityCountProvider.class);
|
||||
|
||||
// Tick Count Provider
|
||||
providerFactory.registerProvider(PaperTickCountProvider.class);
|
||||
|
||||
// Scheduling provider
|
||||
providerFactory.registerProvider(FoliaSchedulingProvider.class, BukkitSchedulingProvider.class);
|
||||
|
||||
if (!TESTING || true) { // FIXME: The scheduling provider is necessary for tests to pass without adding checks for TESTING everywhere
|
||||
if (!TESTING) {
|
||||
providerFactory.finalizeRegistration();
|
||||
}
|
||||
|
||||
@@ -428,12 +431,6 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
}
|
||||
|
||||
execTimer.mark("Init(Providers)");
|
||||
|
||||
EconomyLayers.onEnable(this);
|
||||
execTimer.mark("Init(EconomyLayers)");
|
||||
|
||||
registerListeners(getServer().getPluginManager());
|
||||
initAdventureFacet();
|
||||
reload();
|
||||
|
||||
// The item spawn blacklist is loaded with all other settings, before the item
|
||||
@@ -444,7 +441,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
alternativeCommandsHandler = new AlternativeCommandsHandler(this);
|
||||
|
||||
timer = new EssentialsTimer(this);
|
||||
scheduleGlobalRepeatingTask(timer, 1000, 50);
|
||||
scheduleSyncRepeatingTask(timer, 1000, 50);
|
||||
|
||||
Economy.setEss(this);
|
||||
execTimer.mark("RegHandler");
|
||||
@@ -497,6 +494,8 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
}
|
||||
|
||||
private void registerListeners(final PluginManager pm) {
|
||||
HandlerList.unregisterAll(this);
|
||||
|
||||
if (getSettings().isDebug()) {
|
||||
LOGGER.log(Level.INFO, "Registering Listeners");
|
||||
}
|
||||
@@ -594,6 +593,8 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
getUsers().shutdown();
|
||||
|
||||
EssentialsConfiguration.shutdownExecutor();
|
||||
PasteUtil.shutdownExecutor();
|
||||
getServer().getScheduler().cancelTasks(this);
|
||||
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
@@ -616,6 +617,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
}
|
||||
}
|
||||
|
||||
final PluginManager pm = getServer().getPluginManager();
|
||||
registerListeners(pm);
|
||||
|
||||
initAdventureFacet();
|
||||
}
|
||||
|
||||
@@ -624,7 +628,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
adventureFacet.close();
|
||||
}
|
||||
|
||||
if (VersionUtil.isPaper() && VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_5_R01)) {
|
||||
// Paper only started bundling a modern enough native Adventure (with the MiniMessage TagResolver API)
|
||||
// in 1.18.2, so older versions must continue using our bundled Adventure via the Spigot facet.
|
||||
if (VersionUtil.isPaper() && VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_18_2_R01)) {
|
||||
adventureFacet = new PaperAdventureFacet(getSettings() != null ? getSettings().getPrimaryColor() : null, getSettings() != null ? getSettings().getSecondaryColor() : null);
|
||||
} else {
|
||||
adventureFacet = new SpigotAdventureFacet(this);
|
||||
@@ -848,7 +854,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
sender.sendTl("commandHelpLine2", description);
|
||||
sender.sendTl("commandHelpLine3");
|
||||
for (Map.Entry<String, String> usage : cmd.getUsageStrings().entrySet()) {
|
||||
sender.sendTl("commandHelpLineUsage", AdventureUtil.parsed(usage.getKey().replace("<command>", commandLabel)), AdventureUtil.parsed(usage.getValue()));
|
||||
sender.sendTl("commandHelpLineUsage", AdventureUtil.parsed(usage.getKey().replace("<command>", commandLabel)), AdventureUtil.parsed(sender.tl(usage.getValue())));
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(command.getDescription());
|
||||
@@ -909,6 +915,16 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitScheduler getScheduler() {
|
||||
return this.getServer().getScheduler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Player> getJailedPlayers() {
|
||||
return getUsers().getAllUserUUIDs().stream().map(this::getUser).filter(Objects::nonNull).filter(User::isJailed).map(User::getBase).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJails getJails() {
|
||||
return jails;
|
||||
@@ -1232,135 +1248,33 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleInitTask(Runnable runnable) {
|
||||
provider(SchedulingProvider.class).registerInitTask(runnable);
|
||||
public BukkitTask runTaskAsynchronously(final Runnable run) {
|
||||
return this.getScheduler().runTaskAsynchronously(this, run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runTaskAsynchronously(final Runnable run) {
|
||||
provider(SchedulingProvider.class).runAsyncTask(run);
|
||||
public BukkitTask runTaskLaterAsynchronously(final Runnable run, final long delay) {
|
||||
return this.getScheduler().runTaskLaterAsynchronously(this, run, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runTaskLaterAsynchronously(final Runnable run, final long delay) {
|
||||
provider(SchedulingProvider.class).runAsyncTaskLater(run, delay);
|
||||
public BukkitTask runTaskTimerAsynchronously(final Runnable run, final long delay, final long period) {
|
||||
return this.getScheduler().runTaskTimerAsynchronously(this, run, delay, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchedulingProvider.EssentialsTask runTaskTimerAsynchronously(final Runnable run, final long delay, final long period) {
|
||||
return provider(SchedulingProvider.class).runAsyncTaskRepeating(run, delay, period);
|
||||
public int scheduleSyncDelayedTask(final Runnable run) {
|
||||
return this.getScheduler().scheduleSyncDelayedTask(this, run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleEntityDelayedTask(Entity entity, Runnable run) {
|
||||
provider(SchedulingProvider.class).runEntityTask(entity, run);
|
||||
public int scheduleSyncDelayedTask(final Runnable run, final long delay) {
|
||||
return this.getScheduler().scheduleSyncDelayedTask(this, run, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchedulingProvider.EssentialsTask scheduleEntityDelayedTask(Entity entity, Runnable run, long delay) {
|
||||
return provider(SchedulingProvider.class).runEntityTask(entity, run, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchedulingProvider.EssentialsTask scheduleEntityRepeatingTask(Entity entity, Runnable run, long delay, long period) {
|
||||
return provider(SchedulingProvider.class).runEntityTaskRepeating(entity, run, delay, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleLocationDelayedTask(Location location, Runnable run) {
|
||||
provider(SchedulingProvider.class).runLocationalTask(location, run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleLocationDelayedTask(Location location, Runnable run, long delay) {
|
||||
provider(SchedulingProvider.class).runLocationalTask(location, run, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchedulingProvider.EssentialsTask scheduleLocationRepeatingTask(Location location, Runnable run, long delay, long period) {
|
||||
return provider(SchedulingProvider.class).runLocationalTaskRepeating(location, run, delay, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleGlobalDelayedTask(Runnable run, long delay) {
|
||||
provider(SchedulingProvider.class).runGlobalLocationalTask(run, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchedulingProvider.EssentialsTask scheduleGlobalRepeatingTask(Runnable run, long delay, long period) {
|
||||
return provider(SchedulingProvider.class).runGlobalLocationalTaskRepeating(run, delay, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntityThread(Entity entity) {
|
||||
return provider(SchedulingProvider.class).isEntityThread(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegionThread(Location location) {
|
||||
return provider(SchedulingProvider.class).isRegionThread(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobalThread() {
|
||||
return provider(SchedulingProvider.class).isGlobalThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ensureEntity(Entity entity, Runnable runnable) {
|
||||
if (isEntityThread(entity)) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
|
||||
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
|
||||
scheduleEntityDelayedTask(entity, () -> {
|
||||
runnable.run();
|
||||
taskLock.complete(new Object());
|
||||
});
|
||||
try {
|
||||
taskLock.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ensureRegion(Location location, Runnable runnable) {
|
||||
if (isRegionThread(location)) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
|
||||
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
|
||||
scheduleLocationDelayedTask(location, () -> {
|
||||
runnable.run();
|
||||
taskLock.complete(new Object());
|
||||
});
|
||||
try {
|
||||
taskLock.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ensureGlobal(Runnable runnable) {
|
||||
if (isGlobalThread()) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
|
||||
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
|
||||
scheduleGlobalDelayedTask(() -> {
|
||||
runnable.run();
|
||||
taskLock.complete(new Object());
|
||||
});
|
||||
try {
|
||||
taskLock.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public int scheduleSyncRepeatingTask(final Runnable run, final long delay, final long period) {
|
||||
return this.getScheduler().scheduleSyncRepeatingTask(this, run, delay, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,7 +44,7 @@ public class EssentialsBlockListener implements Listener {
|
||||
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.hasUnlimited(is) && user.getBase().getGameMode() == GameMode.SURVIVAL) {
|
||||
ess.scheduleEntityDelayedTask(user.getBase(), () -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
if (is != null && is.getType() != null && !MaterialUtil.isAir(is.getType())) {
|
||||
final ItemStack cloneIs = is.clone();
|
||||
cloneIs.setAmount(1);
|
||||
|
||||
@@ -110,7 +110,7 @@ public class EssentialsEntityListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleEntityDelayedTask(attacker.getBase(), new PowerToolInteractTask());
|
||||
ess.scheduleSyncDelayedTask(new PowerToolInteractTask());
|
||||
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
|
||||
@@ -22,7 +22,6 @@ import io.papermc.paper.event.player.PlayerServerFullCheckEvent;
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.events.AfkStatusChangeEvent;
|
||||
import net.ess3.provider.CommandSendListenerProvider;
|
||||
import net.ess3.provider.SchedulingProvider;
|
||||
import net.ess3.provider.FormattedCommandAliasProvider;
|
||||
import net.ess3.provider.InventoryViewProvider;
|
||||
import net.ess3.provider.KnownCommandsProvider;
|
||||
@@ -95,7 +94,7 @@ import static com.earth2me.essentials.I18n.tlLiteral;
|
||||
|
||||
public class EssentialsPlayerListener implements Listener {
|
||||
private final transient IEssentials ess;
|
||||
private final ConcurrentHashMap<UUID, SchedulingProvider.EssentialsTask> pendingMotdTasks = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, Integer> pendingMotdTasks = new ConcurrentHashMap<>();
|
||||
|
||||
public EssentialsPlayerListener(final IEssentials parent) {
|
||||
this.ess = parent;
|
||||
@@ -194,22 +193,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.isMuted()) {
|
||||
event.setCancelled(true);
|
||||
|
||||
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
if (user.hasMuteReason()) {
|
||||
user.sendTl("voiceSilencedReason", user.getMuteReason());
|
||||
} else {
|
||||
user.sendTl("voiceSilenced");
|
||||
}
|
||||
} else {
|
||||
if (user.hasMuteReason()) {
|
||||
user.sendTl("voiceSilencedReasonTime", dateDiff, user.getMuteReason());
|
||||
} else {
|
||||
user.sendTl("voiceSilencedTime", dateDiff);
|
||||
}
|
||||
}
|
||||
|
||||
user.notifyMuted();
|
||||
ess.getLogger().info(ess.getAdventureFacet().miniToLegacy(tlLiteral("mutedUserSpeaks", user.getName(), event.getMessage())));
|
||||
}
|
||||
try {
|
||||
@@ -290,9 +274,9 @@ public class EssentialsPlayerListener implements Listener {
|
||||
public void onPlayerQuit(final PlayerQuitEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
|
||||
final SchedulingProvider.EssentialsTask pendingTask = pendingMotdTasks.remove(user.getUUID());
|
||||
if (pendingTask != null) {
|
||||
pendingTask.cancel();
|
||||
final Integer pendingId = pendingMotdTasks.remove(user.getUUID());
|
||||
if (pendingId != null) {
|
||||
ess.getScheduler().cancelTask(pendingId);
|
||||
}
|
||||
|
||||
if (hideJoinQuitMessages() || ess.getSettings().allowSilentJoinQuit() && user.isAuthorized("essentials.silentquit")) {
|
||||
@@ -421,14 +405,25 @@ public class EssentialsPlayerListener implements Listener {
|
||||
|
||||
final String lastAccountName = user.getLastAccountName(); // For comparison
|
||||
user.setLastAccountName(user.getBase().getName());
|
||||
|
||||
final boolean newUsername = lastAccountName != null && !lastAccountName.equals(user.getBase().getName());
|
||||
|
||||
// A null last account name means EssentialsX has never recorded this player before, i.e. it's their first join.
|
||||
// We rely on EssentialsX's own user data here rather than Player#hasPlayedBefore(), which is unreliable on modern
|
||||
// server platforms that persist player data during the login/configuration phase.
|
||||
// See https://github.com/EssentialsX/Essentials/issues/6466, https://github.com/EssentialsX/Essentials/issues/6464
|
||||
final boolean firstJoin = lastAccountName == null;
|
||||
|
||||
// If the Minecraft account name changed, reset the nickname so the old one doesn't persist
|
||||
if (ess.getSettings().isResetNickOnNameChange() && newUsername && user.getNickname() != null) {
|
||||
user.setNickname(null);
|
||||
}
|
||||
|
||||
user.setLastLogin(currentTime);
|
||||
user.setDisplayNick();
|
||||
updateCompass(user);
|
||||
user.setLeavingHidden(false);
|
||||
|
||||
// Check for new username. If they don't want the message, let's just say it's false.
|
||||
final boolean newUsername = ess.getSettings().isCustomNewUsernameMessage() && lastAccountName != null && !lastAccountName.equals(user.getBase().getName());
|
||||
|
||||
if (!ess.getVanishedPlayersNew().isEmpty() && !user.isAuthorized("essentials.vanish.see")) {
|
||||
for (final String p : ess.getVanishedPlayersNew()) {
|
||||
final Player toVanish = ess.getServer().getPlayerExact(p);
|
||||
@@ -454,7 +449,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
} else if (message == null || hideJoinQuitMessages()) {
|
||||
effectiveMessage = null;
|
||||
} else if (ess.getSettings().isCustomJoinMessage()) {
|
||||
final String msg = (newUsername ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
|
||||
final String msg = (newUsername && ess.getSettings().isCustomNewUsernameMessage() ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
|
||||
.replace("{PLAYER}", user.getDisplayName()).replace("{USERNAME}", user.getName())
|
||||
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUsers().getUserCount()))
|
||||
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
|
||||
@@ -474,13 +469,13 @@ public class EssentialsPlayerListener implements Listener {
|
||||
joinMessageConsumer.accept(effectiveMessage);
|
||||
}
|
||||
|
||||
ess.runTaskAsynchronously(() -> ess.getServer().getPluginManager().callEvent(new AsyncUserDataLoadEvent(user, effectiveMessage)));
|
||||
ess.runTaskAsynchronously(() -> ess.getServer().getPluginManager().callEvent(new AsyncUserDataLoadEvent(user, effectiveMessage, firstJoin)));
|
||||
|
||||
if (ess.getSettings().getMotdDelay() >= 0) {
|
||||
final int motdDelay = ess.getSettings().getMotdDelay() / 50;
|
||||
final Runnable motdTask = () -> motdFlow(user);
|
||||
if (motdDelay > 0) {
|
||||
pendingMotdTasks.put(user.getUUID(), ess.scheduleEntityDelayedTask(user.getBase(), motdTask, motdDelay));
|
||||
pendingMotdTasks.put(user.getUUID(), ess.scheduleSyncDelayedTask(motdTask, motdDelay));
|
||||
} else {
|
||||
motdTask.run();
|
||||
}
|
||||
@@ -504,12 +499,21 @@ public class EssentialsPlayerListener implements Listener {
|
||||
});
|
||||
}
|
||||
|
||||
final boolean restoreFly = user.isFlyModeEnabled() && user.isAuthorized("essentials.fly");
|
||||
if (restoreFly) {
|
||||
user.getBase().setAllowFlight(true);
|
||||
if (ess.getSettings().isSendFlyEnableOnJoin()) {
|
||||
user.sendTl("flyMode", CommonPlaceholders.enableDisable(user.getSource(), true), user.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
||||
if (user.isAuthorized("essentials.fly.safelogin")) {
|
||||
user.getBase().setFallDistance(0);
|
||||
if (LocationUtil.shouldFly(ess, user.getLocation())) {
|
||||
final boolean inWater = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_17_R01) && user.getBase().isInWater();
|
||||
if (!inWater && LocationUtil.shouldFly(ess, user.getLocation())) {
|
||||
user.getBase().setAllowFlight(true);
|
||||
user.getBase().setFlying(true);
|
||||
if (ess.getSettings().isSendFlyEnableOnJoin()) {
|
||||
if (!restoreFly && ess.getSettings().isSendFlyEnableOnJoin()) {
|
||||
user.sendTl("flyMode", CommonPlaceholders.enableDisable(user.getSource(), true), user.getDisplayName());
|
||||
}
|
||||
}
|
||||
@@ -525,6 +529,11 @@ public class EssentialsPlayerListener implements Listener {
|
||||
ess.getLogger().log(Level.INFO, "Set socialspy to false for {0} because they had it enabled without permission.", user.getName());
|
||||
}
|
||||
|
||||
if (user.isFlyModeEnabled() && !user.isAuthorized("essentials.fly")) {
|
||||
user.setFlyModeEnabled(false);
|
||||
ess.getLogger().log(Level.INFO, "Set fly mode to false for {0} because they had it enabled without permission.", user.getName());
|
||||
}
|
||||
|
||||
if (user.isGodModeEnabled() && !user.isAuthorized("essentials.god")) {
|
||||
user.setGodModeEnabled(false);
|
||||
ess.getLogger().log(Level.INFO, "Set god mode to false for {0} because they had it enabled without permission.", user.getName());
|
||||
@@ -582,7 +591,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
dUser.updateActivity(false, AfkStatusChangeEvent.Cause.JOIN);
|
||||
dUser.stopTransaction();
|
||||
|
||||
ess.scheduleEntityDelayedTask(player, () -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
final User user = ess.getUser(player);
|
||||
|
||||
if (!user.getBase().isOnline()) {
|
||||
@@ -752,7 +761,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.hasUnlimited(new ItemStack(event.getBucket()))) {
|
||||
event.getItemStack().setType(event.getBucket());
|
||||
ess.scheduleEntityDelayedTask(user.getBase(), user.getBase()::updateInventory);
|
||||
ess.scheduleSyncDelayedTask(user.getBase()::updateInventory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,10 +804,9 @@ public class EssentialsPlayerListener implements Listener {
|
||||
final ComponentHolder base = (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers())
|
||||
? spyer.tlComponent("socialSpyMutedPrefix")
|
||||
: spyer.tlComponent("socialSpyPrefix");
|
||||
final ComponentHolder nameComponent = ess.getAdventureFacet().legacyToAdventure(playerName);
|
||||
final ComponentHolder messageComponent = ess.getAdventureFacet().text(": " + event.getMessage());
|
||||
final ComponentHolder formatted = ess.getAdventureFacet().deserializeMiniMessage(spyer.playerTl("socialSpyCmdFormat", playerName, event.getMessage()));
|
||||
|
||||
spyer.sendComponent(ess.getAdventureFacet().append(base, nameComponent, messageComponent));
|
||||
spyer.sendComponent(ess.getAdventureFacet().append(base, formatted));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -808,20 +816,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
final User user = ess.getUser(player);
|
||||
if (user.isMuted() && (ess.getSettings().getMuteCommands().contains(cmd) || ess.getSettings().getMuteCommands().contains("*"))) {
|
||||
event.setCancelled(true);
|
||||
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
if (user.hasMuteReason()) {
|
||||
user.sendTl("voiceSilencedReason", user.getMuteReason());
|
||||
} else {
|
||||
user.sendTl("voiceSilenced");
|
||||
}
|
||||
} else {
|
||||
if (user.hasMuteReason()) {
|
||||
user.sendTl("voiceSilencedReasonTime", dateDiff, user.getMuteReason());
|
||||
} else {
|
||||
user.sendTl("voiceSilencedTime", dateDiff);
|
||||
}
|
||||
}
|
||||
user.notifyMuted();
|
||||
ess.getLogger().info(ess.getAdventureFacet().miniToLegacy(tlLiteral("mutedUserSpeaks", player.getName(), event.getMessage())));
|
||||
return;
|
||||
}
|
||||
@@ -1013,7 +1008,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleEntityDelayedTask(user.getBase(), new DelayedClickJumpTask());
|
||||
ess.scheduleSyncDelayedTask(new DelayedClickJumpTask());
|
||||
} catch (final Exception ex) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.WARNING, ex.getMessage(), ex);
|
||||
@@ -1044,7 +1039,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleEntityDelayedTask(user.getBase(), new PowerToolUseTask());
|
||||
ess.scheduleSyncDelayedTask(new PowerToolUseTask());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1106,7 +1101,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
}
|
||||
|
||||
if (refreshPlayer != null) {
|
||||
ess.scheduleEntityDelayedTask(refreshPlayer, refreshPlayer::updateInventory, 1);
|
||||
ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1169,12 +1164,16 @@ public class EssentialsPlayerListener implements Listener {
|
||||
}
|
||||
|
||||
if (refreshPlayer != null) {
|
||||
ess.scheduleEntityDelayedTask(refreshPlayer, refreshPlayer::updateInventory, 1);
|
||||
ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPlayerFishEvent(final PlayerFishEvent event) {
|
||||
if (!ess.getSettings().cancelAfkOnFish()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
user.updateActivityOnInteract(true);
|
||||
}
|
||||
@@ -1193,7 +1192,7 @@ public class EssentialsPlayerListener implements Listener {
|
||||
final Player player = event.getPlayer();
|
||||
if (player.isFlying() && player.getAllowFlight() && user.isAuthorized("essentials.fly")) {
|
||||
// The gamemode change happens after the event, so we need to delay the flight enable
|
||||
ess.scheduleEntityDelayedTask(player, () -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
}, 1);
|
||||
@@ -1214,10 +1213,12 @@ public class EssentialsPlayerListener implements Listener {
|
||||
public void onPlayerPickupItem(final org.bukkit.event.player.PlayerPickupItemEvent event) {
|
||||
if (event.getItem().hasMetadata(Commandfireball.FIREBALL_META_KEY)) {
|
||||
event.setCancelled(true);
|
||||
} else if (ess.getSettings().getDisableItemPickupWhileAfk()) {
|
||||
if (ess.getUser(event.getPlayer()).isAfk()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if ((ess.getSettings().getDisableItemPickupWhileAfk() && user.isAfk())
|
||||
|| (user.isVanished() && !user.isAuthorizedCached("essentials.vanish.pickup"))) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1225,8 +1226,10 @@ public class EssentialsPlayerListener implements Listener {
|
||||
private final class PickupListener1_12 implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPlayerPickupItem(final org.bukkit.event.entity.EntityPickupItemEvent event) {
|
||||
if (ess.getSettings().getDisableItemPickupWhileAfk() && event.getEntity() instanceof Player) {
|
||||
if (ess.getUser((Player) event.getEntity()).isAfk()) {
|
||||
if (event.getEntity() instanceof Player) {
|
||||
final User user = ess.getUser((Player) event.getEntity());
|
||||
if ((ess.getSettings().getDisableItemPickupWhileAfk() && user.isAfk())
|
||||
|| (user.isVanished() && !user.isAuthorizedCached("essentials.vanish.pickup"))) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
@@ -1245,6 +1248,10 @@ public class EssentialsPlayerListener implements Listener {
|
||||
private final class CommandSendFilter implements CommandSendListenerProvider.Filter {
|
||||
@Override
|
||||
public Predicate<String> apply(Player player) {
|
||||
// There is no event for op status changes, but the command list is resent when
|
||||
// a player is opped/deopped, so we invalidate cached permissions here.
|
||||
ess.getPermissionsHandler().invalidatePermissionCache(player.getUniqueId());
|
||||
|
||||
final User user = ess.getUser(player);
|
||||
final Set<PluginCommand> checked = new HashSet<>();
|
||||
final Set<PluginCommand> toRemove = new HashSet<>();
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
@@ -12,19 +13,36 @@ import java.util.logging.Level;
|
||||
public class EssentialsTimer implements Runnable {
|
||||
private final transient IEssentials ess;
|
||||
private final transient Set<UUID> onlineUsers = new HashSet<>(); // Field is necessary for hidden users
|
||||
private static final long maxTime = 10 * 1000000;
|
||||
private final LinkedList<Double> history = new LinkedList<>();
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final long maxTime = 10 * 1000000;
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final long tickInterval = 50;
|
||||
private transient long lastPoll = System.nanoTime();
|
||||
private int skip1 = 0;
|
||||
private int skip2 = 0;
|
||||
|
||||
EssentialsTimer(final IEssentials ess) {
|
||||
this.ess = ess;
|
||||
history.add(20d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final long startTime = System.nanoTime();
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
|
||||
long timeSpent = (startTime - lastPoll) / 1000;
|
||||
if (timeSpent == 0) {
|
||||
timeSpent = 1;
|
||||
}
|
||||
if (history.size() > 10) {
|
||||
history.remove();
|
||||
}
|
||||
final double tps = tickInterval * 1000000.0 / timeSpent;
|
||||
if (tps <= 21) {
|
||||
history.add(tps);
|
||||
}
|
||||
lastPoll = startTime;
|
||||
int count = 0;
|
||||
onlineUsers.clear();
|
||||
for (final Player player : ess.getOnlinePlayers()) {
|
||||
@@ -81,4 +99,14 @@ public class EssentialsTimer implements Runnable {
|
||||
user.resetInvulnerabilityAfterTeleport();
|
||||
}
|
||||
}
|
||||
|
||||
public double getAverageTPS() {
|
||||
double avg = 0;
|
||||
for (final Double f : history) {
|
||||
if (f != null) {
|
||||
avg += f;
|
||||
}
|
||||
}
|
||||
return avg / history.size();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -35,7 +36,7 @@ import java.util.regex.Pattern;
|
||||
public class I18n implements net.ess3.api.II18n {
|
||||
private static final String MESSAGES = "messages";
|
||||
private static final Pattern NODOUBLEMARK = Pattern.compile("''");
|
||||
private static final ExecutorService BUNDLE_LOADER_EXECUTOR = Executors.newFixedThreadPool(2);
|
||||
private static volatile ExecutorService BUNDLE_LOADER_EXECUTOR = Executors.newFixedThreadPool(2);
|
||||
private static final ResourceBundle NULL_BUNDLE = new ResourceBundle() {
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public Enumeration<String> getKeys() {
|
||||
@@ -106,6 +107,29 @@ public class I18n implements net.ess3.api.II18n {
|
||||
|
||||
public void onDisable() {
|
||||
instance = null;
|
||||
shutdownExecutor();
|
||||
}
|
||||
|
||||
private static synchronized ExecutorService getExecutor() {
|
||||
if (BUNDLE_LOADER_EXECUTOR == null || BUNDLE_LOADER_EXECUTOR.isShutdown() || BUNDLE_LOADER_EXECUTOR.isTerminated()) {
|
||||
BUNDLE_LOADER_EXECUTOR = Executors.newFixedThreadPool(2);
|
||||
}
|
||||
return BUNDLE_LOADER_EXECUTOR;
|
||||
}
|
||||
|
||||
private static synchronized void shutdownExecutor() {
|
||||
final ExecutorService exec = BUNDLE_LOADER_EXECUTOR;
|
||||
if (exec == null) {
|
||||
return;
|
||||
}
|
||||
exec.shutdown();
|
||||
try {
|
||||
if (!exec.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
exec.shutdownNow();
|
||||
}
|
||||
} catch (final InterruptedException ignored) {
|
||||
exec.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,7 +148,7 @@ public class I18n implements net.ess3.api.II18n {
|
||||
synchronized (loadingBundles) {
|
||||
if (!loadingBundles.contains(locale)) {
|
||||
loadingBundles.add(locale);
|
||||
BUNDLE_LOADER_EXECUTOR.submit(() -> {
|
||||
getExecutor().submit(() -> {
|
||||
blockingLoadBundle(locale);
|
||||
synchronized (loadingBundles) {
|
||||
loadingBundles.remove(locale);
|
||||
|
||||
@@ -9,19 +9,18 @@ import com.earth2me.essentials.commands.PlayerNotFoundException;
|
||||
import com.earth2me.essentials.perm.PermissionsHandler;
|
||||
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
||||
import com.earth2me.essentials.userstorage.IUserMap;
|
||||
import net.ess3.provider.SchedulingProvider;
|
||||
import net.ess3.provider.Provider;
|
||||
import net.essentialsx.api.v2.services.BalanceTop;
|
||||
import net.essentialsx.api.v2.services.mail.MailService;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -81,6 +80,10 @@ public interface IEssentials extends Plugin {
|
||||
|
||||
ISettings getSettings();
|
||||
|
||||
BukkitScheduler getScheduler();
|
||||
|
||||
List<Player> getJailedPlayers();
|
||||
|
||||
IJails getJails();
|
||||
|
||||
IWarps getWarps();
|
||||
@@ -95,45 +98,17 @@ public interface IEssentials extends Plugin {
|
||||
|
||||
UpdateChecker getUpdateChecker();
|
||||
|
||||
void runTaskAsynchronously(Runnable run);
|
||||
BukkitTask runTaskAsynchronously(Runnable run);
|
||||
|
||||
void runTaskLaterAsynchronously(Runnable run, long delay);
|
||||
BukkitTask runTaskLaterAsynchronously(Runnable run, long delay);
|
||||
|
||||
SchedulingProvider.EssentialsTask runTaskTimerAsynchronously(Runnable run, long delay, long period);
|
||||
BukkitTask runTaskTimerAsynchronously(Runnable run, long delay, long period);
|
||||
|
||||
void scheduleEntityDelayedTask(Entity entity, Runnable run);
|
||||
int scheduleSyncDelayedTask(Runnable run);
|
||||
|
||||
SchedulingProvider.EssentialsTask scheduleEntityDelayedTask(Entity entity, Runnable run, long delay);
|
||||
int scheduleSyncDelayedTask(Runnable run, long delay);
|
||||
|
||||
SchedulingProvider.EssentialsTask scheduleEntityRepeatingTask(Entity entity, Runnable run, long delay, long period);
|
||||
|
||||
void scheduleLocationDelayedTask(Location location, Runnable run);
|
||||
|
||||
void scheduleLocationDelayedTask(Location location, Runnable run, long delay);
|
||||
|
||||
SchedulingProvider.EssentialsTask scheduleLocationRepeatingTask(Location location, Runnable run, long delay, long period);
|
||||
|
||||
default void scheduleGlobalDelayedTask(Runnable run) {
|
||||
scheduleGlobalDelayedTask(run, 1);
|
||||
}
|
||||
|
||||
void scheduleGlobalDelayedTask(Runnable run, long delay);
|
||||
|
||||
SchedulingProvider.EssentialsTask scheduleGlobalRepeatingTask(Runnable run, long delay, long period);
|
||||
|
||||
void scheduleInitTask(Runnable runnable);
|
||||
|
||||
boolean isEntityThread(Entity entity);
|
||||
|
||||
boolean isRegionThread(Location location);
|
||||
|
||||
boolean isGlobalThread();
|
||||
|
||||
void ensureEntity(Entity entity, Runnable runnable);
|
||||
|
||||
void ensureRegion(Location location, Runnable runnable);
|
||||
|
||||
void ensureGlobal(Runnable runnable);
|
||||
int scheduleSyncRepeatingTask(Runnable run, long delay, long period);
|
||||
|
||||
PermissionsHandler getPermissionsHandler();
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ public interface ISettings extends IConf {
|
||||
|
||||
String getNicknamePrefix();
|
||||
|
||||
boolean isResetNickOnNameChange();
|
||||
|
||||
String getOperatorColor() throws Exception;
|
||||
|
||||
boolean getPerWarpPermission();
|
||||
@@ -134,6 +136,8 @@ public interface ISettings extends IConf {
|
||||
|
||||
boolean isAlwaysTeleportSafety();
|
||||
|
||||
boolean isConsiderWorldHeightForTeleportSafety();
|
||||
|
||||
boolean isTeleportPassengerDismount();
|
||||
|
||||
boolean isForcePassengerTeleport();
|
||||
@@ -221,6 +225,8 @@ public interface ISettings extends IConf {
|
||||
|
||||
boolean cancelAfkOnChat();
|
||||
|
||||
boolean cancelAfkOnFish();
|
||||
|
||||
boolean sleepIgnoresAfkPlayers();
|
||||
|
||||
boolean sleepIgnoresVanishedPlayers();
|
||||
|
||||
@@ -32,6 +32,8 @@ import java.util.regex.Pattern;
|
||||
public interface IUser {
|
||||
boolean isAuthorized(String node);
|
||||
|
||||
boolean isAuthorizedCached(String node);
|
||||
|
||||
boolean isAuthorized(IEssentialsCommand cmd);
|
||||
|
||||
boolean isAuthorized(IEssentialsCommand cmd, String permissionPrefix);
|
||||
|
||||
@@ -13,6 +13,7 @@ import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.TranslatableException;
|
||||
import net.ess3.provider.BannerDataProvider;
|
||||
import net.ess3.provider.ItemUnbreakableProvider;
|
||||
import net.ess3.provider.PatternTypeProvider;
|
||||
import net.ess3.provider.PotionMetaProvider;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.DyeColor;
|
||||
@@ -207,8 +208,13 @@ public class MetaItemStack {
|
||||
|
||||
try {
|
||||
final String components = Joiner.on(' ').join(Arrays.asList(string).subList(fromArg, string.length));
|
||||
// modifyItemStack requires that the item namespaced key is prepended to the components for some reason
|
||||
stack = ess.getServer().getUnsafe().modifyItemStack(stack, stack.getType().getKey() + components);
|
||||
// From 1.20.6 through 1.21.11, modifyItemStack parses its argument as a full item string, so the item's
|
||||
// namespaced key must be prepended to the components. As of 26.1, the implementation prepends the item
|
||||
// key itself, so prepending it here would produce a malformed (double-namespaced) item string.
|
||||
final String itemString = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v26_1_R01)
|
||||
? components
|
||||
: stack.getType().getKey() + components;
|
||||
stack = ess.getServer().getUnsafe().modifyItemStack(stack, itemString);
|
||||
} catch (final NullPointerException npe) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.INFO, "Itemstack is invalid", npe);
|
||||
@@ -308,8 +314,14 @@ public class MetaItemStack {
|
||||
final BookMeta meta = (BookMeta) stack.getItemMeta();
|
||||
meta.setTitle(title);
|
||||
stack.setItemMeta(meta);
|
||||
} else if (split.length > 1 && split[0].startsWith("page") && split[0].length() > 4 && MaterialUtil.isEditableBook(stack.getType()) && hasMetaPermission(sender, "page", false, true, ess)) {
|
||||
final int page = NumberUtil.isInt(split[0].substring(4)) ? (Integer.parseInt(split[0].substring(4)) - 1) : 0;
|
||||
} else if (split.length > 1 && split[0].startsWith("page") && split[0].length() > 4
|
||||
&& MaterialUtil.isEditableBook(stack.getType())
|
||||
&& hasMetaPermission(sender, "page", false, true, ess)) {
|
||||
final int page = NumberUtil.isInt(split[0].substring(4)) ? (Integer.parseInt(split[0].substring(4)) - 1)
|
||||
: 0;
|
||||
if (page > 100) {
|
||||
throw new TranslatableException("pageLimitExceeded");
|
||||
}
|
||||
final BookMeta meta = (BookMeta) stack.getItemMeta();
|
||||
final List<String> pages = meta.hasPages() ? new ArrayList<>(meta.getPages()) : new ArrayList<>();
|
||||
final List<String> lines = new ArrayList<>();
|
||||
@@ -706,22 +718,15 @@ public class MetaItemStack {
|
||||
throw new TranslatableException("invalidBanner", split[1]);
|
||||
}
|
||||
|
||||
PatternType patternType = null;
|
||||
try {
|
||||
//noinspection removal
|
||||
patternType = PatternType.getByIdentifier(split[0]);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
final PatternType patternType = ess.provider(PatternTypeProvider.class).getPatternTypeByIdentifier(split[0]);
|
||||
|
||||
final BannerMeta meta = (BannerMeta) stack.getItemMeta();
|
||||
if (split[0].equalsIgnoreCase("basecolor")) {
|
||||
final Color color = Color.fromRGB(Integer.parseInt(split[1]));
|
||||
ess.provider(BannerDataProvider.class).setBaseColor(stack, DyeColor.getByColor(color));
|
||||
} else if (patternType != null) {
|
||||
//noinspection removal
|
||||
final PatternType type = PatternType.getByIdentifier(split[0]);
|
||||
final DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
|
||||
final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
|
||||
final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, patternType);
|
||||
meta.addPattern(pattern);
|
||||
}
|
||||
|
||||
@@ -733,12 +738,7 @@ public class MetaItemStack {
|
||||
throw new TranslatableException("invalidBanner", split[1]);
|
||||
}
|
||||
|
||||
PatternType patternType = null;
|
||||
try {
|
||||
//noinspection removal
|
||||
patternType = PatternType.getByIdentifier(split[0]);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
final PatternType patternType = ess.provider(PatternTypeProvider.class).getPatternTypeByIdentifier(split[0]);
|
||||
|
||||
// Hacky fix for accessing Shield meta - https://github.com/drtshock/Essentials/pull/745#issuecomment-234843795
|
||||
final BlockStateMeta meta = (BlockStateMeta) stack.getItemMeta();
|
||||
@@ -747,10 +747,8 @@ public class MetaItemStack {
|
||||
final Color color = Color.fromRGB(Integer.parseInt(split[1]));
|
||||
banner.setBaseColor(DyeColor.getByColor(color));
|
||||
} else if (patternType != null) {
|
||||
//noinspection removal
|
||||
final PatternType type = PatternType.getByIdentifier(split[0]);
|
||||
final DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
|
||||
final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
|
||||
final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, patternType);
|
||||
banner.addPattern(pattern);
|
||||
}
|
||||
banner.update();
|
||||
|
||||
@@ -60,6 +60,7 @@ public enum Mob {
|
||||
CHERRY_BOAT("CherryBoat", Enemies.NEUTRAL, "CHERRY_BOAT"),
|
||||
BAMBOO_RAFT("BambooRaft", Enemies.NEUTRAL, "BAMBOO_RAFT"),
|
||||
PALE_OAK_BOAT("PaleOakBoat", Enemies.NEUTRAL, "PALE_OAK_BOAT"),
|
||||
POPLAR_BOAT("PoplarBoat", Enemies.NEUTRAL, "POPLAR_BOAT"),
|
||||
MINECART("Minecart", Enemies.NEUTRAL, EntityType.MINECART),
|
||||
MINECART_CHEST("ChestMinecart", Enemies.NEUTRAL, MobCompat.CHEST_MINECART),
|
||||
MINECART_FURNACE("FurnaceMinecart", Enemies.NEUTRAL, MobCompat.FURNACE_MINECART),
|
||||
@@ -118,7 +119,17 @@ public enum Mob {
|
||||
FROG("Frog", Enemies.FRIENDLY, "FROG"),
|
||||
TADPOLE("Tadpole", Enemies.FRIENDLY, "TADPOLE"),
|
||||
WARDEN("Warden", Enemies.ENEMY, "WARDEN"),
|
||||
CHEST_BOAT("ChestBoat", Enemies.NEUTRAL, "CHEST_BOAT"),
|
||||
CHEST_BOAT("ChestBoat", Enemies.NEUTRAL, MobCompat.OAK_CHEST_BOAT),
|
||||
ACACIA_CHEST_BOAT("AcaciaChestBoat", Enemies.NEUTRAL, "ACACIA_CHEST_BOAT"),
|
||||
DARK_OAK_CHEST_BOAT("DarkOakChestBoat", Enemies.NEUTRAL, "DARK_OAK_CHEST_BOAT"),
|
||||
BIRCH_CHEST_BOAT("BirchChestBoat", Enemies.NEUTRAL, "BIRCH_CHEST_BOAT"),
|
||||
JUNGLE_CHEST_BOAT("JungleChestBoat", Enemies.NEUTRAL, "JUNGLE_CHEST_BOAT"),
|
||||
SPRUCE_CHEST_BOAT("SpruceChestBoat", Enemies.NEUTRAL, "SPRUCE_CHEST_BOAT"),
|
||||
MANGROVE_CHEST_BOAT("MangroveChestBoat", Enemies.NEUTRAL, "MANGROVE_CHEST_BOAT"),
|
||||
CHERRY_CHEST_BOAT("CherryChestBoat", Enemies.NEUTRAL, "CHERRY_CHEST_BOAT"),
|
||||
BAMBOO_CHEST_RAFT("BambooChestRaft", Enemies.NEUTRAL, "BAMBOO_CHEST_RAFT"),
|
||||
PALE_OAK_CHEST_BOAT("PaleOakChestBoat", Enemies.NEUTRAL, "PALE_OAK_CHEST_BOAT"),
|
||||
POPLAR_CHEST_BOAT("PoplarChestBoat", Enemies.NEUTRAL, "POPLAR_CHEST_BOAT"),
|
||||
CAMEL("Camel", Enemies.FRIENDLY, "CAMEL"),
|
||||
SNIFFER("Sniffer", Enemies.FRIENDLY, "SNIFFER"),
|
||||
ARMADILLO("Armadillo", Enemies.FRIENDLY, "ARMADILLO"),
|
||||
@@ -131,6 +142,8 @@ public enum Mob {
|
||||
NAUTILUS("Nautilus", Enemies.NEUTRAL, "NAUTILUS"),
|
||||
ZOMBIE_NAUTILUS("ZombieNautilus", Enemies.NEUTRAL, "ZOMBIE_NAUTILUS"),
|
||||
PARCHED("Parched", Enemies.ENEMY, "PARCHED"),
|
||||
SULFUR_CUBE("SulfurCube", Enemies.FRIENDLY, "SULFUR_CUBE"),
|
||||
CUSHION("Cushion", Enemies.FRIENDLY, "CUSHION")
|
||||
;
|
||||
|
||||
private static final Map<String, Mob> hashMap = new HashMap<>();
|
||||
|
||||
@@ -88,6 +88,7 @@ public final class MobCompat {
|
||||
public static final EntityType END_CRYSTAL = getEntityType("END_CRYSTAL", "ENDER_CRYSTAL");
|
||||
public static final EntityType FIREWORK_ROCKET = getEntityType("FIREWORK_ROCKET", "FIREWORK");
|
||||
public static final EntityType OAK_BOAT = getEntityType("BOAT", "OAK_BOAT");
|
||||
public static final EntityType OAK_CHEST_BOAT = getEntityType("CHEST_BOAT", "OAK_CHEST_BOAT");
|
||||
|
||||
private MobCompat() {
|
||||
}
|
||||
@@ -409,6 +410,7 @@ public final class MobCompat {
|
||||
OAK("GENERIC", "OAK"),
|
||||
SPRUCE("REDWOOD", "SPRUCE"),
|
||||
MANGROVE("GENERIC", "MANGROVE"),
|
||||
POPLAR("GENERIC", "POPLAR"),
|
||||
;
|
||||
|
||||
private final String treeSpecies;
|
||||
|
||||
@@ -55,8 +55,8 @@ public enum MobData {
|
||||
BABY_VILLAGER("child", EntityType.VILLAGER, Data.BABY, false),
|
||||
TAMED_TAMEABLE("tamed", Tameable.class, Data.TAMED, true),
|
||||
TAME_TAMEABLE("tame", Tameable.class, Data.TAMED, false),
|
||||
RANDOM_SHEEP("random", EntityType.SHEEP, Data.COLORABLE, true),
|
||||
COLORABLE_SHEEP("", Arrays.stream(DyeColor.values()).map(color -> color.name().toLowerCase(Locale.ENGLISH)).collect(Collectors.toList()), EntityType.SHEEP, Data.COLORABLE, true),
|
||||
RANDOM_COLORABLE("random", Colorable.class, Data.COLORABLE, true),
|
||||
COLORABLE("", Arrays.stream(DyeColor.values()).map(color -> color.name().toLowerCase(Locale.ENGLISH)).collect(Collectors.toList()), Colorable.class, Data.COLORABLE, true),
|
||||
POLKA_HORSE("polka", EntityType.HORSE, Horse.Style.BLACK_DOTS, true),
|
||||
SOOTY_HORSE("sooty", EntityType.HORSE, Horse.Style.BLACK_DOTS, false),
|
||||
BLAZE_HORSE("blaze", EntityType.HORSE, Horse.Style.WHITE, true),
|
||||
@@ -181,8 +181,6 @@ public enum MobData {
|
||||
WHITE_TRADER_LLAMA("white", MobCompat.TRADER_LLAMA, "llama:WHITE", true),
|
||||
BROWN_TRADER_LLAMA("brown", MobCompat.TRADER_LLAMA, "llama:BROWN", true),
|
||||
GRAY_TRADER_LLAMA("gray", MobCompat.TRADER_LLAMA, "llama:GRAY", true),
|
||||
RANDOM_SHULKER("random", MobCompat.SHULKER, Data.COLORABLE, true),
|
||||
COLORABLE_SHULKER("", Arrays.stream(DyeColor.values()).map(color -> color.name().toLowerCase(Locale.ENGLISH)).collect(Collectors.toList()), MobCompat.SHULKER, Data.COLORABLE, true),
|
||||
RED_FOX("red", MobCompat.FOX, "fox:RED", true),
|
||||
SNOW_FOX("snow", MobCompat.FOX, "fox:SNOW", true),
|
||||
SIZE_PHANTOM("", Collections.singletonList("<1-100>"), MobCompat.PHANTOM, Data.SIZE, true),
|
||||
@@ -208,12 +206,13 @@ public enum MobData {
|
||||
JUNGLE_BOAT("jungle", Boat.class, MobCompat.BoatVariant.JUNGLE, true),
|
||||
REDWOOD_BOAT("redwood", Boat.class, MobCompat.BoatVariant.REDWOOD, true),
|
||||
MANGROVE_BOAT("mangrove", Boat.class, MobCompat.BoatVariant.MANGROVE, true),
|
||||
POPLAR_BOAT("poplar", Boat.class, MobCompat.BoatVariant.POPLAR, true),
|
||||
OAK_BOAT("oak", Boat.class, MobCompat.BoatVariant.OAK, true),
|
||||
SPRUCE_BOAT("spruce", Boat.class, MobCompat.BoatVariant.SPRUCE, true),
|
||||
SADDLE_CAMEL("saddle", MobCompat.CAMEL, Data.CAMELSADDLE, true),
|
||||
PALE_WOLF("pale", EntityType.WOLF, "wolf:PALE", true),
|
||||
SPOTTED_WOLF("spotted", EntityType.WOLF, "wolf:PALE", true),
|
||||
SNOWY_WOLF("snowy", EntityType.WOLF, "wolf:PALE", true),
|
||||
SPOTTED_WOLF("spotted", EntityType.WOLF, "wolf:SPOTTED", true),
|
||||
SNOWY_WOLF("snowy", EntityType.WOLF, "wolf:SNOWY", true),
|
||||
BLACK_WOLF("black", EntityType.WOLF, "wolf:BLACK", true),
|
||||
ASHEN_WOLF("ashen", EntityType.WOLF, "wolf:ASHEN", true),
|
||||
RUSTY_WOLF("rusty", EntityType.WOLF, "wolf:RUSTY", true),
|
||||
|
||||
@@ -156,7 +156,7 @@ public class RandomTeleport implements IConf {
|
||||
|
||||
// Prompts caching random valid locations, up to a maximum number of attempts.
|
||||
public void cacheRandomLocations(final String name) {
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
ess.getServer().getScheduler().scheduleSyncDelayedTask(ess, () -> {
|
||||
for (int i = 0; i < this.getFindAttempts(); ++i) {
|
||||
calculateRandomLocation(getCenter(name), getMinRange(name), getMaxRange(name)).thenAccept(location -> {
|
||||
if (isValidRandomLocation(location)) {
|
||||
@@ -186,6 +186,7 @@ public class RandomTeleport implements IConf {
|
||||
|
||||
// Calculates a random location asynchronously.
|
||||
private CompletableFuture<Location> calculateRandomLocation(final Location center, final double minRange, final double maxRange) {
|
||||
final CompletableFuture<Location> future = new CompletableFuture<>();
|
||||
// Find an equally distributed offset by randomly rotating a point inside a rectangle about the origin
|
||||
final double rectX = RANDOM.nextDouble() * (maxRange - minRange) + minRange;
|
||||
final double rectZ = RANDOM.nextDouble() * (maxRange + minRange) - minRange;
|
||||
@@ -213,14 +214,15 @@ public class RandomTeleport implements IConf {
|
||||
360 * RANDOM.nextFloat() - 180,
|
||||
0
|
||||
);
|
||||
return PaperLib.getChunkAtAsync(location).thenApply(chunk -> { // FIXME: use thenApplyAsync + an Executor that runs tasks on the correct region thread to be fully safe.
|
||||
PaperLib.getChunkAtAsync(location).thenAccept(chunk -> {
|
||||
if (World.Environment.NETHER.equals(center.getWorld().getEnvironment())) {
|
||||
location.setY(getNetherYAt(location));
|
||||
} else {
|
||||
location.setY(center.getWorld().getHighestBlockYAt(location) + HIGHEST_BLOCK_Y_OFFSET);
|
||||
}
|
||||
return location;
|
||||
future.complete(location);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
// Returns an appropriate elevation for a given location in the nether, or MIN_VALUE if none is found
|
||||
|
||||
@@ -147,6 +147,7 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
private boolean logCommandBlockCommands;
|
||||
private boolean logConsoleCommands;
|
||||
private Set<Predicate<String>> nickBlacklist;
|
||||
private boolean resetNickOnNameChange;
|
||||
private double maxProjectileSpeed;
|
||||
private boolean removeEffectsOnHeal;
|
||||
private Map<String, String> worldAliases;
|
||||
@@ -296,6 +297,11 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
return config.getBoolean("force-safe-teleport-location", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsiderWorldHeightForTeleportSafety() {
|
||||
return config.getBoolean("consider-world-height-for-teleport-safety", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTeleportPassengerDismount() {
|
||||
return config.getBoolean("teleport-passenger-dismount", true);
|
||||
@@ -508,11 +514,20 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
return config.getString("nickname-prefix", "~");
|
||||
}
|
||||
|
||||
private boolean _resetNickOnNameChange() {
|
||||
return config.getBoolean("reset-nick-on-name-change", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNicknamePrefix() {
|
||||
return nicknamePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResetNickOnNameChange() {
|
||||
return resetNickOnNameChange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTeleportCooldown() {
|
||||
return config.getDouble("teleport-cooldown", 0);
|
||||
@@ -837,7 +852,7 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
if (reloadCount.get() < 2) {
|
||||
// on startup: add plugins again in case they registered commands with the new API
|
||||
// we need to schedule this task before any of the below tasks using _addAlternativeCommand.
|
||||
ess.scheduleGlobalDelayedTask(() -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
for (final Plugin plugin : ess.getServer().getPluginManager().getPlugins()) {
|
||||
if (plugin.isEnabled()) {
|
||||
ess.getAlternativeCommandsHandler().addPlugin(plugin);
|
||||
@@ -863,7 +878,7 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
|
||||
// This is 2 because Settings are reloaded twice in the startup lifecycle
|
||||
if (reloadCount.get() < 2) {
|
||||
ess.scheduleGlobalDelayedTask(() -> _addAlternativeCommand(effectiveAlias, toDisable));
|
||||
ess.scheduleSyncDelayedTask(() -> _addAlternativeCommand(effectiveAlias, toDisable));
|
||||
} else {
|
||||
_addAlternativeCommand(effectiveAlias, toDisable);
|
||||
}
|
||||
@@ -878,7 +893,7 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
ess.getLogger().log(Level.INFO, "Syncing commands");
|
||||
}
|
||||
if (reloadCount.get() < 2) {
|
||||
ess.scheduleGlobalDelayedTask(syncCommandsProvider::syncCommands);
|
||||
ess.scheduleSyncDelayedTask(syncCommandsProvider::syncCommands);
|
||||
} else {
|
||||
syncCommandsProvider.syncCommands();
|
||||
}
|
||||
@@ -886,6 +901,7 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
}
|
||||
|
||||
nicknamePrefix = _getNicknamePrefix();
|
||||
resetNickOnNameChange = _resetNickOnNameChange();
|
||||
operatorColor = _getOperatorColor();
|
||||
changePlayerListName = _changePlayerListName();
|
||||
configDebug = _isDebug();
|
||||
@@ -1320,6 +1336,11 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
return config.getBoolean("cancel-afk-on-chat", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancelAfkOnFish() {
|
||||
return config.getBoolean("cancel-afk-on-fish", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sleepIgnoresAfkPlayers() {
|
||||
return sleepIgnoresAfkPlayers;
|
||||
|
||||
@@ -32,6 +32,10 @@ public final class SpawnMob {
|
||||
|
||||
private static final Material GOLDEN_HELMET = EnumUtil.getMaterial("GOLDEN_HELMET", "GOLD_HELMET");
|
||||
private static final Material GOLDEN_CHESTPLATE = EnumUtil.getMaterial("GOLDEN_CHESTPLATE", "GOLD_CHESTPLATE");
|
||||
private static final Material COPPER_HELMET = EnumUtil.getMaterial("COPPER_HELMET");
|
||||
private static final Material COPPER_CHESTPLATE = EnumUtil.getMaterial("COPPER_CHESTPLATE");
|
||||
private static final Material COPPER_LEGGINGS = EnumUtil.getMaterial("COPPER_LEGGINGS");
|
||||
private static final Material COPPER_BOOTS = EnumUtil.getMaterial("COPPER_BOOTS");
|
||||
private static final Material GOLDEN_LEGGINGS = EnumUtil.getMaterial("GOLDEN_LEGGINGS", "GOLD_LEGGINGS");
|
||||
private static final Material GOLDEN_BOOTS = EnumUtil.getMaterial("GOLDEN_BOOTS", "GOLD_BOOTS");
|
||||
private static final Material GOLDEN_SWORD = EnumUtil.getMaterial("GOLDEN_SWORD", "GOLD_SWORD");
|
||||
@@ -210,6 +214,11 @@ public final class SpawnMob {
|
||||
invent.setLeggings(new ItemStack(Material.NETHERITE_LEGGINGS, 1));
|
||||
invent.setChestplate(new ItemStack(Material.NETHERITE_CHESTPLATE, 1));
|
||||
invent.setHelmet(new ItemStack(Material.NETHERITE_HELMET, 1));
|
||||
} else if (inputData.contains("copper") && COPPER_HELMET != null) {
|
||||
invent.setBoots(new ItemStack(COPPER_BOOTS, 1));
|
||||
invent.setLeggings(new ItemStack(COPPER_LEGGINGS, 1));
|
||||
invent.setChestplate(new ItemStack(COPPER_CHESTPLATE, 1));
|
||||
invent.setHelmet(new ItemStack(COPPER_HELMET, 1));
|
||||
} else if (inputData.contains("diamond")) {
|
||||
invent.setBoots(new ItemStack(Material.DIAMOND_BOOTS, 1));
|
||||
invent.setLeggings(new ItemStack(Material.DIAMOND_LEGGINGS, 1));
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.ess3.api.events.JailStatusChangeEvent;
|
||||
import net.ess3.api.events.MuteStatusChangeEvent;
|
||||
import net.ess3.api.events.UserBalanceUpdateEvent;
|
||||
import net.ess3.provider.PlayerLocaleProvider;
|
||||
import net.essentialsx.api.v2.events.PreTransactionEvent;
|
||||
import net.essentialsx.api.v2.events.TransactionEvent;
|
||||
import net.essentialsx.api.v2.services.mail.MailSender;
|
||||
import org.bukkit.Location;
|
||||
@@ -146,6 +147,19 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorizedCached(final String node) {
|
||||
if (Essentials.TESTING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean result = isAuthorizedCachedCheck(node);
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.INFO, "checking if " + base.getName() + " has " + node + " (cached) - " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(final String node) {
|
||||
if (Essentials.TESTING) {
|
||||
@@ -185,6 +199,24 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAuthorizedCachedCheck(final String node) {
|
||||
if (base instanceof OfflinePlayerStub) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return ess.getPermissionsHandler().hasPermissionCached(base, node);
|
||||
} catch (final Exception ex) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.SEVERE, "Permission System Error: " + ess.getPermissionsHandler().getName() + " returned: " + ex.getMessage(), ex);
|
||||
} else {
|
||||
ess.getLogger().log(Level.SEVERE, "Permission System Error: " + ess.getPermissionsHandler().getName() + " returned: " + ex.getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPermSetCheck(final String node) {
|
||||
if (base instanceof OfflinePlayerStub) {
|
||||
return false;
|
||||
@@ -263,12 +295,20 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
payUser(reciever, value, UserBalanceUpdateEvent.Cause.UNKNOWN);
|
||||
}
|
||||
|
||||
public void payUser(final User reciever, final BigDecimal value, final UserBalanceUpdateEvent.Cause cause) throws Exception {
|
||||
public void payUser(final User reciever, BigDecimal value, final UserBalanceUpdateEvent.Cause cause) throws Exception {
|
||||
if (value.compareTo(BigDecimal.ZERO) < 1) {
|
||||
throw new Exception(tlLocale(playerLocale, "payMustBePositive"));
|
||||
}
|
||||
|
||||
if (canAfford(value)) {
|
||||
// Call an event for pre-transaction
|
||||
final PreTransactionEvent preTransactionEvent = new PreTransactionEvent(this.getSource(), reciever, value);
|
||||
ess.getServer().getPluginManager().callEvent(preTransactionEvent);
|
||||
if (preTransactionEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
value = preTransactionEvent.getAmount();
|
||||
|
||||
setMoney(getMoney().subtract(value), cause);
|
||||
reciever.setMoney(reciever.getMoney().add(value), cause);
|
||||
sendTl("moneySentTo", AdventureUtil.parsed(NumberUtil.displayCurrency(value, ess)), reciever.getDisplayName());
|
||||
@@ -577,7 +617,8 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
final BigDecimal value = _getMoney();
|
||||
final long elapsed = System.nanoTime() - start;
|
||||
if (elapsed > ess.getSettings().getEconomyLagWarning()) {
|
||||
ess.getLogger().log(Level.INFO, "Lag Notice - Slow Economy Response - Request took over {0}ms!", elapsed / 1000000.0);
|
||||
ess.getLogger().log(Level.INFO, String.format(Locale.ROOT,
|
||||
"Lag Notice - Slow Economy Response - Request took over %.3fms!", elapsed / 1_000_000.0));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -782,6 +823,24 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
return false;
|
||||
}
|
||||
|
||||
// Notifies this (muted) user that they have been silenced, including the reason and remaining time if applicable.
|
||||
public void notifyMuted() {
|
||||
final String dateDiff = getMuteTimeout() > 0 ? DateUtil.formatDateDiff(getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
if (hasMuteReason()) {
|
||||
sendTl("voiceSilencedReason", getMuteReason());
|
||||
} else {
|
||||
sendTl("voiceSilenced");
|
||||
}
|
||||
} else {
|
||||
if (hasMuteReason()) {
|
||||
sendTl("voiceSilencedReasonTime", dateDiff, getMuteReason());
|
||||
} else {
|
||||
sendTl("voiceSilencedTime", dateDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastActivityTime() {
|
||||
return this.lastActivity;
|
||||
@@ -821,7 +880,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
public void updateActivityOnChat(final boolean broadcast) {
|
||||
if (ess.getSettings().cancelAfkOnChat()) {
|
||||
//Chat happens async, make sure we have a sync context
|
||||
ess.scheduleEntityDelayedTask(base, () -> updateActivity(broadcast, AfkStatusChangeEvent.Cause.CHAT));
|
||||
ess.scheduleSyncDelayedTask(() -> updateActivity(broadcast, AfkStatusChangeEvent.Cause.CHAT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,7 +906,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
|
||||
for (final User user : ess.getOnlineUsers()) {
|
||||
if (user.isAuthorized("essentials.kick.notify")) {
|
||||
user.sendTl("playerKicked", Console.DISPLAY_NAME, getName(), user.playerTl("autoAfkKickReason", kickTime));
|
||||
user.sendTl("playerKicked", Console.displayName(), getName(), user.playerTl("autoAfkKickReason", kickTime));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -863,7 +922,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
}
|
||||
}
|
||||
final long autoafk = ess.getSettings().getAutoAfk();
|
||||
if (!isAfk() && autoafk > 0 && lastActivity + autoafk * 1000 < System.currentTimeMillis() && isAuthorized("essentials.afk.auto")) {
|
||||
if (!isAfk() && autoafk > 0 && lastActivity + autoafk * 1000 < System.currentTimeMillis() && isAuthorizedCached("essentials.afk.auto")) {
|
||||
setAfk(true, AfkStatusChangeEvent.Cause.ACTIVITY);
|
||||
if (isAfk() && !isHidden()) {
|
||||
setDisplayNick();
|
||||
|
||||
@@ -42,7 +42,9 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
super(base);
|
||||
this.ess = (Essentials) ess;
|
||||
final File folder = new File(ess.getDataFolder(), "userdata");
|
||||
if (!folder.exists() && !folder.mkdirs()) {
|
||||
// mkdirs() may return false if another thread created the folder concurrently,
|
||||
// so fall back to checking whether the directory now exists to avoid a race.
|
||||
if (!folder.isDirectory() && !folder.mkdirs() && !folder.isDirectory()) {
|
||||
throw new RuntimeException("Unable to create userdata folder!");
|
||||
}
|
||||
|
||||
@@ -469,6 +471,15 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
config.save();
|
||||
}
|
||||
|
||||
public boolean isFlyModeEnabled() {
|
||||
return holder.flyMode();
|
||||
}
|
||||
|
||||
public void setFlyModeEnabled(final boolean set) {
|
||||
holder.flyMode(set);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public boolean getMuted() {
|
||||
return holder.muted();
|
||||
}
|
||||
|
||||
+8
-2
@@ -21,17 +21,18 @@ import java.util.Locale;
|
||||
|
||||
public class SpigotAdventureFacet implements AdventureFacet {
|
||||
private static final LegacyComponentSerializer LEGACY_SERIALIZER;
|
||||
private static final LegacyComponentSerializer LEGACY_SERIALIZER_URLS;
|
||||
private static final MiniMessage MINI_MESSAGE_NO_TAGS;
|
||||
|
||||
static {
|
||||
final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder()
|
||||
.flattener(ComponentFlattener.basic())
|
||||
.extractUrls(AbstractChatEvent.URL_PATTERN)
|
||||
.useUnusualXRepeatedCharacterHexFormat();
|
||||
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) {
|
||||
builder.hexColors();
|
||||
}
|
||||
LEGACY_SERIALIZER = builder.build();
|
||||
LEGACY_SERIALIZER_URLS = builder.extractUrls(AbstractChatEvent.URL_PATTERN).build();
|
||||
|
||||
MINI_MESSAGE_NO_TAGS = MiniMessage.builder().strict(true).build();
|
||||
}
|
||||
@@ -75,7 +76,7 @@ public class SpigotAdventureFacet implements AdventureFacet {
|
||||
|
||||
@Override
|
||||
public String legacyToMini(String message) {
|
||||
return legacyToMini(message, true);
|
||||
return legacyToMini(message, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,6 +128,11 @@ public class SpigotAdventureFacet implements AdventureFacet {
|
||||
return miniMessageInstance.stripTags(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String legacyToMiniWithUrls(String message) {
|
||||
return miniMessageInstance.serialize(LEGACY_SERIALIZER_URLS.deserialize(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeTags(String input) {
|
||||
return miniMessageInstance.escapeTags(input);
|
||||
|
||||
@@ -323,7 +323,13 @@ public class Economy {
|
||||
throw new IllegalArgumentException("Economy user cannot be null");
|
||||
}
|
||||
final BigDecimal result = getMoneyExact(user).add(amount, MATH_CONTEXT);
|
||||
setMoney(user, result);
|
||||
// Deposits (positive amounts) always improve the balance, so minMoney and
|
||||
// loan checks don't apply — they exist to prevent the balance from dropping.
|
||||
if (amount.signum() <= 0) {
|
||||
setMoney(user, result);
|
||||
} else {
|
||||
user.setMoney(result, UserBalanceUpdateEvent.Cause.API);
|
||||
}
|
||||
Trade.log("API", "Add", "API", user.getName(), new Trade(amount, ess), null, null, null, result, ess);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class Commandbalancetop extends EssentialsCommand {
|
||||
new TextPager(cache).showPage(Integer.toString(page), null, "balancetop", sender);
|
||||
};
|
||||
if (sender.getSender() instanceof BlockCommandSender) {
|
||||
ess.scheduleGlobalDelayedTask(runnable);
|
||||
ess.scheduleSyncDelayedTask(runnable);
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class Commandban extends EssentialsCommand {
|
||||
}
|
||||
|
||||
final String senderName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.displayName();
|
||||
final String banReason;
|
||||
if (args.length > 1) {
|
||||
banReason = FormatUtil.replaceFormat(getFinalArg(args, 1).replace("\\n", "\n").replace("|", "\n"));
|
||||
|
||||
@@ -26,7 +26,7 @@ public class Commandbanip extends EssentialsCommand {
|
||||
}
|
||||
|
||||
final String senderName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.displayName();
|
||||
|
||||
String ipAddress;
|
||||
if (FormatUtil.validIP(args[0])) {
|
||||
|
||||
@@ -23,7 +23,7 @@ public class Commandbeezooka extends EssentialsCommand {
|
||||
final Entity bee = Mob.BEE.spawn(user.getWorld(), server, user.getBase().getEyeLocation());
|
||||
bee.setVelocity(user.getBase().getEyeLocation().getDirection().multiply(2));
|
||||
|
||||
ess.scheduleEntityDelayedTask(bee, () -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
final Location loc = bee.getLocation();
|
||||
bee.remove();
|
||||
loc.getWorld().createExplosion(loc, 0F);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.adventure.AdventureUtil;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import org.bukkit.Server;
|
||||
|
||||
@@ -15,6 +16,9 @@ public class Commandbroadcast extends EssentialsCommand {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
ess.broadcastTl("broadcast", FormatUtil.replaceFormat(getFinalArg(args, 0)).replace("\\n", "\n"), sender.getDisplayName());
|
||||
final String message = FormatUtil.replaceFormat(getFinalArg(args, 0)).replace("\\n", "\n");
|
||||
ess.broadcastTl("broadcast",
|
||||
AdventureUtil.parsed(ess.getAdventureFacet().legacyToMiniWithUrls(ess.getAdventureFacet().escapeTags(message))),
|
||||
sender.getDisplayName());
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -54,7 +54,11 @@ public class Commandbroadcastworld extends EssentialsCommand {
|
||||
if (message.isEmpty()) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast", FormatUtil.replaceFormat(message).replace("\\n", "\n"), AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(name)));
|
||||
final String formatted = FormatUtil.replaceFormat(message).replace("\\n", "\n");
|
||||
ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast",
|
||||
AdventureUtil.parsed(
|
||||
ess.getAdventureFacet().legacyToMiniWithUrls(ess.getAdventureFacet().escapeTags(formatted))),
|
||||
AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(name)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,8 +19,12 @@ public class Commandburn extends EssentialsCommand {
|
||||
}
|
||||
|
||||
final User user = getPlayer(server, sender, args, 0);
|
||||
user.getBase().setFireTicks(Integer.parseInt(args[1]) * 20);
|
||||
sender.sendTl("burnMsg", user.getDisplayName(), Integer.parseInt(args[1]));
|
||||
final int seconds = Integer.parseInt(args[1]);
|
||||
|
||||
final int fireTicks = (int) Math.max(0L, Math.min((long) seconds * 20, Integer.MAX_VALUE));
|
||||
|
||||
user.getBase().setFireTicks(fireTicks);
|
||||
sender.sendTl("burnMsg", user.getDisplayName(), Math.max(0, seconds));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,7 +12,8 @@ public class Commanddisposal extends EssentialsCommand {
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
user.sendTl("openingDisposal");
|
||||
user.getBase().openInventory(ess.getServer().createInventory(user.getBase(), 36, user.playerTl("disposal")));
|
||||
final String title = ess.getAdventureFacet().miniToLegacy(user.playerTl("disposal"));
|
||||
user.getBase().openInventory(ess.getServer().createInventory(user.getBase(), 36, title));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.google.common.collect.Lists;
|
||||
import net.ess3.api.MaxMoneyException;
|
||||
import net.ess3.api.TranslatableException;
|
||||
import net.ess3.api.events.UserBalanceUpdateEvent;
|
||||
import net.ess3.provider.PlayerLocaleProvider;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -34,7 +35,14 @@ public class Commandeco extends EssentialsLoopCommand {
|
||||
try {
|
||||
cmd = EcoCommands.valueOf(args[0].toUpperCase(Locale.ENGLISH));
|
||||
isPercent = cmd != EcoCommands.RESET && args[2].endsWith("%");
|
||||
amount = (cmd == EcoCommands.RESET) ? ess.getSettings().getStartingBalance() : new BigDecimal(args[2].replaceAll("[^0-9\\.]", ""));
|
||||
if (cmd == EcoCommands.RESET) {
|
||||
amount = ess.getSettings().getStartingBalance();
|
||||
} else if (sender.isPlayer() && ess.getSettings().isPerPlayerLocale()) {
|
||||
final String playerLocale = ess.provider(PlayerLocaleProvider.class).getLocale(sender.getPlayer());
|
||||
amount = NumberUtil.parseStringToBDecimal(args[2], ess.getUser(sender.getPlayer()).getPlayerLocale(playerLocale));
|
||||
} else {
|
||||
amount = NumberUtil.parseStringToBDecimal(args[2]);
|
||||
}
|
||||
} catch (final Exception ex) {
|
||||
throw new NotEnoughArgumentsException(ex);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.earth2me.essentials.commands.essentials.HomesCommand;
|
||||
import com.earth2me.essentials.commands.essentials.ItemTestCommand;
|
||||
import com.earth2me.essentials.commands.essentials.MooCommand;
|
||||
import com.earth2me.essentials.commands.essentials.NyanCommand;
|
||||
import com.earth2me.essentials.commands.essentials.ReloadCommand;
|
||||
import com.earth2me.essentials.commands.essentials.UsermapCommand;
|
||||
import com.earth2me.essentials.commands.essentials.VersionCommand;
|
||||
|
||||
@@ -19,6 +20,7 @@ public class Commandessentials extends EssentialsTreeCommand {
|
||||
registerNode(new DebugCommand());
|
||||
registerNode(new CommandMapCommand());
|
||||
registerNode(new DumpCommand());
|
||||
registerNode(new ReloadCommand());
|
||||
registerNode(new CleanupCommand());
|
||||
registerNode(new HomesCommand());
|
||||
registerNode(new UsermapCommand());
|
||||
|
||||
@@ -147,15 +147,21 @@ public class Commandexp extends EssentialsLoopCommand {
|
||||
}
|
||||
return options;
|
||||
} else if (args.length == 2) {
|
||||
if ((args[0].equalsIgnoreCase("set") && user.isAuthorized("essentials.exp.set")) || (args[0].equalsIgnoreCase("give") && user.isAuthorized("essentials.exp.give")) || (args[0].equalsIgnoreCase("take") && user.isAuthorized("essentials.exp.take"))) {
|
||||
final ExpCommands cmd;
|
||||
try {
|
||||
cmd = ExpCommands.valueOf(args[0].toUpperCase(Locale.ENGLISH));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (cmd.hasOtherPermission(user)) {
|
||||
return getPlayers(user);
|
||||
}
|
||||
if (cmd.hasPermission(user) && cmd != ExpCommands.SHOW && cmd != ExpCommands.RESET) {
|
||||
final String levellessArg = args[1].toLowerCase(Locale.ENGLISH).replaceAll("l", "");
|
||||
if (NumberUtil.isInt(levellessArg)) {
|
||||
return Lists.newArrayList(levellessArg + "l");
|
||||
}
|
||||
}
|
||||
if (user.isAuthorized("essentials.exp.others")) {
|
||||
return getPlayers(user);
|
||||
}
|
||||
} else if (args.length == 3 && !(args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("reset"))) {
|
||||
final String levellessArg = args[2].toLowerCase(Locale.ENGLISH).replaceAll("l", "");
|
||||
if (NumberUtil.isInt(levellessArg)) {
|
||||
|
||||
@@ -34,6 +34,8 @@ public class Commandfly extends EssentialsToggleCommand {
|
||||
user.getBase().setFallDistance(0f);
|
||||
user.getBase().setAllowFlight(enabled);
|
||||
|
||||
user.setFlyModeEnabled(enabled);
|
||||
|
||||
if (!user.getBase().getAllowFlight()) {
|
||||
user.getBase().setFlying(false);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,15 @@ package com.earth2me.essentials.commands;
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import net.ess3.provider.WorldTileEntityCountProvider;
|
||||
import net.ess3.provider.TileEntityProvider;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Commandgc extends EssentialsCommand {
|
||||
public Commandgc() {
|
||||
@@ -18,7 +20,7 @@ public class Commandgc extends EssentialsCommand {
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
final double tps = 20d; //TODO
|
||||
final double tps = ess.getTimer().getAverageTPS();
|
||||
final ChatColor color;
|
||||
if (tps >= 18.0) {
|
||||
color = ChatColor.GREEN;
|
||||
@@ -35,7 +37,7 @@ public class Commandgc extends EssentialsCommand {
|
||||
sender.sendTl("gcfree", Runtime.getRuntime().freeMemory() / 1024 / 1024);
|
||||
|
||||
final List<World> worlds = server.getWorlds();
|
||||
final WorldTileEntityCountProvider worldTileEntityCountProvider = ess.provider(WorldTileEntityCountProvider.class);
|
||||
final TileEntityProvider tileEntityProvider = ess.provider(TileEntityProvider.class);
|
||||
for (final World w : worlds) {
|
||||
String worldType = "World";
|
||||
switch (w.getEnvironment()) {
|
||||
@@ -47,7 +49,15 @@ public class Commandgc extends EssentialsCommand {
|
||||
break;
|
||||
}
|
||||
|
||||
final int tileEntities = worldTileEntityCountProvider.getTileEntityCount(w);
|
||||
int tileEntities = 0;
|
||||
|
||||
try {
|
||||
for (final Chunk chunk : w.getLoadedChunks()) {
|
||||
tileEntities += tileEntityProvider.getTileEntities(chunk).length;
|
||||
}
|
||||
} catch (final java.lang.ClassCastException ex) {
|
||||
ess.getLogger().log(Level.SEVERE, "Corrupted chunk data on world " + w, ex);
|
||||
}
|
||||
|
||||
sender.sendTl("gcWorld", worldType, w.getName(), w.getLoadedChunks().length, w.getEntities().size(), tileEntities);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class Commandhelp extends EssentialsCommand {
|
||||
user.sendTl("commandHelpLine3");
|
||||
if (essCommand != null && !essCommand.getUsageStrings().isEmpty()) {
|
||||
for (Map.Entry<String, String> usage : essCommand.getUsageStrings().entrySet()) {
|
||||
user.sendTl("commandHelpLineUsage", AdventureUtil.parsed(usage.getKey().replace("<command>", cmd)), AdventureUtil.parsed(usage.getValue()));
|
||||
user.sendTl("commandHelpLineUsage", AdventureUtil.parsed(usage.getKey().replace("<command>", cmd)), AdventureUtil.parsed(user.playerTl(usage.getValue())));
|
||||
}
|
||||
} else {
|
||||
user.sendMessage(bukkit.getUsage());
|
||||
|
||||
@@ -41,7 +41,10 @@ public class Commandhome extends EssentialsCommand {
|
||||
}
|
||||
}
|
||||
try {
|
||||
if ("bed".equalsIgnoreCase(homeName) && user.isAuthorized("essentials.home.bed")) {
|
||||
if ("bed".equalsIgnoreCase(homeName)) {
|
||||
if (!user.isAuthorized("essentials.home.bed")) {
|
||||
throw new TranslatableException("noAccessCommand");
|
||||
}
|
||||
if (!player.getBase().isOnline() || player.getBase() instanceof OfflinePlayerStub) {
|
||||
throw new TranslatableException("bedOffline");
|
||||
}
|
||||
@@ -73,10 +76,15 @@ public class Commandhome extends EssentialsCommand {
|
||||
final List<String> homes = finalPlayer.getHomes();
|
||||
if (homes.isEmpty() && finalPlayer.equals(user)) {
|
||||
if (ess.getSettings().isSpawnIfNoHome()) {
|
||||
final UserTeleportHomeEvent event = new UserTeleportHomeEvent(user, null, bed != null ? bed : finalPlayer.getWorld().getSpawnLocation(), bed != null ? UserTeleportHomeEvent.HomeType.BED : UserTeleportHomeEvent.HomeType.SPAWN);
|
||||
final boolean useBed = bed != null && user.isAuthorized("essentials.home.bed");
|
||||
final UserTeleportHomeEvent event = new UserTeleportHomeEvent(user, null, useBed ? bed : finalPlayer.getWorld().getSpawnLocation(), useBed ? UserTeleportHomeEvent.HomeType.BED : UserTeleportHomeEvent.HomeType.SPAWN);
|
||||
server.getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()) {
|
||||
user.getAsyncTeleport().respawn(charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
|
||||
if (useBed) {
|
||||
user.getAsyncTeleport().respawn(charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
|
||||
} else {
|
||||
user.getAsyncTeleport().teleport(finalPlayer.getWorld().getSpawnLocation(), charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showError(user.getBase(), new TranslatableException("noHomeSetPlayer"), commandLabel);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Commandjailedplayers extends EssentialsCommand {
|
||||
public Commandjailedplayers() {
|
||||
super("jailedplayers");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (ess.getJailedPlayers() != null && !ess.getJailedPlayers().isEmpty()) {
|
||||
sender.sendTl("jailedPlayersList", StringUtil.joinList(", ", ess.getJailedPlayers().stream().map(Player::getName).collect(Collectors.toList())));
|
||||
} else {
|
||||
sender.sendTl("jailedPlayersEmpty");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class Commandkick extends EssentialsCommand {
|
||||
|
||||
kickReason = event.getReason();
|
||||
target.getBase().kickPlayer(kickReason);
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.displayName();
|
||||
|
||||
final String tlKey = "playerKicked";
|
||||
final Object[] objects = {senderDisplayName, target.getName(), kickReason};
|
||||
|
||||
@@ -32,15 +32,13 @@ public class Commandkill extends EssentialsLoopCommand {
|
||||
}
|
||||
final DamageEventProvider provider = ess.provider(DamageEventProvider.class);
|
||||
|
||||
ess.scheduleEntityDelayedTask(matchPlayer, () -> {
|
||||
final EntityDamageEvent ede = provider.callDamageEvent(matchPlayer, sender.isPlayer() && sender.getPlayer().getName().equals(matchPlayer.getName()) ? EntityDamageEvent.DamageCause.SUICIDE : EntityDamageEvent.DamageCause.CUSTOM, Float.MAX_VALUE);
|
||||
if (ede.isCancelled() && sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.kill.force")) {
|
||||
return;
|
||||
}
|
||||
ede.getEntity().setLastDamageCause(ede);
|
||||
matchPlayer.setHealth(0);
|
||||
sender.sendTl("kill", matchPlayer.getDisplayName());
|
||||
});
|
||||
final EntityDamageEvent ede = provider.callDamageEvent(matchPlayer, sender.isPlayer() && sender.getPlayer().getName().equals(matchPlayer.getName()) ? EntityDamageEvent.DamageCause.SUICIDE : EntityDamageEvent.DamageCause.CUSTOM, Float.MAX_VALUE);
|
||||
if (ede.isCancelled() && sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.kill.force")) {
|
||||
return;
|
||||
}
|
||||
ede.getEntity().setLastDamageCause(ede);
|
||||
matchPlayer.setHealth(0);
|
||||
sender.sendTl("kill", matchPlayer.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -49,7 +49,7 @@ public class Commandkittycannon extends EssentialsCommand {
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
final Entity ocelot = Mob.CAT.getType() == null ? spawnOcelot(server, user) : spawnCat(server, user);
|
||||
ess.scheduleEntityDelayedTask(ocelot, () -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
final Location loc = ocelot.getLocation();
|
||||
ocelot.remove();
|
||||
loc.getWorld().createExplosion(loc, 0F);
|
||||
|
||||
@@ -33,17 +33,15 @@ public class Commandlightning extends EssentialsLoopCommand {
|
||||
}
|
||||
final int finalPower = power;
|
||||
loopOnlinePlayersConsumer(server, sender, false, true, args[0], player -> {
|
||||
ess.scheduleEntityDelayedTask(player.getBase(), () -> {
|
||||
sender.sendTl("lightningUse", player.getDisplayName());
|
||||
final LightningStrike strike = player.getBase().getWorld().strikeLightningEffect(player.getBase().getLocation());
|
||||
sender.sendTl("lightningUse", player.getDisplayName());
|
||||
final LightningStrike strike = player.getBase().getWorld().strikeLightningEffect(player.getBase().getLocation());
|
||||
|
||||
if (!player.isGodModeEnabled()) {
|
||||
player.getBase().damage(finalPower, strike);
|
||||
}
|
||||
if (ess.getSettings().warnOnSmite()) {
|
||||
player.sendTl("lightningSmited");
|
||||
}
|
||||
});
|
||||
if (!player.isGodModeEnabled()) {
|
||||
player.getBase().damage(finalPower, strike);
|
||||
}
|
||||
if (ess.getSettings().warnOnSmite()) {
|
||||
player.sendTl("lightningSmited");
|
||||
}
|
||||
});
|
||||
loopOnlinePlayers(server, sender, true, true, args[0], null);
|
||||
}
|
||||
|
||||
@@ -36,11 +36,13 @@ public class Commandmore extends EssentialsCommand {
|
||||
if (!NumberUtil.isPositiveInt(args[0])) {
|
||||
throw new TranslatableException("nonZeroPosNumber");
|
||||
}
|
||||
newStackSize += Integer.parseInt(args[0]);
|
||||
|
||||
if (newStackSize > (canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize())) {
|
||||
user.sendTl(canOversized ? "fullStackDefaultOversize" : "fullStackDefault", canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize());
|
||||
newStackSize = canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize();
|
||||
final int cap = canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize();
|
||||
final long newSizeLong = (long) stack.getAmount() + Integer.parseInt(args[0]);
|
||||
if (newSizeLong > cap) {
|
||||
user.sendTl(canOversized ? "fullStackDefaultOversize" : "fullStackDefault", cap);
|
||||
newStackSize = cap;
|
||||
} else {
|
||||
newStackSize = (int) newSizeLong;
|
||||
}
|
||||
} else if (canOversized) {
|
||||
newStackSize = ess.getSettings().getOversizedStackSize();
|
||||
|
||||
@@ -4,9 +4,7 @@ import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.Console;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.messaging.IMessageRecipient;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import net.ess3.api.TranslatableException;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -27,20 +25,15 @@ public class Commandmsg extends EssentialsLoopCommand {
|
||||
final boolean canWildcard = sender.isAuthorized("essentials.msg.multiple");
|
||||
if (sender.isPlayer()) {
|
||||
final User user = ess.getUser(sender.getPlayer());
|
||||
if (user.isMuted()) {
|
||||
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
throw new TranslatableException(user.hasMuteReason() ? "voiceSilencedReason" : "voiceSilenced", user.getMuteReason());
|
||||
}
|
||||
throw new TranslatableException(user.hasMuteReason() ? "voiceSilencedReasonTime" : "voiceSilencedTime", dateDiff, user.getMuteReason());
|
||||
}
|
||||
// Muted players are handled in SimpleMessageRecipient#sendMessage so that the message can still
|
||||
// be shown to social spies before being dropped instead of being delivered to the recipient.
|
||||
message = FormatUtil.formatMessage(user, "essentials.msg", message);
|
||||
} else {
|
||||
message = FormatUtil.replaceFormat(message);
|
||||
}
|
||||
|
||||
// Sending messages to console
|
||||
if (args[0].equalsIgnoreCase(Console.NAME) || args[0].equalsIgnoreCase(Console.DISPLAY_NAME)) {
|
||||
if (args[0].equalsIgnoreCase(Console.NAME) || args[0].equalsIgnoreCase(Console.displayName())) {
|
||||
final IMessageRecipient messageSender = sender.isPlayer() ? ess.getUser(sender.getPlayer()) : Console.getInstance();
|
||||
messageSender.sendMessage(Console.getInstance(), message);
|
||||
return;
|
||||
|
||||
@@ -21,6 +21,7 @@ public class Commandmute extends EssentialsCommand {
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
final boolean isUnmute = commandLabel.toLowerCase(java.util.Locale.ENGLISH).contains("unmute");
|
||||
boolean nomatch = false;
|
||||
if (args.length < 1) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
@@ -32,11 +33,17 @@ public class Commandmute extends EssentialsCommand {
|
||||
nomatch = true;
|
||||
user = ess.getUser(new OfflinePlayerStub(args[0], ess.getServer()));
|
||||
}
|
||||
|
||||
if (isUnmute && !user.getMuted()) {
|
||||
sender.sendTl("playerNotMuted", user.getDisplayName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.getBase().isOnline() && sender.isPlayer()) {
|
||||
if (!sender.isAuthorized("essentials.mute.offline")) {
|
||||
throw new TranslatableException("muteExemptOffline");
|
||||
}
|
||||
} else if (user.isAuthorized("essentials.mute.exempt")) {
|
||||
} else if (!isUnmute && user.isAuthorized("essentials.mute.exempt")) {
|
||||
throw new TranslatableException("muteExempt");
|
||||
}
|
||||
|
||||
@@ -44,7 +51,7 @@ public class Commandmute extends EssentialsCommand {
|
||||
final String time;
|
||||
String muteReason = null;
|
||||
|
||||
if (args.length > 1) {
|
||||
if (!isUnmute && args.length > 1) {
|
||||
time = args[1];
|
||||
try {
|
||||
muteTimestamp = DateUtil.parseDateDiff(time, true);
|
||||
@@ -59,7 +66,7 @@ public class Commandmute extends EssentialsCommand {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean willMute = (args.length > 1) || !user.getMuted();
|
||||
final boolean willMute = !isUnmute && ((args.length > 1) || !user.getMuted());
|
||||
final User controller = sender.isPlayer() ? ess.getUser(sender.getPlayer()) : null;
|
||||
final MuteStatusChangeEvent event = new MuteStatusChangeEvent(user, controller, willMute, muteTimestamp, muteReason);
|
||||
ess.getServer().getPluginManager().callEvent(event);
|
||||
@@ -121,6 +128,8 @@ public class Commandmute extends EssentialsCommand {
|
||||
} else {
|
||||
sender.sendTl("unmutedPlayer", user.getDisplayName());
|
||||
user.sendTl("playerUnmuted");
|
||||
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("unmuteNotify", sender.getSender().getName(), user.getName())));
|
||||
ess.broadcastTl(null, "essentials.mute.notify", "unmuteNotify", new Object[]{sender.getSender().getName(), user.getName()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,20 +35,17 @@ public class Commandnuke extends EssentialsCommand {
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ess.scheduleEntityDelayedTask(user.getBase(), () -> {
|
||||
user.sendTl("nuke");
|
||||
final Location loc = user.getLocation();
|
||||
final World world = loc.getWorld();
|
||||
if (world != null) {
|
||||
for (int x = -10; x <= 10; x += 5) {
|
||||
for (int z = -10; z <= 10; z += 5) {
|
||||
final TNTPrimed entity = world.spawn(new Location(world, loc.getBlockX() + x, world.getHighestBlockYAt(loc) + 64, loc.getBlockZ() + z), TNTPrimed.class);
|
||||
entity.setMetadata(NUKE_META_KEY, new FixedMetadataValue(ess, true));
|
||||
}
|
||||
user.sendTl("nuke");
|
||||
final Location loc = user.getLocation();
|
||||
final World world = loc.getWorld();
|
||||
if (world != null) {
|
||||
for (int x = -10; x <= 10; x += 5) {
|
||||
for (int z = -10; z <= 10; z += 5) {
|
||||
final TNTPrimed entity = world.spawn(new Location(world, loc.getBlockX() + x, world.getHighestBlockYAt(loc) + 64, loc.getBlockZ() + z), TNTPrimed.class);
|
||||
entity.setMetadata(NUKE_META_KEY, new FixedMetadataValue(ess, true));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.Console;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.messaging.IMessageRecipient;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import net.ess3.api.TranslatableException;
|
||||
import org.bukkit.Server;
|
||||
@@ -24,14 +23,8 @@ public class Commandr extends EssentialsCommand {
|
||||
final IMessageRecipient messageSender;
|
||||
if (sender.isPlayer()) {
|
||||
final User user = ess.getUser(sender.getPlayer());
|
||||
if (user.isMuted()) {
|
||||
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
throw new TranslatableException(user.hasMuteReason() ? "voiceSilencedReason" : "voiceSilenced", user.getMuteReason());
|
||||
}
|
||||
throw new TranslatableException(user.hasMuteReason() ? "voiceSilencedReasonTime" : "voiceSilencedTime", dateDiff, user.getMuteReason());
|
||||
}
|
||||
|
||||
// Muted players are handled in SimpleMessageRecipient#sendMessage so that the message can still
|
||||
// be shown to social spies before being dropped instead of being delivered to the recipient.
|
||||
message = FormatUtil.formatMessage(user, "essentials.msg", message);
|
||||
messageSender = user;
|
||||
} else {
|
||||
|
||||
@@ -13,8 +13,10 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.FurnaceRecipe;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.Recipe;
|
||||
import org.bukkit.inventory.RecipeChoice;
|
||||
@@ -158,7 +160,13 @@ public class Commandrecipe extends EssentialsCommand {
|
||||
final User user = ess.getUser(sender.getPlayer());
|
||||
user.getBase().closeInventory();
|
||||
user.setRecipeSee(true);
|
||||
final InventoryView view = user.getBase().openWorkbench(null, true);
|
||||
final InventoryView view = openWorkbench(user);
|
||||
if (view == null) {
|
||||
user.setRecipeSee(false);
|
||||
return;
|
||||
}
|
||||
final Inventory topInventory = ess.provider(InventoryViewProvider.class).getTopInventory(view);
|
||||
|
||||
final String[] recipeShape = recipe.getShape();
|
||||
final Map<Character, ItemStack> ingredientMap = recipe.getIngredientMap();
|
||||
for (int j = 0; j < recipeShape.length; j++) {
|
||||
@@ -170,7 +178,7 @@ public class Commandrecipe extends EssentialsCommand {
|
||||
if (VersionUtil.PRE_FLATTENING && item.getDurability() == Short.MAX_VALUE) {
|
||||
item.setDurability((short) 0);
|
||||
}
|
||||
ess.provider(InventoryViewProvider.class).getTopInventory(view).setItem(j * 3 + k + 1, item);
|
||||
topInventory.setItem(j * 3 + k + 1, item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -217,7 +225,11 @@ public class Commandrecipe extends EssentialsCommand {
|
||||
final User user = ess.getUser(sender.getPlayer());
|
||||
user.getBase().closeInventory();
|
||||
user.setRecipeSee(true);
|
||||
final InventoryView view = user.getBase().openWorkbench(null, true);
|
||||
final InventoryView view = openWorkbench(user);
|
||||
if (view == null) {
|
||||
user.setRecipeSee(false);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < ingredients.size(); i++) {
|
||||
final ItemStack item = ingredients.get(i);
|
||||
if (VersionUtil.PRE_FLATTENING && item.getDurability() == Short.MAX_VALUE) {
|
||||
@@ -239,6 +251,17 @@ public class Commandrecipe extends EssentialsCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private InventoryView openWorkbench(final User user) {
|
||||
final InventoryView view = user.getBase().openWorkbench(null, true);
|
||||
// If InventoryOpenEvent is canceled, the items can end up in the player's own crafting grid
|
||||
// which allows players to extract counterfeit items.
|
||||
if (view == null)
|
||||
return null;
|
||||
|
||||
final Inventory inventory = ess.provider(InventoryViewProvider.class).getTopInventory(view);
|
||||
return inventory.getType() == InventoryType.WORKBENCH ? view : null;
|
||||
}
|
||||
|
||||
public String getMaterialName(final CommandSource sender, final ItemStack stack) {
|
||||
if (stack == null) {
|
||||
return sender.tl("recipeNothing");
|
||||
|
||||
@@ -61,7 +61,7 @@ public class Commandseen extends EssentialsCommand {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ess.runTaskAsynchronously(new Runnable() {
|
||||
ess.getScheduler().runTaskAsynchronously(ess, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final User userFromBukkit = ess.getUsers().getUser(args[0]);
|
||||
|
||||
@@ -25,6 +25,10 @@ public class Commandsetwarp extends EssentialsCommand {
|
||||
throw new TranslatableException("invalidWarpName");
|
||||
}
|
||||
|
||||
if (StringUtil.isReservedFileName(args[0])) {
|
||||
throw new TranslatableException("invalidWarpName");
|
||||
}
|
||||
|
||||
final IWarps warps = ess.getWarps();
|
||||
Location warpLoc = null;
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ public class Commandskull extends EssentialsCommand {
|
||||
|
||||
final String shortNameFinal = shortOwnerName;
|
||||
|
||||
ess.scheduleEntityDelayedTask(user.getBase(), () -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
stack.setItemMeta(skullMeta);
|
||||
if (spawn) {
|
||||
Inventories.addItem(receive.getBase(), stack);
|
||||
|
||||
@@ -37,7 +37,8 @@ public class Commandspeed extends EssentialsCommand {
|
||||
final float speed;
|
||||
final boolean isBypass = user.isAuthorized("essentials.speed.bypass");
|
||||
if (args.length == 1) {
|
||||
isFly = flyPermCheck(user, user.getBase().isFlying());
|
||||
final boolean inferredFly = isFlyAlias(commandLabel) ? true : isWalkAlias(commandLabel) ? false : user.getBase().isFlying();
|
||||
isFly = flyPermCheck(user, inferredFly);
|
||||
speed = getMoveSpeed(args[0]);
|
||||
} else {
|
||||
isFly = flyPermCheck(user, isFlyMode(args[0]));
|
||||
@@ -91,6 +92,14 @@ public class Commandspeed extends EssentialsCommand {
|
||||
} else return !canWalk;
|
||||
}
|
||||
|
||||
private boolean isFlyAlias(final String label) {
|
||||
return label.contains("fly") || label.equalsIgnoreCase("fspeed") || label.equalsIgnoreCase("efspeed");
|
||||
}
|
||||
|
||||
private boolean isWalkAlias(final String label) {
|
||||
return label.contains("walk") || label.equalsIgnoreCase("wspeed") || label.equalsIgnoreCase("ewspeed");
|
||||
}
|
||||
|
||||
private boolean isFlyMode(final String modeString) throws NotEnoughArgumentsException {
|
||||
final boolean isFlyMode;
|
||||
if (modeString.contains("fly") || modeString.equalsIgnoreCase("f")) {
|
||||
|
||||
@@ -53,7 +53,7 @@ public class Commandsudo extends EssentialsLoopCommand {
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleEntityDelayedTask(user.getBase(), new SudoCommandTask());
|
||||
ess.scheduleSyncDelayedTask(new SudoCommandTask());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class Commandtempban extends EssentialsCommand {
|
||||
}
|
||||
|
||||
final String senderName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.displayName();
|
||||
ess.getServer().getBanList(BanList.Type.NAME).addBan(user.getName(), banReason, new Date(banTimestamp), senderName);
|
||||
final String expiry = DateUtil.formatDateDiff(banTimestamp);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public class Commandtempbanip extends EssentialsCommand {
|
||||
}
|
||||
|
||||
final String senderName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.displayName();
|
||||
|
||||
String ipAddress;
|
||||
if (FormatUtil.validIP(args[0])) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.CommonPlaceholders;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
@@ -21,17 +22,16 @@ public class Commandthunder extends EssentialsCommand {
|
||||
|
||||
final World world = user.getWorld();
|
||||
final boolean setThunder = args[0].equalsIgnoreCase("true");
|
||||
|
||||
if (args.length == 1) {
|
||||
ess.scheduleGlobalDelayedTask(() -> world.setThundering(setThunder));
|
||||
user.sendTl("thunder", setThunder ? user.playerTl("enabled") : user.playerTl("disabled"));
|
||||
world.setThundering(setThunder);
|
||||
user.sendTl("thunder", CommonPlaceholders.enableDisable(user.getSource(), setThunder));
|
||||
return;
|
||||
}
|
||||
|
||||
ess.scheduleGlobalDelayedTask(() -> {
|
||||
world.setThundering(setThunder);
|
||||
world.setThunderDuration(Integer.parseInt(args[1]) * 20);
|
||||
});
|
||||
user.sendTl("thunderDuration", setThunder ? user.playerTl("enabled") : user.playerTl("disabled"), Integer.parseInt(args[1]));
|
||||
world.setThundering(setThunder);
|
||||
world.setThunderDuration(Integer.parseInt(args[1]) * 20);
|
||||
user.sendTl("thunderDuration", CommonPlaceholders.enableDisable(user.getSource(), setThunder), Integer.parseInt(args[1]));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,14 +8,17 @@ import com.google.common.collect.Lists;
|
||||
import net.ess3.api.TranslatableException;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.TreeSet;
|
||||
@@ -81,16 +84,28 @@ public class Commandtime extends EssentialsCommand {
|
||||
}
|
||||
|
||||
final StringJoiner joiner = new StringJoiner(", ");
|
||||
final boolean timeAdd = add;
|
||||
for (final World world : worlds) {
|
||||
joiner.add(world.getName());
|
||||
ess.scheduleGlobalDelayedTask(() -> {
|
||||
long time = world.getTime();
|
||||
if (!timeAdd) {
|
||||
time -= time % 24000;
|
||||
// Capture intended visible time for players with relative ptime before world time changes
|
||||
final Map<Player, Long> ptimePlayers = new HashMap<>();
|
||||
for (final Player player : world.getPlayers()) {
|
||||
if (player.getPlayerTimeOffset() != 0 && player.isPlayerTimeRelative()) {
|
||||
ptimePlayers.put(player, player.getPlayerTime());
|
||||
}
|
||||
world.setTime(time + (timeAdd ? 0 : 24000) + timeTick);
|
||||
});
|
||||
}
|
||||
|
||||
long time = world.getTime();
|
||||
if (!add) {
|
||||
time -= time % 24000;
|
||||
}
|
||||
world.setTime(time + (add ? 0 : 24000) + timeTick);
|
||||
|
||||
// Re-apply ptime offsets so players maintain their intended visible time
|
||||
final long newWorldTime = world.getTime();
|
||||
for (final Map.Entry<Player, Long> entry : ptimePlayers.entrySet()) {
|
||||
entry.getKey().setPlayerTime(entry.getValue() - newWorldTime, true);
|
||||
}
|
||||
|
||||
joiner.add(world.getName());
|
||||
}
|
||||
|
||||
sender.sendTl(add ? "timeWorldAdd" : "timeWorldSet", DescParseTickFormat.formatTicks(timeTick), joiner.toString());
|
||||
|
||||
@@ -56,7 +56,7 @@ public class Commandtp extends EssentialsCommand {
|
||||
throw new NotEnoughArgumentsException(user.playerTl("teleportInvalidLocation"));
|
||||
}
|
||||
final Location locpos = new Location(user.getWorld(), x2, y2, z2, user.getLocation().getYaw(), user.getLocation().getPitch());
|
||||
user.getAsyncTeleport().now(locpos, false, TeleportCause.COMMAND, future);
|
||||
user.getAsyncTeleport().now(locpos, true, TeleportCause.COMMAND, future);
|
||||
future.thenAccept(success -> {
|
||||
if (success) {
|
||||
user.sendTl("teleporting", locpos.getWorld().getName(), locpos.getBlockX(), locpos.getBlockY(), locpos.getBlockZ());
|
||||
@@ -120,7 +120,7 @@ public class Commandtp extends EssentialsCommand {
|
||||
final User target = getPlayer(server, args, 0, true, false);
|
||||
if (args.length == 2) {
|
||||
final User toPlayer = getPlayer(server, args, 1, true, false);
|
||||
target.sendTl("teleportAtoB", Console.DISPLAY_NAME, toPlayer.getDisplayName());
|
||||
target.sendTl("teleportAtoB", Console.displayName(), toPlayer.getDisplayName());
|
||||
target.getAsyncTeleport().now(toPlayer.getBase(), false, TeleportCause.COMMAND, getNewExceptionFuture(sender, commandLabel));
|
||||
} else if (args.length > 3) {
|
||||
final double x = args[1].startsWith("~") ? target.getLocation().getX() + (args[1].length() > 1 ? Double.parseDouble(args[1].substring(1)) : 0) : Double.parseDouble(args[1]);
|
||||
|
||||
@@ -21,7 +21,7 @@ public class Commandtpoffline extends EssentialsCommand {
|
||||
final Location logout = target.getLogoutLocation();
|
||||
|
||||
if (logout == null) {
|
||||
user.sendTl("teleportOfflineUnknown", user.getDisplayName());
|
||||
user.sendTl("teleportOfflineUnknown", target.getDisplayName());
|
||||
throw new NoChargeException();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ public class Commandunban extends EssentialsCommand {
|
||||
ess.getServer().getBanList(BanList.Type.NAME).pardon(name);
|
||||
}
|
||||
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.displayName();
|
||||
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerUnbanned", senderDisplayName, name)));
|
||||
|
||||
ess.broadcastTl((IUser) null, "essentials.ban.notify", "playerUnbanned", senderDisplayName, name);
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Commandunbanip extends EssentialsCommand {
|
||||
}
|
||||
|
||||
ess.getServer().getBanList(BanList.Type.IP).pardon(ipAddress);
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME;
|
||||
final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.displayName();
|
||||
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerUnbanIpAddress", senderDisplayName, ipAddress)));
|
||||
|
||||
ess.broadcastTl((IUser) null, "essentials.banip.notify", "playerUnbanIpAddress", senderDisplayName, ipAddress);
|
||||
|
||||
@@ -30,18 +30,16 @@ public class Commandweather extends EssentialsCommand {
|
||||
isStorm = args[0].equalsIgnoreCase("storm");
|
||||
}
|
||||
|
||||
ess.scheduleGlobalDelayedTask(() -> {
|
||||
final World world = user.getWorld();
|
||||
final World world = user.getWorld();
|
||||
|
||||
if (args.length > 1) {
|
||||
world.setStorm(isStorm);
|
||||
world.setWeatherDuration(Integer.parseInt(args[1]) * 20);
|
||||
user.sendTl(isStorm ? "weatherStormFor" : "weatherSunFor", world.getName(), args[1]);
|
||||
return;
|
||||
}
|
||||
if (args.length > 1) {
|
||||
world.setStorm(isStorm);
|
||||
user.sendTl(isStorm ? "weatherStorm" : "weatherSun", world.getName());
|
||||
});
|
||||
world.setWeatherDuration(Integer.parseInt(args[1]) * 20);
|
||||
user.sendTl(isStorm ? "weatherStormFor" : "weatherSunFor", world.getName(), args[1]);
|
||||
return;
|
||||
}
|
||||
world.setStorm(isStorm);
|
||||
user.sendTl(isStorm ? "weatherStorm" : "weatherSun", world.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,16 +54,14 @@ public class Commandweather extends EssentialsCommand {
|
||||
throw new TranslatableException("weatherInvalidWorld", args[0]);
|
||||
}
|
||||
|
||||
ess.scheduleLocationDelayedTask(world.getSpawnLocation(), () -> {
|
||||
if (args.length > 2) {
|
||||
world.setStorm(isStorm);
|
||||
world.setWeatherDuration(Integer.parseInt(args[2]) * 20);
|
||||
sender.sendTl(isStorm ? "weatherStormFor" : "weatherSunFor", world.getName(), args[2]);
|
||||
return;
|
||||
}
|
||||
if (args.length > 2) {
|
||||
world.setStorm(isStorm);
|
||||
sender.sendTl(isStorm ? "weatherStorm" : "weatherSun", world.getName());
|
||||
});
|
||||
world.setWeatherDuration(Integer.parseInt(args[2]) * 20);
|
||||
sender.sendTl(isStorm ? "weatherStormFor" : "weatherSunFor", world.getName(), args[2]);
|
||||
return;
|
||||
}
|
||||
world.setStorm(isStorm);
|
||||
sender.sendTl(isStorm ? "weatherStorm" : "weatherSun", world.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -56,7 +56,7 @@ public abstract class EssentialsCommand implements IEssentialsCommand {
|
||||
//noinspection InfiniteLoopStatement
|
||||
while (true) {
|
||||
final String baseKey = name + "CommandUsage" + i;
|
||||
addUsageString(tlLiteral(baseKey), tlLiteral(baseKey + "Description"));
|
||||
addUsageString(tlLiteral(baseKey), baseKey + "Description");
|
||||
i++;
|
||||
}
|
||||
} catch (MissingResourceException ignored) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.IEssentialsModule;
|
||||
import com.earth2me.essentials.User;
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -10,6 +12,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class EssentialsTreeCommand extends EssentialsCommand {
|
||||
private final Map<String, EssentialsTreeNode> nodes = new HashMap<>();
|
||||
@@ -32,6 +35,22 @@ public class EssentialsTreeCommand extends EssentialsCommand {
|
||||
node.setParent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEssentials(final IEssentials ess) {
|
||||
super.setEssentials(ess);
|
||||
for (final EssentialsTreeNode node : new HashSet<>(nodes.values())) {
|
||||
node.setEssentials(ess);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEssentialsModule(final IEssentialsModule module) {
|
||||
super.setEssentialsModule(module);
|
||||
for (final EssentialsTreeNode node : new HashSet<>(nodes.values())) {
|
||||
node.setEssentialsModule(module);
|
||||
}
|
||||
}
|
||||
|
||||
public void runDefault(final User user, final String commandLabel) throws Exception {
|
||||
runDefault(user.getSource(), commandLabel);
|
||||
}
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ public class VersionCommand extends EssentialsTreeNode {
|
||||
);
|
||||
|
||||
public VersionCommand() {
|
||||
super("version");
|
||||
super("version", "ver");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -171,6 +171,16 @@ public class UserConfigHolder {
|
||||
this.godmode = value;
|
||||
}
|
||||
|
||||
private boolean flymode = false;
|
||||
|
||||
public boolean flyMode() {
|
||||
return this.flymode;
|
||||
}
|
||||
|
||||
public void flyMode(final boolean value) {
|
||||
this.flymode = value;
|
||||
}
|
||||
|
||||
private boolean muted = false;
|
||||
|
||||
public boolean muted() {
|
||||
|
||||
@@ -32,7 +32,7 @@ public final class EconomyLayers {
|
||||
}
|
||||
|
||||
public static void onEnable(final Essentials ess) {
|
||||
ess.scheduleInitTask(() -> {
|
||||
ess.scheduleSyncDelayedTask(() -> {
|
||||
serverStarted = true;
|
||||
for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
|
||||
if (!plugin.isEnabled()) {
|
||||
|
||||
@@ -122,7 +122,12 @@ public interface IMessageRecipient extends MailSender {
|
||||
/**
|
||||
* States that the message was <b>NOT</b> received as a result of the message pre-send event being cancelled.
|
||||
*/
|
||||
EVENT_CANCELLED;
|
||||
EVENT_CANCELLED,
|
||||
/**
|
||||
* States that the message was <b>NOT</b> delivered as a result of the sender being muted. The message may
|
||||
* still have been shown to social spies.
|
||||
*/
|
||||
SENDER_MUTED;
|
||||
|
||||
/**
|
||||
* Returns whether this response is a success. In other words equal to {@link #SUCCESS} or {@link #SUCCESS_BUT_AFK}
|
||||
|
||||
+52
-23
@@ -91,6 +91,18 @@ public class SimpleMessageRecipient implements IMessageRecipient {
|
||||
}
|
||||
|
||||
message = preSendEvent.getMessage();
|
||||
|
||||
final User senderUser = getUser(this);
|
||||
// A muted player must not have their message delivered to the recipient. However, social spies
|
||||
// may still observe the attempted message (see the socialspy-listen-muted-players setting).
|
||||
// Note: PrivateMessageSentEvent is intentionally not fired here so the message is not relayed
|
||||
// elsewhere (e.g. to Discord) as though it had actually been delivered.
|
||||
if (senderUser != null && senderUser.isMuted()) {
|
||||
sendSocialSpy(recipient, message, true);
|
||||
senderUser.notifyMuted();
|
||||
return MessageResponse.SENDER_MUTED;
|
||||
}
|
||||
|
||||
final MessageResponse messageResponse = recipient.onReceiveMessage(this.parent, message);
|
||||
switch (messageResponse) {
|
||||
case UNREACHABLE:
|
||||
@@ -114,29 +126,7 @@ public class SimpleMessageRecipient implements IMessageRecipient {
|
||||
sendTl("msgFormat", AdventureUtil.parsed(tlSender("meSender")), recipient.getDisplayName(), message);
|
||||
|
||||
// Better Social Spy
|
||||
if (ess.getSettings().isSocialSpyMessages()) {
|
||||
final User senderUser = getUser(this);
|
||||
final User recipientUser = getUser(recipient);
|
||||
if (senderUser != null // not null if player.
|
||||
// Dont spy on chats involving socialspy exempt players
|
||||
&& !senderUser.isAuthorized("essentials.chat.spy.exempt")
|
||||
&& recipientUser != null && !recipientUser.isAuthorized("essentials.chat.spy.exempt")) {
|
||||
final String senderName = ess.getSettings().isSocialSpyDisplayNames() ? getDisplayName() : getName();
|
||||
final String recipientName = ess.getSettings().isSocialSpyDisplayNames() ? recipient.getDisplayName() : recipient.getName();
|
||||
for (final User onlineUser : ess.getOnlineUsers()) {
|
||||
if (onlineUser.isSocialSpyEnabled()
|
||||
// Don't send socialspy messages to message sender/receiver to prevent spam
|
||||
&& !onlineUser.equals(senderUser)
|
||||
&& !onlineUser.equals(recipient)) {
|
||||
if (senderUser.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers()) {
|
||||
onlineUser.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(tlSender("socialSpyMutedPrefix") + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message)));
|
||||
} else {
|
||||
onlineUser.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(tlLiteral("socialSpyPrefix") + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sendSocialSpy(recipient, message, false);
|
||||
break;
|
||||
}
|
||||
// If the message was a success, set this sender's reply-recipient to the current recipient.
|
||||
@@ -150,6 +140,45 @@ public class SimpleMessageRecipient implements IMessageRecipient {
|
||||
return messageResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a private message to all online social spies, unless either party is exempt from being spied on.
|
||||
*
|
||||
* @param recipient the recipient of the private message
|
||||
* @param message the message that was sent
|
||||
* @param muted whether the sender is muted; muted messages are only shown when
|
||||
* {@code socialspy-listen-muted-players} is enabled and use a distinct prefix
|
||||
*/
|
||||
private void sendSocialSpy(final IMessageRecipient recipient, final String message, final boolean muted) {
|
||||
if (!ess.getSettings().isSocialSpyMessages()) {
|
||||
return;
|
||||
}
|
||||
if (muted && !ess.getSettings().getSocialSpyListenMutedPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final User senderUser = getUser(this);
|
||||
final User recipientUser = getUser(recipient);
|
||||
// Don't spy on chats involving socialspy exempt players.
|
||||
if (senderUser == null // not null if player.
|
||||
|| senderUser.isAuthorized("essentials.chat.spy.exempt")
|
||||
|| recipientUser == null
|
||||
|| recipientUser.isAuthorized("essentials.chat.spy.exempt")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String senderName = ess.getSettings().isSocialSpyDisplayNames() ? getDisplayName() : getName();
|
||||
final String recipientName = ess.getSettings().isSocialSpyDisplayNames() ? recipient.getDisplayName() : recipient.getName();
|
||||
final String prefix = muted ? tlSender("socialSpyMutedPrefix") : tlLiteral("socialSpyPrefix");
|
||||
for (final User onlineUser : ess.getOnlineUsers()) {
|
||||
if (onlineUser.isSocialSpyEnabled()
|
||||
// Don't send socialspy messages to message sender/receiver to prevent spam
|
||||
&& !onlineUser.equals(senderUser)
|
||||
&& !onlineUser.equals(recipient)) {
|
||||
onlineUser.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(prefix + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageResponse onReceiveMessage(final IMessageRecipient sender, final String message) {
|
||||
if (!isReachable()) {
|
||||
|
||||
@@ -28,6 +28,13 @@ public interface IPermissionsHandler {
|
||||
|
||||
boolean hasPermission(Player base, String node);
|
||||
|
||||
default boolean hasPermissionCached(Player base, String node) {
|
||||
return hasPermission(base, node);
|
||||
}
|
||||
|
||||
default void invalidatePermissionCache(UUID uuid) {
|
||||
}
|
||||
|
||||
// Does not check for * permissions
|
||||
boolean isPermissionSet(Player base, String node);
|
||||
|
||||
|
||||
@@ -105,6 +105,16 @@ public class PermissionsHandler implements IPermissionsHandler {
|
||||
return handler.hasPermission(base, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionCached(final Player base, final String node) {
|
||||
return handler.hasPermissionCached(base, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidatePermissionCache(final UUID uuid) {
|
||||
handler.invalidatePermissionCache(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(final Player base, final String node) {
|
||||
return handler.isPermissionSet(base, node);
|
||||
|
||||
@@ -7,17 +7,22 @@ import net.luckperms.api.context.ContextCalculator;
|
||||
import net.luckperms.api.context.ContextConsumer;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.event.EventSubscription;
|
||||
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
||||
import net.luckperms.api.model.group.Group;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -25,6 +30,8 @@ public class LuckPermsHandler extends ModernVaultHandler {
|
||||
private LuckPerms luckPerms;
|
||||
private Essentials ess;
|
||||
private CombinedCalculator calculator;
|
||||
private final Map<UUID, Map<String, Boolean>> permissionCache = new ConcurrentHashMap<>();
|
||||
private EventSubscription<UserDataRecalculateEvent> recalculateSubscription;
|
||||
|
||||
@Override
|
||||
public void registerContext(final String context, final Function<User, Iterable<String>> calculator, final Supplier<Iterable<String>> suggestions) {
|
||||
@@ -41,6 +48,30 @@ public class LuckPermsHandler extends ModernVaultHandler {
|
||||
this.luckPerms.getContextManager().unregisterCalculator(this.calculator);
|
||||
this.calculator = null;
|
||||
}
|
||||
if (this.recalculateSubscription != null) {
|
||||
this.recalculateSubscription.close();
|
||||
this.recalculateSubscription = null;
|
||||
}
|
||||
this.permissionCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionCached(final Player base, final String node) {
|
||||
final UUID uuid = base.getUniqueId();
|
||||
final Map<String, Boolean> userCache = permissionCache.computeIfAbsent(uuid, k -> new ConcurrentHashMap<>());
|
||||
return userCache.computeIfAbsent(node, k -> hasPermission(base, node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidatePermissionCache(final UUID uuid) {
|
||||
invalidateCache(uuid);
|
||||
}
|
||||
|
||||
public void invalidateCache(final UUID uuid) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.INFO, "Invalidating permission cache for " + uuid);
|
||||
}
|
||||
permissionCache.remove(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +93,13 @@ public class LuckPermsHandler extends ModernVaultHandler {
|
||||
|
||||
@Override
|
||||
public boolean isOfflinePermissionSet(UUID uuid, String node) {
|
||||
final net.luckperms.api.model.user.User user = this.luckPerms.getUserManager().loadUser(uuid).join();
|
||||
// Try to get the user from LuckPerms' cache first to avoid blocking the main
|
||||
// thread with a database lookup. In the context of a player join (e.g.
|
||||
// PlayerServerFullCheckEvent), the user should already be loaded.
|
||||
net.luckperms.api.model.user.User user = this.luckPerms.getUserManager().getUser(uuid);
|
||||
if (user == null) {
|
||||
user = this.luckPerms.getUserManager().loadUser(uuid).join();
|
||||
}
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -77,6 +114,11 @@ public class LuckPermsHandler extends ModernVaultHandler {
|
||||
if (provider != null) {
|
||||
this.luckPerms = provider.getProvider();
|
||||
this.ess = ess;
|
||||
this.recalculateSubscription = this.luckPerms.getEventBus().subscribe(
|
||||
ess,
|
||||
UserDataRecalculateEvent.class,
|
||||
event -> invalidateCache(event.getUser().getUniqueId())
|
||||
);
|
||||
}
|
||||
return luckPerms != null && super.tryProvider(ess);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,8 @@ public class SignDisposal extends EssentialsSign {
|
||||
|
||||
@Override
|
||||
protected boolean onSignInteract(final ISign sign, final User player, final String username, final IEssentials ess) {
|
||||
String title = (sign.getLine(1) + " " + sign.getLine(2) + " " + sign.getLine(3)).trim();
|
||||
if (title.isEmpty()) {
|
||||
title = player.playerTl("disposal");
|
||||
}
|
||||
final String customTitle = (sign.getLine(1) + " " + sign.getLine(2) + " " + sign.getLine(3)).trim();
|
||||
final String title = customTitle.isEmpty() ? ess.getAdventureFacet().miniToLegacy(player.playerTl("disposal")) : customTitle;
|
||||
player.getBase().openInventory(ess.getServer().createInventory(player.getBase(), 36, title));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -97,8 +97,9 @@ public class SignTrade extends EssentialsSign {
|
||||
stack = stack.clone();
|
||||
stack.setAmount(amount);
|
||||
final Trade store = new Trade(stack, ess);
|
||||
addAmount(sign, 2, store, ess, false);
|
||||
addAmount(sign, 2, store, ess, true);
|
||||
store.charge(player);
|
||||
addAmount(sign, 2, store, ess, false);
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ public class KeywordReplacer implements IText {
|
||||
for (final String groupName : playerList.keySet()) {
|
||||
final List<User> groupUsers = playerList.get(groupName);
|
||||
if (groupUsers != null && !groupUsers.isEmpty()) {
|
||||
outputList.put(groupName, PlayerList.listUsers(ess, groupUsers, " "));
|
||||
outputList.put(groupName, ess.getAdventureFacet().miniToLegacy(PlayerList.listUsers(ess, groupUsers, " ")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ public class KeywordReplacer implements IText {
|
||||
}
|
||||
break;
|
||||
case TPS:
|
||||
replacer = NumberUtil.formatDouble(20d); //todo
|
||||
replacer = NumberUtil.formatDouble(ess.getTimer().getAverageTPS());
|
||||
break;
|
||||
case UPTIME:
|
||||
replacer = DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime());
|
||||
@@ -380,8 +380,10 @@ public class KeywordReplacer implements IText {
|
||||
}
|
||||
|
||||
if (this.replaceSpacesWithUnderscores) {
|
||||
// Don't replace spaces with underscores in command nor escape underscores.
|
||||
if (!line.startsWith("/")) {
|
||||
// Don't replace spaces with underscores in commands, and skip escaping
|
||||
// for USERNAME since Minecraft names never contain spaces but can
|
||||
// contain underscores that must stay literal (e.g. player:{USERNAME}).
|
||||
if (!line.startsWith("/") && validKeyword != KeywordType.USERNAME) {
|
||||
replacer = replacer.replace("_", "\\_").replaceAll("\\s", "_");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,13 @@ public class ModernUserMap extends CacheLoader<UUID, User> implements IUserMap {
|
||||
|
||||
@Override
|
||||
public User getUser(final Player base) {
|
||||
final UUID uuid = base.getUniqueId();
|
||||
final User cachedUser = userCache.getIfPresent(uuid);
|
||||
if (cachedUser != null && cachedUser.getBase() == base) {
|
||||
uuidCache.updateCache(uuid, base.getName());
|
||||
return cachedUser;
|
||||
}
|
||||
|
||||
final User user = loadUncachedUser(base);
|
||||
userCache.put(user.getUUID(), user);
|
||||
debugLogCache(user);
|
||||
|
||||
@@ -167,6 +167,10 @@ public final class LocationUtil {
|
||||
return isBlockDamaging(world, x, y, z) || isBlockAboveAir(ess, world, x, y, z);
|
||||
}
|
||||
|
||||
private static boolean isBlockUnsafe(IEssentials ess, final World world, final int x, final int y, final int z, final int maxY) {
|
||||
return y >= maxY || isBlockUnsafe(ess, world, x, y, z);
|
||||
}
|
||||
|
||||
public static boolean isBlockDamaging(final World world, final int x, final int y, final int z) {
|
||||
final Material block = world.getBlockAt(x, y, z).getType();
|
||||
final Material below = world.getBlockAt(x, y - 1, z).getType();
|
||||
@@ -224,7 +228,9 @@ public final class LocationUtil {
|
||||
final World world = loc.getWorld();
|
||||
final int worldMinY = worldInfoProvider.getMinHeight(world);
|
||||
final int worldLogicalY = worldInfoProvider.getLogicalHeight(world);
|
||||
final int worldMaxY = loc.getBlockY() < worldLogicalY ? worldLogicalY : worldInfoProvider.getMaxHeight(world);
|
||||
final int worldMaxY = ess.getSettings().isConsiderWorldHeightForTeleportSafety() && loc.getBlockY() < worldLogicalY
|
||||
? worldLogicalY
|
||||
: worldInfoProvider.getMaxHeight(world);
|
||||
int x = loc.getBlockX();
|
||||
int y = (int) Math.round(loc.getY());
|
||||
int z = loc.getBlockZ();
|
||||
@@ -242,12 +248,12 @@ public final class LocationUtil {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isBlockUnsafe(ess, world, x, y, z)) {
|
||||
if (isBlockUnsafe(ess, world, x, y, z, worldMaxY)) {
|
||||
x = Math.round(loc.getX()) == origX ? x - 1 : x + 1;
|
||||
z = Math.round(loc.getZ()) == origZ ? z - 1 : z + 1;
|
||||
}
|
||||
int i = 0;
|
||||
while (isBlockUnsafe(ess, world, x, y, z)) {
|
||||
while (isBlockUnsafe(ess, world, x, y, z, worldMaxY)) {
|
||||
i++;
|
||||
if (i >= VOLUME.length) {
|
||||
x = origX;
|
||||
@@ -259,14 +265,14 @@ public final class LocationUtil {
|
||||
y = NumberUtil.constrainToRange(origY + VOLUME[i].y, worldMinY, worldMaxY);
|
||||
z = origZ + VOLUME[i].z;
|
||||
}
|
||||
while (isBlockUnsafe(ess, world, x, y, z)) {
|
||||
while (isBlockUnsafe(ess, world, x, y, z, worldMaxY)) {
|
||||
y += 1;
|
||||
if (y >= worldMaxY) {
|
||||
x += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (isBlockUnsafe(ess, world, x, y, z)) {
|
||||
while (isBlockUnsafe(ess, world, x, y, z, worldMaxY)) {
|
||||
y -= 1;
|
||||
if (y <= worldMinY + 1) {
|
||||
x += 1;
|
||||
|
||||
@@ -52,7 +52,7 @@ public final class MaterialUtil {
|
||||
BEDS = EnumUtil.getAllMatching(Material.class, "BED", "BED_BLOCK", "WHITE_BED", "ORANGE_BED",
|
||||
"MAGENTA_BED", "LIGHT_BLUE_BED", "YELLOW_BED", "LIME_BED", "PINK_BED", "GRAY_BED",
|
||||
"LIGHT_GRAY_BED", "CYAN_BED", "PURPLE_BED", "BLUE_BED", "BROWN_BED", "GREEN_BED",
|
||||
"RED_BED", "BLACK_BED");
|
||||
"RED_BED", "BLACK_BED", "STRAW_BED");
|
||||
|
||||
BANNERS = EnumUtil.getAllMatching(Material.class, "BANNER", "WHITE_BANNER",
|
||||
"ORANGE_BANNER", "MAGENTA_BANNER", "LIGHT_BLUE_BANNER", "YELLOW_BANNER", "LIME_BANNER",
|
||||
@@ -86,7 +86,8 @@ public final class MaterialUtil {
|
||||
"OAK_SIGN", "SPRUCE_SIGN",
|
||||
"CRIMSON_SIGN", "WARPED_SIGN",
|
||||
"MANGROVE_SIGN", "CHERRY_SIGN",
|
||||
"BAMBOO_SIGN", "PALE_OAK_SIGN");
|
||||
"BAMBOO_SIGN", "PALE_OAK_SIGN",
|
||||
"POPLAR_SIGN");
|
||||
|
||||
WALL_SIGNS = EnumUtil.getAllMatching(Material.class, "WALL_SIGN",
|
||||
"ACACIA_WALL_SIGN", "BIRCH_WALL_SIGN",
|
||||
@@ -94,21 +95,24 @@ public final class MaterialUtil {
|
||||
"OAK_WALL_SIGN", "SPRUCE_WALL_SIGN",
|
||||
"CRIMSON_WALL_SIGN", "WARPED_WALL_SIGN",
|
||||
"MANGROVE_WALL_SIGN", "CHERRY_WALL_SIGN",
|
||||
"BAMBOO_WALL_SIGN", "PALE_OAK_WALL_SIGN");
|
||||
"BAMBOO_WALL_SIGN", "PALE_OAK_WALL_SIGN",
|
||||
"POPLAR_WALL_SIGN");
|
||||
|
||||
HANGING_SIGNS = EnumUtil.getAllMatching(Material.class, "ACACIA_HANGING_SIGN", "BIRCH_HANGING_SIGN",
|
||||
"DARK_OAK_HANGING_SIGN", "JUNGLE_HANGING_SIGN",
|
||||
"OAK_HANGING_SIGN", "SPRUCE_HANGING_SIGN",
|
||||
"CRIMSON_HANGING_SIGN", "WARPED_HANGING_SIGN",
|
||||
"MANGROVE_HANGING_SIGN", "CHERRY_HANGING_SIGN",
|
||||
"BAMBOO_HANGING_SIGN", "PALE_OAK_HANGING_SIGN");
|
||||
"BAMBOO_HANGING_SIGN", "PALE_OAK_HANGING_SIGN",
|
||||
"POPLAR_HANGING_SIGN");
|
||||
|
||||
HANGING_WALL_SIGNS = EnumUtil.getAllMatching(Material.class, "ACACIA_WALL_HANGING_SIGN", "BIRCH_WALL_HANGING_SIGN",
|
||||
"DARK_OAK_WALL_HANGING_SIGN", "JUNGLE_WALL_HANGING_SIGN",
|
||||
"OAK_WALL_HANGING_SIGN", "SPRUCE_WALL_HANGING_SIGN",
|
||||
"CRIMSON_WALL_HANGING_SIGN", "WARPED_WALL_HANGING_SIGN",
|
||||
"MANGROVE_WALL_HANGING_SIGN", "CHERRY_WALL_HANGING_SIGN",
|
||||
"BAMBOO_WALL_HANGING_SIGN", "PALE_OAK_WALL_HANGING_SIGN");
|
||||
"BAMBOO_WALL_HANGING_SIGN", "PALE_OAK_WALL_HANGING_SIGN",
|
||||
"POPLAR_WALL_HANGING_SIGN");
|
||||
}
|
||||
|
||||
private MaterialUtil() {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.earth2me.essentials.utils;
|
||||
|
||||
import io.papermc.lib.environments.PaperEnvironment;
|
||||
import io.papermc.lib.features.asyncchunks.AsyncChunksPaper_15;
|
||||
import io.papermc.lib.features.asyncteleport.AsyncTeleportPaper_13;
|
||||
import io.papermc.lib.features.bedspawnlocation.BedSpawnLocationPaper;
|
||||
import io.papermc.lib.features.chunkisgenerated.ChunkIsGeneratedApiExists;
|
||||
|
||||
/**
|
||||
* A PaperLib environment for Paper servers whose version PaperLib cannot read.
|
||||
* <p>
|
||||
* PaperLib 1.0.6 expects a single digit major version in {@code Bukkit#getVersion()}, so it reads 26.x as version 0 and
|
||||
* selects its synchronous fallbacks for chunk loading, teleporting and bed lookups. All of those exist on every Paper
|
||||
* version that reports a year based version.
|
||||
*/
|
||||
public class ModernPaperEnvironment extends PaperEnvironment {
|
||||
public ModernPaperEnvironment() {
|
||||
asyncChunksHandler = new AsyncChunksPaper_15();
|
||||
asyncTeleportHandler = new AsyncTeleportPaper_13();
|
||||
isGeneratedHandler = new ChunkIsGeneratedApiExists();
|
||||
bedSpawnLocationHandler = new BedSpawnLocationPaper();
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,13 @@ import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public final class PasteUtil {
|
||||
private static final String PASTE_URL = "https://pastes.dev/";
|
||||
private static final String PASTE_UPLOAD_URL = "https://api.pastes.dev/post";
|
||||
private static final ExecutorService PASTE_EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
|
||||
private static volatile ExecutorService PASTE_EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private PasteUtil() {
|
||||
@@ -35,7 +36,7 @@ public final class PasteUtil {
|
||||
*/
|
||||
public static CompletableFuture<PasteResult> createPaste(List<PasteFile> pages) {
|
||||
final CompletableFuture<PasteResult> future = new CompletableFuture<>();
|
||||
PASTE_EXECUTOR_SERVICE.submit(() -> {
|
||||
getExecutor().submit(() -> {
|
||||
try {
|
||||
final HttpURLConnection connection = (HttpURLConnection) new URL(PASTE_UPLOAD_URL).openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
@@ -81,6 +82,28 @@ public final class PasteUtil {
|
||||
return future;
|
||||
}
|
||||
|
||||
private static synchronized ExecutorService getExecutor() {
|
||||
if (PASTE_EXECUTOR_SERVICE == null || PASTE_EXECUTOR_SERVICE.isShutdown() || PASTE_EXECUTOR_SERVICE.isTerminated()) {
|
||||
PASTE_EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
return PASTE_EXECUTOR_SERVICE;
|
||||
}
|
||||
|
||||
public static synchronized void shutdownExecutor() {
|
||||
final ExecutorService exec = PASTE_EXECUTOR_SERVICE;
|
||||
if (exec == null) {
|
||||
return;
|
||||
}
|
||||
exec.shutdown();
|
||||
try {
|
||||
if (!exec.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
exec.shutdownNow();
|
||||
}
|
||||
} catch (final InterruptedException ignored) {
|
||||
exec.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
public static class PasteFile {
|
||||
private final String name;
|
||||
private final String contents;
|
||||
|
||||
@@ -18,11 +18,21 @@ public final class StringUtil {
|
||||
private StringUtil() {
|
||||
}
|
||||
|
||||
private static final Pattern WINDOWS_RESERVED = Pattern.compile("^(con|prn|aux|nul|com[0-9]|lpt[0-9])$");
|
||||
|
||||
//Used to clean file names before saving to disk
|
||||
public static String sanitizeFileName(final String name) {
|
||||
return INVALIDFILECHARS.matcher(name.toLowerCase(Locale.ENGLISH)).replaceAll("_");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given name is a Windows reserved filename (e.g. CON, NUL, AUX).
|
||||
* Using these as file names on Windows will hang or crash the server.
|
||||
*/
|
||||
public static boolean isReservedFileName(final String name) {
|
||||
return WINDOWS_RESERVED.matcher(name.toLowerCase(Locale.ENGLISH)).matches();
|
||||
}
|
||||
|
||||
//Used to clean strings/names before saving as filenames/permissions
|
||||
public static String safeString(final String string) {
|
||||
if (string == null) {
|
||||
|
||||
@@ -40,17 +40,20 @@ public final class VersionUtil {
|
||||
public static final BukkitVersion v1_19_R01 = BukkitVersion.fromString("1.19-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_19_4_R01 = BukkitVersion.fromString("1.19.4-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_20_1_R01 = BukkitVersion.fromString("1.20.1-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_20_4_R01 = BukkitVersion.fromString("1.20.4-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_20_6_R01 = BukkitVersion.fromString("1.20.6-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_21_R01 = BukkitVersion.fromString("1.21-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_21_3_R01 = BukkitVersion.fromString("1.21.3-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_21_5_R01 = BukkitVersion.fromString("1.21.5-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_21_8_R01 = BukkitVersion.fromString("1.21.8-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_21_11_R01 = BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v26_1_R01 = BukkitVersion.fromString("26.1.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v26_2_R01 = BukkitVersion.fromString("26.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v26_3_R01 = BukkitVersion.fromString("26.3-R0.1-SNAPSHOT");
|
||||
|
||||
private static final Set<BukkitVersion> supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_5_R01, v1_17_1_R01, v1_18_2_R01, v1_19_4_R01, v1_20_6_R01, v1_21_11_R01);
|
||||
private static final Set<BukkitVersion> supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_5_R01, v1_17_1_R01, v1_18_2_R01, v1_19_4_R01, v1_20_6_R01, v1_21_11_R01, v26_1_R01, v26_2_R01, v26_3_R01);
|
||||
|
||||
public static final boolean PRE_FLATTENING = VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_13_0_R01);
|
||||
public static final boolean FOLIA;
|
||||
|
||||
public static final List<String> officialPlugins = Arrays.asList(
|
||||
"EssentialsAntiBuild",
|
||||
@@ -76,15 +79,6 @@ public final class VersionUtil {
|
||||
private static final String PFX = make("8(;4>`");
|
||||
|
||||
static {
|
||||
boolean isFolia;
|
||||
try {
|
||||
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
isFolia = true;
|
||||
} catch (Throwable ignored) {
|
||||
isFolia = false;
|
||||
}
|
||||
FOLIA = isFolia;
|
||||
|
||||
final ImmutableMap.Builder<String, SupportStatus> builder = new ImmutableMap.Builder<>();
|
||||
|
||||
// Leaf, yet another "High Performance" fork of Paper. Not supported by EssentialsX.
|
||||
@@ -185,7 +179,7 @@ public final class VersionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if (!supportedVersions.contains(getServerBukkitVersion())) {
|
||||
if (!isSupportedVersion(getServerBukkitVersion())) {
|
||||
return supportStatus = SupportStatus.OUTDATED;
|
||||
}
|
||||
|
||||
@@ -198,14 +192,39 @@ public final class VersionUtil {
|
||||
return supportStatusClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a version is considered supported, either by exact match in the
|
||||
* supported versions set, or by matching the base version (major.minor.patch)
|
||||
* of a supported version. This handles both the new PaperMC versioning scheme
|
||||
* (which omits the Bukkit revision suffix) and development builds that report
|
||||
* versions like {@code 26.1-rc-3.build.8-alpha}.
|
||||
*/
|
||||
private static boolean isSupportedVersion(final BukkitVersion version) {
|
||||
if (supportedVersions.contains(version)) {
|
||||
return true;
|
||||
}
|
||||
// Check if this version matches a supported base version.
|
||||
// This covers Paper versions (no -R0.1-SNAPSHOT suffix) and
|
||||
// dev variants (snapshot/pre-release/RC/Paper builds).
|
||||
for (final BukkitVersion supported : supportedVersions) {
|
||||
if (version.equalsBaseVersion(supported)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final class BukkitVersion implements Comparable<BukkitVersion> {
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-pre(\\d))?(?:-rc(\\d+))?(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?");
|
||||
private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("^(\\d{2})w(\\d{2})([a-z])(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?");
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-snapshot-(\\d+))?(?:-pre-?(\\d+))?(?:-rc-?(\\d+))?(?:\\.build\\.(\\d+)(?:-([a-z]+))?)?(?:-?R?([\\d.]+))?(?:(?:\\.local)?-SNAPSHOT)?");
|
||||
private static final Pattern LEGACY_SNAPSHOT_PATTERN = Pattern.compile("^(\\d{2})w(\\d{2})([a-z])(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?");
|
||||
|
||||
private final int major;
|
||||
private final int minor;
|
||||
private final int snapshotRelease;
|
||||
private final int preRelease;
|
||||
private final int releaseCandidate;
|
||||
private final int paperBuild;
|
||||
private final String releaseChannel;
|
||||
private final int patch;
|
||||
private final double revision;
|
||||
|
||||
@@ -214,27 +233,33 @@ public final class VersionUtil {
|
||||
private final int snapshotWeek;
|
||||
private final char snapshotLetter;
|
||||
|
||||
private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int preRelease, final int releaseCandidate) {
|
||||
private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate, final int paperBuild, final String releaseChannel) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.revision = revision;
|
||||
this.snapshotRelease = snapshotRelease;
|
||||
this.preRelease = preRelease;
|
||||
this.releaseCandidate = releaseCandidate;
|
||||
this.paperBuild = paperBuild;
|
||||
this.releaseChannel = releaseChannel;
|
||||
this.snapshot = false;
|
||||
this.snapshotYear = -1;
|
||||
this.snapshotWeek = -1;
|
||||
this.snapshotLetter = '\0';
|
||||
}
|
||||
|
||||
private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int preRelease, final int releaseCandidate,
|
||||
private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate, final int paperBuild, final String releaseChannel,
|
||||
final boolean snapshot, final int snapshotYear, final int snapshotWeek, final char snapshotLetter) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.revision = revision;
|
||||
this.snapshotRelease = snapshotRelease;
|
||||
this.preRelease = preRelease;
|
||||
this.releaseCandidate = releaseCandidate;
|
||||
this.paperBuild = paperBuild;
|
||||
this.releaseChannel = releaseChannel;
|
||||
this.snapshot = snapshot;
|
||||
this.snapshotYear = snapshotYear;
|
||||
this.snapshotWeek = snapshotWeek;
|
||||
@@ -247,11 +272,11 @@ public final class VersionUtil {
|
||||
// Try standard release format first
|
||||
Matcher matcher = VERSION_PATTERN.matcher(string);
|
||||
if (matcher.matches()) {
|
||||
return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(6), matcher.group(4), matcher.group(5));
|
||||
return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(9), matcher.group(4), matcher.group(5), matcher.group(6), matcher.group(7), matcher.group(8));
|
||||
}
|
||||
|
||||
// Try snapshot format (e.g., 25w32a-R0.1-SNAPSHOT)
|
||||
final Matcher snapshotMatcher = SNAPSHOT_PATTERN.matcher(string);
|
||||
final Matcher snapshotMatcher = LEGACY_SNAPSHOT_PATTERN.matcher(string);
|
||||
if (snapshotMatcher.matches()) {
|
||||
final int year = Integer.parseInt(snapshotMatcher.group(1));
|
||||
final int week = Integer.parseInt(snapshotMatcher.group(2));
|
||||
@@ -269,24 +294,29 @@ public final class VersionUtil {
|
||||
}
|
||||
matcher = VERSION_PATTERN.matcher(v1_16_1_R01.toString());
|
||||
Preconditions.checkArgument(matcher.matches(), string + " is not in valid version format. e.g. 1.8.8-R0.1");
|
||||
return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(6), matcher.group(4), matcher.group(5));
|
||||
return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(9), matcher.group(4), matcher.group(5), matcher.group(6), matcher.group(7), matcher.group(8));
|
||||
}
|
||||
|
||||
private static BukkitVersion from(final String major, final String minor, String patch, String revision, String preRelease, String releaseCandidate) {
|
||||
private static BukkitVersion from(final String major, final String minor, String patch, String revision, String snapshotRelease, String preRelease, String releaseCandidate, String paperBuild, final String releaseChannel) {
|
||||
if (patch == null || patch.isEmpty()) patch = "0";
|
||||
if (revision == null || revision.isEmpty()) revision = "0";
|
||||
if (snapshotRelease == null || snapshotRelease.isEmpty()) snapshotRelease = "-1";
|
||||
if (preRelease == null || preRelease.isEmpty()) preRelease = "-1";
|
||||
if (releaseCandidate == null || releaseCandidate.isEmpty()) releaseCandidate = "-1";
|
||||
if (paperBuild == null || paperBuild.isEmpty()) paperBuild = "-1";
|
||||
return new BukkitVersion(Integer.parseInt(major),
|
||||
Integer.parseInt(minor),
|
||||
Integer.parseInt(patch),
|
||||
Double.parseDouble(revision),
|
||||
Integer.parseInt(snapshotRelease),
|
||||
Integer.parseInt(preRelease),
|
||||
Integer.parseInt(releaseCandidate));
|
||||
Integer.parseInt(releaseCandidate),
|
||||
Integer.parseInt(paperBuild),
|
||||
releaseChannel);
|
||||
}
|
||||
|
||||
private static BukkitVersion fromSnapshot(final int year, final int week, final char letter, final double revision) {
|
||||
return new BukkitVersion(-1, -1, -1, revision, -1, -1, true, year, week, letter);
|
||||
return new BukkitVersion(-1, -1, -1, revision, -1, -1, -1, -1, null, true, year, week, letter);
|
||||
}
|
||||
|
||||
public boolean isHigherThan(final BukkitVersion o) {
|
||||
@@ -329,10 +359,37 @@ public final class VersionUtil {
|
||||
return releaseCandidate;
|
||||
}
|
||||
|
||||
public int getSnapshotRelease() {
|
||||
return snapshotRelease;
|
||||
}
|
||||
|
||||
public int getPaperBuild() {
|
||||
return paperBuild;
|
||||
}
|
||||
|
||||
public String getReleaseChannel() {
|
||||
return releaseChannel;
|
||||
}
|
||||
|
||||
public boolean isSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version has the same base version (major, minor, and patch)
|
||||
* as the given version, ignoring development specifiers and Bukkit metadata.
|
||||
* Revision is ignored because Paper versions do not include the Bukkit
|
||||
* revision suffix (e.g. {@code -R0.1-SNAPSHOT}).
|
||||
*/
|
||||
public boolean equalsBaseVersion(final BukkitVersion other) {
|
||||
if (this.snapshot || other.snapshot) {
|
||||
return false;
|
||||
}
|
||||
return this.major == other.major &&
|
||||
this.minor == other.minor &&
|
||||
this.patch == other.patch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
@@ -353,7 +410,9 @@ public final class VersionUtil {
|
||||
minor == that.minor &&
|
||||
patch == that.patch &&
|
||||
revision == that.revision &&
|
||||
preRelease == that.preRelease;
|
||||
snapshotRelease == that.snapshotRelease &&
|
||||
preRelease == that.preRelease &&
|
||||
releaseCandidate == that.releaseCandidate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -361,7 +420,7 @@ public final class VersionUtil {
|
||||
if (snapshot) {
|
||||
return Objects.hashCode("snapshot", snapshotYear, snapshotWeek, snapshotLetter, revision);
|
||||
}
|
||||
return Objects.hashCode(major, minor, patch, revision, preRelease, releaseCandidate);
|
||||
return Objects.hashCode(major, minor, patch, revision, snapshotRelease, preRelease, releaseCandidate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -373,15 +432,31 @@ public final class VersionUtil {
|
||||
if (patch != 0) {
|
||||
sb.append(".").append(patch);
|
||||
}
|
||||
if (snapshotRelease != -1) {
|
||||
sb.append("-snapshot-").append(snapshotRelease);
|
||||
}
|
||||
if (preRelease != -1) {
|
||||
sb.append("-pre").append(preRelease);
|
||||
sb.append("-pre-").append(preRelease);
|
||||
}
|
||||
if (releaseCandidate != -1) {
|
||||
sb.append("-rc").append(releaseCandidate);
|
||||
sb.append("-rc-").append(releaseCandidate);
|
||||
}
|
||||
if (paperBuild != -1) {
|
||||
sb.append(".build.").append(paperBuild);
|
||||
if (releaseChannel != null) {
|
||||
sb.append("-").append(releaseChannel);
|
||||
}
|
||||
}
|
||||
return sb.append("-R").append(revision).toString();
|
||||
}
|
||||
|
||||
private static int compareDevSpecifier(final int a, final int b) {
|
||||
if (a == b) return 0;
|
||||
if (a == -1) return 1;
|
||||
if (b == -1) return -1;
|
||||
return Integer.compare(a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final BukkitVersion o) {
|
||||
// Snapshots are always considered the most recent
|
||||
@@ -417,18 +492,20 @@ public final class VersionUtil {
|
||||
} else if (patch > o.patch) {
|
||||
return 1;
|
||||
} else { // equal patch
|
||||
if (preRelease < o.preRelease) {
|
||||
// For dev specifiers, -1 means absent (final release) and
|
||||
// sorts HIGHER than any present value: snapshot < pre < rc < release
|
||||
int cmp = compareDevSpecifier(snapshotRelease, o.snapshotRelease);
|
||||
if (cmp != 0) return cmp;
|
||||
cmp = compareDevSpecifier(preRelease, o.preRelease);
|
||||
if (cmp != 0) return cmp;
|
||||
cmp = compareDevSpecifier(releaseCandidate, o.releaseCandidate);
|
||||
if (cmp != 0) return cmp;
|
||||
if (paperBuild < o.paperBuild) {
|
||||
return -1;
|
||||
} else if (preRelease > o.preRelease) {
|
||||
} else if (paperBuild > o.paperBuild) {
|
||||
return 1;
|
||||
} else { // equal prerelease
|
||||
if (releaseCandidate < o.releaseCandidate) {
|
||||
return -1;
|
||||
} else if (releaseCandidate > o.releaseCandidate) {
|
||||
return 1;
|
||||
} else { // equal release candidate
|
||||
return Double.compare(revision, o.revision);
|
||||
}
|
||||
} else {
|
||||
return Double.compare(revision, o.revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@ import org.bukkit.event.HandlerList;
|
||||
* <p>
|
||||
* For other cases where the player's vanish status changes, you should listen on PlayerJoinEvent and
|
||||
* check with {@link IUser#isVanished()}.
|
||||
*
|
||||
* <b>WARNING: The values of {@link #getAffected()} and {@link #getController()} are inverted due to
|
||||
* a long-standing parameter swap. {@link #getAffected()} returns the command sender (null for console),
|
||||
* and {@link #getController()} returns the player whose vanish status changed.</b>
|
||||
*
|
||||
* @see <a href="https://github.com/EssentialsX/Essentials/issues/2604">#2604</a>
|
||||
*/
|
||||
public class VanishStatusChangeEvent extends StatusChangeEvent {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@@ -13,11 +13,13 @@ public class AsyncUserDataLoadEvent extends Event {
|
||||
|
||||
private final IUser user;
|
||||
private final String joinMessage;
|
||||
private final boolean firstJoin;
|
||||
|
||||
public AsyncUserDataLoadEvent(IUser user, String joinMessage) {
|
||||
public AsyncUserDataLoadEvent(IUser user, String joinMessage, boolean firstJoin) {
|
||||
super(true);
|
||||
this.user = user;
|
||||
this.joinMessage = joinMessage;
|
||||
this.firstJoin = firstJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,6 +36,18 @@ public class AsyncUserDataLoadEvent extends Event {
|
||||
return joinMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is the first time EssentialsX has seen this player join.
|
||||
* <p>
|
||||
* This is determined from EssentialsX's own user data rather than {@link org.bukkit.OfflinePlayer#hasPlayedBefore()},
|
||||
* which is unreliable on modern server platforms that persist player data during the login/configuration phase.
|
||||
*
|
||||
* @return {@code true} if this is the player's first recorded join, otherwise {@code false}.
|
||||
*/
|
||||
public boolean isFirstJoin() {
|
||||
return firstJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package net.essentialsx.api.v2.events;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.ess3.api.IUser;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Fired when a transaction (e.g. /pay) is about to be handled.
|
||||
*/
|
||||
public class PreTransactionEvent extends TransactionEvent implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public PreTransactionEvent(final CommandSource requester, final IUser target, final BigDecimal amount) {
|
||||
super(!Bukkit.isPrimaryThread(), requester, target, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount to be subtracted from the requester's balance.
|
||||
* <p>
|
||||
* Note: Changing this amount will not verify the requester actually has enough balance to complete the transaction.
|
||||
* @param decimal the new amount
|
||||
*/
|
||||
public void setAmount(final BigDecimal decimal) {
|
||||
Preconditions.checkNotNull(decimal, "decimal cannot be null");
|
||||
Preconditions.checkArgument(decimal.compareTo(BigDecimal.ZERO) >= 0, "decimal cannot be negative");
|
||||
|
||||
this.amount = decimal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this event should be cancelled. If cancelled, no messages will be displayed to the users involved.
|
||||
* @param cancelled whether this event should be cancelled
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,19 @@ public class TransactionEvent extends Event {
|
||||
|
||||
private final CommandSource requester;
|
||||
private final IUser target;
|
||||
private final BigDecimal amount;
|
||||
protected BigDecimal amount;
|
||||
|
||||
public TransactionEvent(CommandSource requester, IUser target, BigDecimal amount) {
|
||||
super(!Bukkit.isPrimaryThread());
|
||||
protected TransactionEvent(boolean async, CommandSource requester, IUser target, BigDecimal amount) {
|
||||
super(async);
|
||||
this.requester = requester;
|
||||
this.target = target;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public TransactionEvent(CommandSource requester, IUser target, BigDecimal amount) {
|
||||
this(!Bukkit.isPrimaryThread(), requester, target, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the user who initiated the transaction
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ ops-name-color: '4'
|
||||
nickname-prefix: '~'
|
||||
|
||||
# The maximum length allowed in nicknames. The nickname prefix is not included in this.
|
||||
max-nick-length: 15
|
||||
max-nick-length: 16
|
||||
|
||||
# The regex pattern used to determine if a requested nickname should be allowed for use.
|
||||
# If the requested nickname does not match this pattern, the nickname will be rejected.
|
||||
@@ -51,6 +51,10 @@ nick-blacklist:
|
||||
# For example, if "&6Notch" has 7 characters (2 are part of a color code), a length of 5 is used when this option is set to true.
|
||||
ignore-colors-in-max-nick-length: false
|
||||
|
||||
# When this option is enabled, any nickname set by a player will be reset when their Minecraft account name changes.
|
||||
# This prevents old nicknames (including RGB-formatted ones) from persisting after a name change.
|
||||
reset-nick-on-name-change: false
|
||||
|
||||
# When this option is enabled, display names for hidden players will not be shown. This prevents players from being
|
||||
# able to see that they are online while vanished.
|
||||
hide-displayname-in-vanish: true
|
||||
@@ -95,6 +99,10 @@ force-disable-teleport-safety: false
|
||||
# safe location. If you'd like players to be teleported to a safe location all of the time, set this option to true.
|
||||
force-safe-teleport-location: false
|
||||
|
||||
# When teleporting to an unsafe location, should Essentials consider the world's logical height when finding a safe destination?
|
||||
# This prevents safety checks from moving players above the Nether roof unless the requested destination is already above it.
|
||||
consider-world-height-for-teleport-safety: true
|
||||
|
||||
# Consider water blocks as "safe", therefore allowing players to teleport
|
||||
# using commands such as /home or /spawn to a location that is occupied by water blocks.
|
||||
is-water-safe: false
|
||||
@@ -491,7 +499,7 @@ auto-afk: 300
|
||||
auto-afk-timeout: -1
|
||||
|
||||
# A list of commands to be executed instead of kicking the player once the
|
||||
# threshold defined above in 'afk-auto-timeout' is reached. If this list is empty
|
||||
# threshold defined above in 'afk-auto-timeout' is reached. If this list is empty
|
||||
# and 'afk-auto-timeout' is not set to -1, Essentials will default to
|
||||
# kicking the player once they reach the timeout threshold.
|
||||
#
|
||||
@@ -527,6 +535,9 @@ cancel-afk-on-move: true
|
||||
# Should Essentials automatically remove AFK status when a player sends a chat message?
|
||||
cancel-afk-on-chat: true
|
||||
|
||||
# Should Essentials automatically remove AFK status when a player uses a fishing rod?
|
||||
cancel-afk-on-fish: true
|
||||
|
||||
# Should AFK players be ignored when other players are trying to sleep?
|
||||
# When this setting is false, players won't be able to skip the night if some players are AFK.
|
||||
# Players with the permission 'essentials.sleepingignored' will always be ignored.
|
||||
@@ -897,8 +908,8 @@ baltop-requirements:
|
||||
minimum-balance: 0
|
||||
minimum-playtime: 0
|
||||
|
||||
# Limit the number of cached balance top entries.
|
||||
# Recommended for servers with a large number of players, as it reduces memory usage.
|
||||
# Limit the number of cached balance top entries.
|
||||
# Recommended for servers with a large number of players, as it reduces memory usage.
|
||||
# Set to -1 to disable the limit.
|
||||
baltop-entry-limit: -1
|
||||
|
||||
@@ -1138,6 +1149,70 @@ protect:
|
||||
witch: false
|
||||
horse: false
|
||||
phantom: false
|
||||
zombified_piglin: false
|
||||
mooshroom: false
|
||||
snow_golem: false
|
||||
rabbit: false
|
||||
guardian: false
|
||||
elder_guardian: false
|
||||
endermite: false
|
||||
shulker: false
|
||||
polar_bear: false
|
||||
stray: false
|
||||
husk: false
|
||||
wither_skeleton: false
|
||||
zombie_villager: false
|
||||
skeleton_horse: false
|
||||
zombie_horse: false
|
||||
donkey: false
|
||||
mule: false
|
||||
llama: false
|
||||
evoker: false
|
||||
vex: false
|
||||
vindicator: false
|
||||
illusioner: false
|
||||
parrot: false
|
||||
turtle: false
|
||||
cod: false
|
||||
salmon: false
|
||||
pufferfish: false
|
||||
tropical_fish: false
|
||||
dolphin: false
|
||||
drowned: false
|
||||
cat: false
|
||||
fox: false
|
||||
panda: false
|
||||
pillager: false
|
||||
ravager: false
|
||||
trader_llama: false
|
||||
wandering_trader: false
|
||||
bee: false
|
||||
hoglin: false
|
||||
piglin: false
|
||||
piglin_brute: false
|
||||
strider: false
|
||||
zoglin: false
|
||||
axolotl: false
|
||||
glow_squid: false
|
||||
goat: false
|
||||
allay: false
|
||||
frog: false
|
||||
tadpole: false
|
||||
warden: false
|
||||
camel: false
|
||||
sniffer: false
|
||||
armadillo: false
|
||||
bogged: false
|
||||
breeze: false
|
||||
creaking: false
|
||||
happy_ghast: false
|
||||
copper_golem: false
|
||||
camel_husk: false
|
||||
nautilus: false
|
||||
zombie_nautilus: false
|
||||
parched: false
|
||||
sulfur_cube: false
|
||||
cushion: false
|
||||
|
||||
# The maximum height a creeper can explode. -1 allows them to explode everywhere.
|
||||
# Set 'creeper-explosion' above to true if you want to disable creeper explosions completely.
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 202 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user