13 Commits
28 changed files with 2323 additions and 639 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -0,0 +1,3 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+47 -5
View File
@@ -1,7 +1,49 @@
target
/classes
/bin
*~
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
# macOS system files
.DS_Store
.idea
# gradle build output files
build/
.gradle/
# eclipse project files
.settings/
.project
.classpath
.checkstyle
bin/
# IntelliJ files
.idea/
.kotlin
*.iml
*.ipr
*.iws
out/
# Maven leftovers
target/
+40 -6
View File
@@ -1,5 +1,20 @@
MobLimiter
===========
MobLimiter-OG
=============
TrueOG Network fork of [MobLimiter](https://github.com/NerdNu/MobLimiter). Ported to Gradle 8.14.3 / Purpur API 1.19.4 with additional population controls and admin visibility.
Changes over upstream
---------------------
* **Gradle 8.14.3 build** targeting Purpur API `1.19.4-R0.1-SNAPSHOT`. Uses [Template-OG](https://git.trueog.net/trueog/Template-OG) tooling (Spotless, Checkstyle, Shadow, Eclipse integration).
* **Tamed mob limiting** — new `limit_tamed` config flag (default `false`). When `true`, tamed mobs are no longer exempt from age / spawn / chunk-unload limiters.
* **Elder guardian limiting** — new `limit_elder_guardian` config flag (default `false`). When `true`, elder guardians are no longer exempt.
* **Vanilla persistence respect** — new `respect_persistence` flag (default `true`). Honors vanilla `PersistenceRequired` so spawn-egg, `/summon`, and trader-llama mobs are exempt from limiters, matching vanilla "never despawn" semantics. Natural-spawn mobs (spawners, mob spawning) are not persistent and remain subject to limits.
* **Armed-mob culling** — new `limit_armed` flag (default `true`). Mobs carrying armor, a main-hand item, or an off-hand item are culled instead of protected. Closes the abuse path where players drop gear to mobs to keep them loaded indefinitely (vanilla sets `PersistenceRequired` on pickup). When `true` this overrides `respect_persistence` for armed mobs. Named / tamed / elder guardian exemptions still apply.
* **Enchanted-item age extension** — new `enchanted_age_multiplier` (default `2.0`). Mobs holding or wearing any enchanted item get their configured `age` limit multiplied by this value before culling. `1.0` disables the feature; `age: -1` mobs are unaffected.
* **Debug-gated despawn console log** — every plugin-initiated despawn logs to the server console when `debug` is on, including entity type, reason (`age limit`, `chunk unload cull`, `spawn cap radius`, `spawn cap chunk`), world, and block coordinates. (Upstream's separate `despawn_log_console` option was removed in favor of gating on `debug`.)
* **Despawn player notifications** — new `despawn_notify_players` flag (default `false`) and matching permission `moblimiter.notify` (default `op`). When enabled, every despawn is broadcast to online players holding the permission.
* **Configurable notify message** — `despawn_notify_message` template with `&`-color codes and placeholders `%mob%`, `%reason%`, `%world%`, `%x%`, `%y%`, `%z%`.
Version 2 of MobLimiter, featuring three configurable Limiter engines that control the mob population in different ways.
@@ -29,6 +44,13 @@ Configuration
* `growth_ticks`: Ticks for a farm animal to grow up (-1 to disable)
* `logblock`: Enable LogBlock support. More below.
* `debug`: Print debugging info to console
* `limit_tamed`: If `true`, tamed mobs are subject to all limiters. Default `false`.
* `limit_elder_guardian`: If `true`, elder guardians are subject to all limiters. Default `false`.
* `respect_persistence`: If `true`, honor vanilla `PersistenceRequired` (spawn eggs, `/summon`, trader llamas). Default `true`.
* `limit_armed`: If `true`, cull mobs carrying armor or hand items; overrides `respect_persistence` for armed mobs. Default `true`.
* `enchanted_age_multiplier`: Multiplier applied to a mob's configured `age` when it is holding or wearing an enchanted item. Default `2.0`.
* `despawn_notify_players`: If `true`, broadcast every despawn to online players with `moblimiter.notify`. Default `false`.
* `despawn_notify_message`: Template for the notification. Supports `&`-color codes and placeholders `%mob%`, `%reason%`, `%world%`, `%x%`, `%y%`, `%z%`.
### Default Limits
@@ -106,12 +128,14 @@ MobLimiter will not remove any mobs that are deemed to be "special" in some way
The criteria include:
* Mobs with custom names, such as from a name tag
* Tamed mobs
* Elder guardians. (Regular guardians can be limited, but Elder ones won't be touched.)
* Tamed mobs *(unless `limit_tamed: true`)*
* Any mob that is holding an item, as it may have picked up a player's equipment.
* Elder guardians. (Regular guardians can be limited, but Elder ones won't be touched unless `limit_elder_guardian: true`.)
* Mobs wearing armor or holding items in their main or off hand *(unless `limit_armed: true`, the default; players would otherwise abuse this by dropping gear to mobs to keep them loaded)*. Mobs holding or wearing any enchanted item still get culled under `limit_armed: true`, but receive an extended age threshold via `enchanted_age_multiplier` (default `2.0` = double the configured age).
* Mobs with vanilla `PersistenceRequired` set, including spawn-egg placements, `/summon` commands, and trader llamas *(unless `respect_persistence: false`)*. Armed mobs are still culled when `limit_armed: true`, since picking up an item also sets `PersistenceRequired` in vanilla and would otherwise reopen the abuse path.
### LogBlock Integration
@@ -140,3 +164,13 @@ name of `MobLimiter`.
All commands can be accessed with the `moblimiter.*` permission node.
### Permissions
* `moblimiter.reload` — `/moblimiter reload`
* `moblimiter.count` — `/moblimiter count`
* `moblimiter.limits` — `/moblimiter limits`
* `moblimiter.check` — `/moblimiter check`
* `moblimiter.spawners.bypass` — Modify spawners with spawn eggs.
* `moblimiter.notify` — Receive in-game despawn notifications when `despawn_notify_players` is enabled.
* `moblimiter.*` — All of the above.
+107
View File
@@ -0,0 +1,107 @@
/* ------------------------------ Plugins ------------------------------ */
plugins {
id("java")
id("java-library")
id("com.diffplug.spotless") version "8.1.0"
id("com.gradleup.shadow") version "8.3.9"
id("checkstyle")
eclipse
}
/* --------------------------- JDK ------------------------------------- */
java {
sourceCompatibility = JavaVersion.VERSION_17
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.GRAAL_VM)
}
}
/* ----------------------------- Metadata ------------------------------ */
group = "nu.nerd"
version = "2.3.2"
val apiVersion = "1.19"
/* ----------------------------- Resources ----------------------------- */
tasks.named<ProcessResources>("processResources") {
val props = mapOf("version" to version, "apiVersion" to apiVersion)
inputs.properties(props)
filesMatching("plugin.yml") { expand(props) }
from("LICENSE") { into("/") }
}
/* ---------------------------- Repos ---------------------------------- */
repositories {
mavenCentral()
gradlePluginPortal()
maven { url = uri("https://repo.purpurmc.org/snapshots") }
maven { url = uri("https://repo.papermc.io/repository/maven-public/") }
maven { url = uri("file://${System.getProperty("user.home")}/.m2/repository") }
System.getProperty("SELF_MAVEN_LOCAL_REPO")?.let {
val dir = file(it)
if (dir.isDirectory) {
println("Using SELF_MAVEN_LOCAL_REPO at: $it")
maven { url = uri("file://${dir.absolutePath}") }
} else {
logger.error("TrueOG Bootstrap not found, defaulting to ~/.m2 for mavenLocal()")
mavenLocal()
}
} ?: logger.error("TrueOG Bootstrap not found, defaulting to ~/.m2 for mavenLocal()")
}
/* ---------------------- Java project deps ---------------------------- */
dependencies { compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT") }
apply(from = "eclipse.gradle.kts")
/* ---------------------- Reproducible jars ---------------------------- */
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
/* ----------------------------- Shadow -------------------------------- */
tasks.shadowJar {
isEnableRelocation = true
relocationPrefix = "${project.group}.shadow"
archiveClassifier.set("")
minimize()
}
tasks.jar { archiveClassifier.set("part") }
tasks.build { dependsOn(tasks.spotlessApply, tasks.shadowJar) }
/* --------------------------- Javac opts ------------------------------- */
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("-parameters")
options.isFork = true
options.compilerArgs.add("-Xlint:deprecation")
options.encoding = "UTF-8"
}
/* ----------------------------- Auto Formatting ------------------------ */
spotless {
java {
eclipse().configFile("config/formatter/eclipse-java-formatter.xml")
leadingTabsToSpaces()
removeUnusedImports()
}
kotlinGradle {
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) }
target("build.gradle.kts", "settings.gradle.kts")
}
}
checkstyle {
toolVersion = "10.18.1"
configFile = file("config/checkstyle/checkstyle.xml")
isIgnoreFailures = true
isShowViolations = true
}
tasks.named("compileJava") { dependsOn("spotlessApply") }
tasks.named("spotlessCheck") { dependsOn("spotlessApply") }
-212
View File
@@ -1,212 +0,0 @@
radius: 3
breeding_ticks: 400
growth_ticks: 400
logblock: false
relative_age: false
debug: false
defaults:
age: -1
max: 150
chunk_max: 50
cull: 4
limits:
bat:
cull: 6
blaze:
age: 12000
cull: 0
cave_spider:
age: 12000
cull: 2
chicken: []
cod: []
cow: []
creeper:
age: 24000
cull: 8
dolphin: []
ender_dragon: []
enderman:
age: 18000
cull: 0
endermite:
cull: 6
evoker: []
ghast:
cull: 1
giant:
cull: -1
guardian:
age: 12000
max: 100
chunk_max: 25
cull: 4
horse:
age: -1
illusioner: []
iron_golem: []
llama:
cull: 8
magma_cube:
age: 24000
mushroom_cow: []
ocelot:
cull: 8
parrot:
cull: 8
phantom: []
pig: []
pig_zombie:
age: 18000
pufferfish: []
rabbit:
cull: 8
salmon: []
sheep_white:
cull: 2
sheep_orange:
cull: 2
sheep_magenta:
cull: 2
sheep_light_blue:
cull: 2
sheep_yellow:
cull: 2
sheep_lime:
cull: 2
sheep_pink:
cull: 2
sheep_silver:
cull: 2
sheep_cyan:
cull: 2
sheep_purple:
cull: 2
sheep_blue:
cull: 2
sheep_brown:
cull: 2
sheep_green:
cull: 2
sheep_red:
cull: 2
sheep_black:
cull: 2
shulker: []
silverfish:
cull: 6
skeleton:
age: 24000
slime:
age: 24000
snowman:
cull: -1
spider:
age: 12000
cull: 2
squid:
age: 12000
max: 80
chunk_max: 5
cull: 1
tropical_fish: []
turtle: []
vex: []
villager:
chunk_max: 20
age: -1
vindicator: []
witch:
age: 24000
cull: 2
wither:
cull: -1
wolf:
cull: 8
zombie:
age: 24000
spawn_eggs:
- 'ALLAY_SPAWN_EGG'
- 'ARMADILLO_SPAWN_EGG'
- 'AXOLOTL_SPAWN_EGG'
- 'BAT_SPAWN_EGG'
- 'BEE_SPAWN_EGG'
- 'BLAZE_SPAWN_EGG'
- 'BOGGED_SPAWN_EGG'
- 'BREEZE_SPAWN_EGG'
- 'CAT_SPAWN_EGG'
- 'CAMEL_SPAWN_EGG'
- 'CAVE_SPIDER_SPAWN_EGG'
- 'COD_SPAWN_EGG'
- 'COW_SPAWN_EGG'
- 'CREEPER_SPAWN_EGG'
- 'DOLPHIN_SPAWN_EGG'
- 'DONKEY_SPAWN_EGG'
- 'DROWNED_SPAWN_EGG'
- 'ELDER_GUARDIAN_SPAWN_EGG'
- 'ENDER_DRAGON_SPAWN_EGG'
- 'ENDERMAN_SPAWN_EGG'
- 'ENDERMITE_SPAWN_EGG'
- 'EVOKER_SPAWN_EGG'
- 'FOX_SPAWN_EGG'
- 'FROG_SPAWN_EGG'
- 'GHAST_SPAWN_EGG'
- 'GLOW_SQUID_SPAWN_EGG'
- 'GOAT_SPAWN_EGG'
- 'GUARDIAN_SPAWN_EGG'
- 'HOGLIN_SPAWN_EGG'
- 'HORSE_SPAWN_EGG'
- 'HUSK_SPAWN_EGG'
- 'IRON_GOLEM_SPAWN_EGG'
- 'LLAMA_SPAWN_EGG'
- 'MAGMA_CUBE_SPAWN_EGG'
- 'MOOSHROOM_SPAWN_EGG'
- 'MULE_SPAWN_EGG'
- 'OCELOT_SPAWN_EGG'
- 'PANDA_SPAWN_EGG'
- 'PARROT_SPAWN_EGG'
- 'PHANTOM_SPAWN_EGG'
- 'PIG_SPAWN_EGG'
- 'PIGLIN_SPAWN_EGG'
- 'PIGLIN_BRUTE_SPAWN_EGG'
- 'PILLAGER_SPAWN_EGG'
- 'POLAR_BEAR_SPAWN_EGG'
- 'PUFFERFISH_SPAWN_EGG'
- 'RABBIT_SPAWN_EGG'
- 'RAVAGER_SPAWN_EGG'
- 'SALMON_SPAWN_EGG'
- 'SHEEP_SPAWN_EGG'
- 'SHULKER_SPAWN_EGG'
- 'SILVERFISH_SPAWN_EGG'
- 'SKELETON_SPAWN_EGG'
- 'SKELETON_HORSE_SPAWN_EGG'
- 'SLIME_SPAWN_EGG'
- 'SNIFFER_SPAWN_EGG'
- 'SNOW_GOLEM_SPAWN_EGG'
- 'SPIDER_SPAWN_EGG'
- 'SQUID_SPAWN_EGG'
- 'STRAY_SPAWN_EGG'
- 'STRIDER_SPAWN_EGG'
- 'TADPOLE_SPAWN_EGG'
- 'TRADER_LLAMA_SPAWN_EGG'
- 'TROPICAL_FISH_SPAWN_EGG'
- 'TURTLE_SPAWN_EGG'
- 'VEX_SPAWN_EGG'
- 'VILLAGER_SPAWN_EGG'
- 'VINDICATOR_SPAWN_EGG'
- 'WANDERING_TRADER_SPAWN_EGG'
- 'WARDEN_SPAWN_EGG'
- 'WITCH_SPAWN_EGG'
- 'WITHER_SPAWN_EGG'
- 'WITHER_SKELETON_SPAWN_EGG'
- 'WOLF_SPAWN_EGG'
- 'ZOGLIN_SPAWN_EGG'
- 'ZOMBIE_SPAWN_EGG'
- 'ZOMBIE_HORSE_SPAWN_EGG'
- 'ZOMBIE_VILLAGER_SPAWN_EGG'
- 'ZOMBIFIED_PIGLIN_SPAWN_EGG'
+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="TreeWalker">
<module name="FinalLocalVariable"/>
<module name="AvoidStarImport"/>
<module name="Indentation">
<property name="severity" value="ignore"/>
</module>
</module>
<module name="RegexpSingleline">
<property name="format" value="\borg\.junit\.jupiter\.api\.Assertions\.fail\s*\(\s*&quot;Not yet implemented&quot;\s*\)\s*;"/>
<property name="message" value="Remove placeholder fail() and implement the test."/>
<property name="ignoreCase" value="true"/>
<property name="severity" value="warning"/>
</module>
<module name="RegexpSingleline">
<property name="format" value="(?&lt;!\w)fail\s*\(\s*&quot;Not yet implemented&quot;\s*\)\s*;"/>
<property name="message" value="Remove placeholder fail() and implement the test."/>
<property name="severity" value="warning"/>
</module>
</module>
+404
View File
@@ -0,0 +1,404 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="23">
<profile kind="CodeFormatterProfile" name="TrueOG Network Formatting" version="23">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case_after_arrow" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="20"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>
+158
View File
@@ -0,0 +1,158 @@
/* This is free and unencumbered software released into the public domain */
import org.gradle.api.tasks.bundling.Jar
import org.gradle.plugins.ide.eclipse.model.EclipseModel
import java.nio.file.Files
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
val sourcePluginProjects =
configurations
.getByName("compileClasspath")
.allDependencies
.filterIsInstance<ProjectDependency>()
.map { it.path }
.filter { it.startsWith(":libs:") }
.distinct()
val sourcePluginProjectNames = sourcePluginProjects.map { it.substringAfterLast(":") }.toSet()
sourcePluginProjects.forEach { evaluationDependsOn(it) }
val ideLibs: Configuration by
configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
}
configure<EclipseModel> {
project {
name = "${rootProject.name}-Plugin"
}
classpath {
plusConfigurations.add(ideLibs)
}
}
val ideLibDir = layout.buildDirectory.dir("ide-libs")
val hashRegex = Regex("-[0-9a-fA-F]{10}(?=\\.jar$)") // Black magic.
val copyTasks = mutableListOf<TaskProvider<Copy>>()
sourcePluginProjects.forEach { path ->
val sub = project(path)
val archiveTaskName = if ("shadowJar" in sub.tasks.names) "shadowJar" else "jar"
val archiveTask = sub.tasks.named<Jar>(archiveTaskName)
val copyTask =
tasks.register<Copy>("ideCopy${sub.name.replaceFirstChar(Char::titlecase)}") {
dependsOn(archiveTask)
from(archiveTask.flatMap { it.archiveFile })
into(ideLibDir)
rename { it.replace(hashRegex, "") } // Remove git commit hash from jarfile.
}
copyTasks += copyTask
}
/* Ensure the jars exist before .classpath is generated */
tasks.named("eclipseClasspath").configure { dependsOn(copyTasks) }
/* Supply those jars to the ideLibs configuration (after evaluation) */
afterEvaluate { dependencies { add("ideLibs", fileTree(ideLibDir) { include("*.jar") }) } }
val injectIdeLibs =
tasks.register("injectIdeLibs") {
dependsOn("eclipse") // run after all eclipse files are generated
doLast {
val cpFile = file(".classpath")
if (!cpFile.exists()) return@doLast
val jars = fileTree(ideLibDir.get()) { include("*.jar") }.files.sortedBy { it.name }
if (jars.isEmpty()) return@doLast
// Parse DOM
val dbf = DocumentBuilderFactory.newInstance()
val doc = dbf.newDocumentBuilder().parse(cpFile)
val root = doc.documentElement
// Helper to see if an entry already exists
fun exists(path: String): Boolean =
root.getElementsByTagName("classpathentry").let { list ->
(0 until list.length).any { i ->
val n = list.item(i)
val kind = n.attributes?.getNamedItem("kind")?.nodeValue
val p = n.attributes?.getNamedItem("path")?.nodeValue
kind == "lib" && p == path
}
}
// Remove any entry that points to ide-libs dir or unresolved :libs: project refs.
val toRemove = mutableListOf<org.w3c.dom.Node>()
val list = root.getElementsByTagName("classpathentry")
val dirPath = ideLibDir.get().asFile.absolutePath
for (i in 0 until list.length) {
val n = list.item(i)
val kind = n.attributes?.getNamedItem("kind")?.nodeValue
val p = n.attributes?.getNamedItem("path")?.nodeValue ?: ""
val isIdeLibEntry = kind == "lib" && (p == dirPath || p.startsWith("$dirPath/"))
val sourceProjectPath = p.removePrefix("/")
val isHalfImportedSourceModule = kind == "src" && p.startsWith("/libs:")
val isSourcePluginModule = kind == "src" && sourceProjectPath in sourcePluginProjectNames
if (isIdeLibEntry || isHalfImportedSourceModule || isSourcePluginModule) {
toRemove += n
}
}
toRemove.forEach { root.removeChild(it) }
// Append our jar entries LAST
jars.forEach { f ->
val abs = f.absolutePath
if (!exists(abs)) {
val entry = doc.createElement("classpathentry")
entry.setAttribute("kind", "lib")
entry.setAttribute("path", abs)
root.appendChild(entry)
}
}
// Write back pretty
val tf =
TransformerFactory.newInstance().newTransformer().apply {
setOutputProperty(OutputKeys.INDENT, "yes")
setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "1")
setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no")
setOutputProperty(OutputKeys.ENCODING, "UTF-8")
}
Files.newBufferedWriter(cpFile.toPath()).use { w -> tf.transform(DOMSource(doc), StreamResult(w)) }
}
}
tasks.named("eclipse").configure { finalizedBy(injectIdeLibs) }
val eclipseModel = extensions.getByType<EclipseModel>()
fun Project.addResolvableEclipseConfigs() {
val jarAttr =
objects.named(org.gradle.api.attributes.LibraryElements::class, org.gradle.api.attributes.LibraryElements.JAR)
val apiAttr = objects.named(org.gradle.api.attributes.Usage::class, org.gradle.api.attributes.Usage.JAVA_API)
val compileOnlyRes =
configurations.create("eclipseCompileOnly") {
extendsFrom(configurations.getByName("compileOnly"))
isCanBeResolved = true
isCanBeConsumed = false
attributes.attribute(org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE, apiAttr)
attributes.attribute(org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarAttr)
}
val compileOnlyApiRes =
configurations.create("eclipseCompileOnlyApi") {
extendsFrom(configurations.getByName("compileOnlyApi"))
isCanBeResolved = true
isCanBeConsumed = false
attributes.attribute(org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE, apiAttr)
attributes.attribute(org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarAttr)
}
eclipseModel.classpath.plusConfigurations.addAll(listOf(compileOnlyRes, compileOnlyApiRes))
}
addResolvableEclipseConfigs()
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+251
View File
@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+94
View File
@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-64
View File
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>nu.nerd</groupId>
<artifactId>MobLimiter</artifactId>
<version>2.3.1</version>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>md_5-releases</id>
<url>http://repo.md-5.net/content/repositories/releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${basedir}/src</sourceDirectory>
<resources>
<resource>
<targetPath>.</targetPath>
<filtering>true</filtering>
<directory>.</directory>
<includes>
<include>plugin.yml</include>
<include>config.yml</include>
</includes>
</resource>
</resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
+1
View File
@@ -0,0 +1 @@
rootProject.name = "MobLimiter-OG"
@@ -1,14 +1,8 @@
package nu.nerd.moblimiter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.md_5.bungee.api.ChatColor;
import nu.nerd.moblimiter.configuration.ConfiguredDefaults;
import nu.nerd.moblimiter.configuration.ConfiguredMob;
import org.bukkit.Chunk;
@@ -21,32 +15,47 @@ import java.util.*;
public class CommandHandler implements CommandExecutor {
private MobLimiter plugin;
public CommandHandler() {
plugin = MobLimiter.instance;
plugin.getCommand("moblimiter").setExecutor(this);
}
// --------------------------------------------------------------------------------------------
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
reloadConfig(sender);
} else if (args.length > 0 && args[0].equalsIgnoreCase("count")) {
countCommand(sender);
} else if (args.length > 0 && args[0].equalsIgnoreCase("limits")) {
limitsCommand(sender, args);
} else if (args.length > 0 && args[0].equalsIgnoreCase("check")) {
checkCommand(sender);
} else if (args.length > 0 && args[0].equalsIgnoreCase("help")) {
helpText(sender, cmd, args);
} else {
commandsList(sender);
}
return true;
}
// --------------------------------------------------------------------------------------------
@@ -55,21 +64,25 @@ public class CommandHandler implements CommandExecutor {
* Command to reload the configuration
*/
private void reloadConfig(CommandSender sender) {
if (!sender.hasPermission("moblimiter.reload")) {
sender.sendMessage(Component.text("You don't have permission to do this.")
.color(TextColor.fromHexString("#FF5555")));
sender.sendMessage(
Component.text("You don't have permission to do this.").color(TextColor.fromHexString("#FF5555")));
return;
}
plugin.getConfiguration().load();
sender.sendMessage(Component.text("MobLimiter config reloaded!")
.color(TextColor.fromHexString("#FFAA00")));
sender.sendMessage(Component.text("MobLimiter config reloaded!").color(TextColor.fromHexString("#FFAA00")));
}
// --------------------------------------------------------------------------------------------
/**
* When a player runs /moblimiter with no arguments, list available commands
* and guide newbies to /moblimiter help
* When a player runs /moblimiter with no arguments, list available commands and
* guide newbies to /moblimiter help
*
* @param sender
*/
@@ -80,22 +93,35 @@ public class CommandHandler implements CommandExecutor {
helpMessage = helpMessage.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6/moblimiter help&r - What is MobLimiter and how does it work?"));
if (sender.hasPermission("moblimiter.limits")) {
helpMessage = helpMessage.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6/moblimiter limits&r - List the limits for each mob type"));
}
if (sender.hasPermission("moblimiter.count")) {
helpMessage = helpMessage.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6/moblimiter count&r - Count entities in the current chunk and radius"));
}
if (sender.hasPermission("moblimiter.check")) {
helpMessage = helpMessage.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6/moblimiter check&r - Inspect limiting details for the mob you're looking at"));
}
if (sender.hasPermission("moblimiter.reload")) {
helpMessage = helpMessage.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6/moblimiter reload&r - Reload configuration from disk"));
}
sender.sendMessage(helpMessage);
}
// --------------------------------------------------------------------------------------------
@@ -107,57 +133,74 @@ public class CommandHandler implements CommandExecutor {
int num = 1;
if (args.length > 1) {
try {
num = Integer.parseInt(args[1]);
} catch (NumberFormatException ex) {
num = 1;
}
}
if (num == 1) {
Component helpMessage = LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6This server runs a plugin called &eMobLimiter" +
" &6in order to manage the amount of mobs on the server, as too many mobs creates lag" +
" which affects everyone. MobLimiter uses several methods to manage the mob population," +
" depending on how it is configured:").appendNewline();
helpMessage = helpMessage.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&e1. Entity unload culling"))
.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6Mobs are culled down to a defined maximum" +
" whenever a cluster of entities unloads or the server restarts."))
Component helpMessage = LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6This server runs a plugin called &eMobLimiter"
+ " &6in order to manage the amount of mobs on the server, as too many mobs creates lag"
+ " which affects everyone. MobLimiter uses several methods to manage the mob population,"
+ " depending on how it is configured:")
.appendNewline();
helpMessage = helpMessage.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&e2. Real-time spawn limiting"))
.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6Mobs are tracked in real-time and new spawns" +
" are prevented in an area if there are more than a certain amount" +
" of that mob type there already."));
helpMessage = helpMessage
.append(LegacyComponentSerializer.legacyAmpersand().deserialize("&e1. Entity unload culling"))
.appendNewline()
.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6Mobs are culled down to a defined maximum"
+ " whenever a cluster of entities unloads or the server restarts."))
.appendNewline();
helpMessage = helpMessage
.append(LegacyComponentSerializer.legacyAmpersand().deserialize("&e2. Real-time spawn limiting"))
.appendNewline()
.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6Mobs are tracked in real-time and new spawns"
+ " are prevented in an area if there are more than a certain amount"
+ " of that mob type there already."));
sender.sendMessage(helpMessage);
}
if (num == 2) {
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&e3. Age limiting").appendNewline()
.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6Mobs automatically die when they reach a certain age," +
" dropping their items when they do. " +
"e.g. a skeleton may live for 15 minutes before dying."))
.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6In all cases, \"special\" mobs are always protected" +
" and will not be removed by the plugin. This includes mobs with" +
" custom names, tamed animals, and mobs wearing armor."))
.appendNewline().append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("To make up for the culling of passive farm animals," +
" the plugin shortens the growth rate of baby animals and the" +
" waiting time in between breeding events, to approximately &7" +
(plugin.getConfiguration().getGrowthTicks() / 20) + " &6and &7" +
(plugin.getConfiguration().getBreedingTicks() / 20) + " &6seconds " +
"respectively.")));
sender.sendMessage(
LegacyComponentSerializer.legacyAmpersand().deserialize("&e3. Age limiting").appendNewline()
.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6Mobs automatically die when they reach a certain age,"
+ " dropping their items when they do. "
+ "e.g. a skeleton may live for 15 minutes before dying."))
.appendNewline()
.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("&6In all cases, \"special\" mobs are always protected"
+ " and will not be removed by the plugin. This includes mobs with"
+ " custom names, tamed animals, and mobs wearing armor."))
.appendNewline()
.append(LegacyComponentSerializer.legacyAmpersand()
.deserialize("To make up for the culling of passive farm animals,"
+ " the plugin shortens the growth rate of baby animals and the"
+ " waiting time in between breeding events, to approximately &7"
+ (plugin.getConfiguration().getGrowthTicks() / 20) + " &6and &7"
+ (plugin.getConfiguration().getBreedingTicks() / 20) + " &6seconds "
+ "respectively.")));
}
sender.sendMessage(Component.text(String.format("[Page %d/2 - /moblimiter help #]", num)));
}
// --------------------------------------------------------------------------------------------
@@ -167,11 +210,15 @@ public class CommandHandler implements CommandExecutor {
*/
private void countCommand(CommandSender sender) {
if (!sender.hasPermission("moblimiter.count")) return;
if (!sender.hasPermission("moblimiter.count"))
return;
if (!(sender instanceof Player)) {
sender.sendMessage("Console can't do that.");
return;
}
Player player = (Player) sender;
Chunk playerChunk = player.getLocation().getChunk();
int chunkRadius = plugin.getConfiguration().getRadius();
@@ -194,21 +241,23 @@ public class CommandHandler implements CommandExecutor {
/**
* Outputs the number of mobs nearby
* @param map A hashmap of mob names and counts
*
* @param map A hashmap of mob names and counts
* @param header
* @return
*/
private Component countMessager(HashMap<String, Integer> map, String header) {
Component countMessage = LegacyComponentSerializer.legacyAmpersand().deserialize(header);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
countMessage = countMessage.append(Component.text(
"&f" +
entry.getKey().toLowerCase() +
": &7" +
entry.getValue() +
" "));
countMessage = countMessage
.append(Component.text("&f" + entry.getKey().toLowerCase() + ": &7" + entry.getValue() + " "));
}
return countMessage;
}
// --------------------------------------------------------------------------------------------
@@ -217,38 +266,61 @@ public class CommandHandler implements CommandExecutor {
* Command to display the configured limits
*/
private void limitsCommand(CommandSender sender, String[] args) {
if (!sender.hasPermission("moblimiter.limits")) return;
if (!sender.hasPermission("moblimiter.limits"))
return;
List<String> lines = new ArrayList<>();
ConfiguredDefaults d = plugin.getConfiguration().getDefaults();
String values = String.format("Age: %d Max: %d Chunk: %d Cull: %d", d.getAge(),
d.getMax(), d.getChunkMax(), d.getCull());
String values = String.format("Age: %d Max: %d Chunk: %d Cull: %d", d.getAge(), d.getMax(), d.getChunkMax(),
d.getCull());
lines.add(String.format("%s%s %s[%s]", "&9", "DEFAULT", "&e", values));
TreeMap<String, ConfiguredMob> limits = new TreeMap<>(plugin.getConfiguration().getAllLimits());
for (ConfiguredMob l : limits.values()) {
values = String.format("Age: %d Max: %d Chunk: %d Cull: %d",
l.getAge(), l.getMax(), l.getChunkMax(), l.getCull());
values = String.format("Age: %d Max: %d Chunk: %d Cull: %d", l.getAge(), l.getMax(), l.getChunkMax(),
l.getCull());
lines.add(String.format("%s%s %s[%s]", "&6", l.getKey(), "&e", values));
}
int page = 1;
if (args.length > 1) {
try {
page = Integer.parseInt(args[1]);
} catch (NumberFormatException ex) {
page = 1;
}
}
if (lines.size() > 0) {
int offset = (page - 1) * 10;
int pages = ((lines.size() - 1) / 10) + 1;
for (int i = offset; i <= offset + 9; i++) {
if (i >= lines.size()) break;
if (i >= lines.size())
break;
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(lines.get(i)));
}
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(String.format("%s[Page %d/%d]", "&7", page, pages)));
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand()
.deserialize(String.format("%s[Page %d/%d]", "&7", page, pages)));
} else {
sender.sendMessage(Component.text("There are no limits configured. All mob types will fall back" +
" to the default block.").color(TextColor.fromHexString("#FF5555")));
sender.sendMessage(Component
.text("There are no limits configured. All mob types will fall back" + " to the default block.")
.color(TextColor.fromHexString("#FF5555")));
}
}
// --------------------------------------------------------------------------------------------
@@ -258,17 +330,22 @@ public class CommandHandler implements CommandExecutor {
*/
private void checkCommand(CommandSender sender) {
if (!sender.hasPermission("moblimiter.check")) return;
if (!sender.hasPermission("moblimiter.check"))
return;
if (!(sender instanceof Player)) {
sender.sendMessage("Console can't do that.");
return;
}
Player player = (Player) sender;
LivingEntity entity = EntityHelper.getMobInLineOfSight(player);
if (entity == null || entity.isDead() || !EntityHelper.isLimitableMob(entity)) {
sender.sendMessage("No mob in sight");
return;
}
ConfiguredMob limits = plugin.getConfiguration().getLimits(entity);
@@ -285,25 +362,33 @@ public class CommandHandler implements CommandExecutor {
boolean moreCanSpawn = canSpawnChunk && canSpawnNearby;
sender.sendMessage(Component.text("---").color(TextColor.fromHexString("#FFAA00")));
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(
String.format("&7 (%d/%d nearby, %d/%d in chunk)", nearby, limits.getMax(), inChunk,
limits.getChunkMax())));
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(String
.format("&7 (%d/%d nearby, %d/%d in chunk)", nearby, limits.getMax(), inChunk, limits.getChunkMax())));
String ageStr;
if (limits.getAge() > -1) {
if (!EntityHelper.isBreedingPair(entity)) {
ageStr = String.format("%d/%d", plugin.getAgeLimiter().adjustedAge(entity), limits.getAge());
} else {
ageStr = "Breeding Pair Exempted";
}
} else {
ageStr = "Not Limited";
}
String chunkCullStr = (limits.getCull() > -1 && isCullable) ? String.format("%d", limits.getCull()) : "Unlimited";
String chunkCullStr = (limits.getCull() > -1 && isCullable) ? String.format("%d", limits.getCull())
: "Unlimited";
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand()
.deserialize(String.format("Age: %s%s", "&7", ageStr)));
sender.sendMessage(
LegacyComponentSerializer.legacyAmpersand().deserialize(String.format("Age: %s%s", "&7", ageStr)));
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand()
.deserialize(String.format("Is Special: %s%b", "&7", isSpecial)));
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand()
@@ -315,5 +400,4 @@ public class CommandHandler implements CommandExecutor {
}
}
@@ -1,6 +1,8 @@
package nu.nerd.moblimiter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import nu.nerd.moblimiter.configuration.Configuration;
import nu.nerd.moblimiter.configuration.ConfiguredMob;
import org.bukkit.*;
@@ -17,187 +19,382 @@ import java.util.*;
*/
public class EntityHelper {
private static final HashSet<EntityType> BLACKLIST = new HashSet<>(Arrays.asList(
EntityType.ARMOR_STAND, EntityType.PLAYER
));
private static final HashSet<EntityType> BLACKLIST = new HashSet<>(
Arrays.asList(EntityType.ARMOR_STAND, EntityType.PLAYER));
/**
* Check if this is a mob that should ever be limited.
* Merely checking Animals or Monster subclassing is not sufficient due to Bukkit inconsistency.
* e.g. Armor Stands are LivingEntities, apparently.
* So to check whether something is a limitable mob, we check if it's a LivingEntity first and then
* apply a blacklist of edge cases such as Armor Stands.
* Check if this is a mob that should ever be limited. Merely checking Animals
* or Monster subclassing is not sufficient due to Bukkit inconsistency. e.g.
* Armor Stands are LivingEntities, apparently. So to check whether something is
* a limitable mob, we check if it's a LivingEntity first and then apply a
* blacklist of edge cases such as Armor Stands.
*
* @param entity The entity to check
* @return true if the entity is a limitable mob
*/
public static boolean isLimitableMob(Entity entity) {
return entity instanceof LivingEntity && !BLACKLIST.contains(entity.getType());
}
/**
* Check if this is a "special mob" that shouldn't be removed in any circumstance
* Check if this is a "special mob" that shouldn't be removed in any
* circumstance
*
* @param entity entity to check
* @return true if this is a special mob
*/
public static boolean isSpecialMob(LivingEntity entity) {
Configuration cfg = getConfiguration();
// Keep mobs with custom names
if (entity.customName() != null) {
return true;
}
// Don't remove tamed mobs
if (entity instanceof Tameable) {
// Don't remove tamed mobs (unless limit_tamed is enabled)
if (!cfg.limitTamed() && entity instanceof Tameable) {
Tameable tameable = (Tameable) entity;
if (tameable.isTamed()) {
return true;
}
}
// Save the sponge!
if (entity.getType() == EntityType.ELDER_GUARDIAN) {
// Save the sponge! (unless limit_elder_guardian is enabled)
if (!cfg.limitElderGuardian() && entity.getType() == EntityType.ELDER_GUARDIAN) {
return true;
}
// Don't remove mobs that are holding something, which they may have picked up
EntityEquipment equipment = entity.getEquipment();
for (ItemStack armor : equipment.getArmorContents()) {
// Unarmored mobs, even animals, spawn with 1xAIR as armor.
if (armor != null && armor.getType() != Material.AIR) {
return true;
}
// Equipped mobs (armor or hand items) are handled specially. When
// limit_armed is true (default), armed mobs are culled and this
// overrides respect_persistence — picking up an item sets vanilla
// PersistenceRequired, which is the exact abuse path the toggle closes.
boolean armed = hasEquippedItem(entity);
if (armed) {
return !cfg.limitArmed();
}
// Don't cull allays who are holding items
if(entity.getType() == EntityType.ALLAY) {
if(equipment.getItemInMainHand() != null && equipment.getItemInMainHand().getType() != Material.AIR) {
return true;
}
// Honor vanilla PersistenceRequired for unarmed mobs: spawn-egg,
// /summon, trader llamas, etc. Natural-spawn mobs are not persistent,
// so farm cycling is unaffected.
if (cfg.respectPersistence() && entity.isPersistent()) {
return true;
}
return false;
}
/**
* True if the mob has any non-AIR item in armor slots, main hand, or off hand.
*/
private static boolean hasEquippedItem(LivingEntity entity) {
EntityEquipment equipment = entity.getEquipment();
if (equipment == null) {
return false;
}
for (ItemStack armor : equipment.getArmorContents()) {
if (armor != null && armor.getType() != Material.AIR) {
return true;
}
}
ItemStack main = equipment.getItemInMainHand();
if (main != null && main.getType() != Material.AIR) {
return true;
}
ItemStack off = equipment.getItemInOffHand();
if (off != null && off.getType() != Material.AIR) {
return true;
}
return false;
}
/**
* If this is an Animal and there are two or less in the chunk, don't remove them.
* This protects breeding pairs for farm animals.
* True if the mob has any enchanted item in armor slots, main hand, or off
* hand. Used to grant a longer age limit via enchanted_age_multiplier.
*/
public static boolean hasEnchantedItem(LivingEntity entity) {
EntityEquipment equipment = entity.getEquipment();
if (equipment == null) {
return false;
}
for (ItemStack armor : equipment.getArmorContents()) {
if (isEnchanted(armor)) {
return true;
}
}
return isEnchanted(equipment.getItemInMainHand()) || isEnchanted(equipment.getItemInOffHand());
}
private static boolean isEnchanted(ItemStack item) {
if (item == null || item.getType() == Material.AIR) {
return false;
}
return !item.getEnchantments().isEmpty();
}
/**
* If this is an Animal and there are two or less in the chunk, don't remove
* them. This protects breeding pairs for farm animals.
*
* @param entity the entity to check
* @return false if the entity doesn't match the criteria
*/
public static boolean isBreedingPair(Entity entity) {
if (!(entity instanceof Animals)) return false;
if (entity instanceof Tameable) return false;
if (!(entity instanceof Animals))
return false;
if (entity instanceof Tameable)
return false;
int count = 0;
ConfiguredMob mob = getConfiguration().getLimits(entity);
for (Entity e : entity.getLocation().getChunk().getEntities()) {
ConfiguredMob m = getConfiguration().getLimits(e);
if (m.getKey().equals(mob.getKey()) && !e.isDead()) {
count++;
}
}
return count < 3;
}
count++;
}
}
return count < 3;
}
/**
* Return details about an entity for debugging
*
* @param entity the entity
* @return String representation of the mob name and location
*/
public static String getMobDescription(Entity entity) {
String type = entity.getType().toString();
String world = entity.getLocation().getWorld().getName();
Location loc = entity.getLocation();
return String.format("%s at (%s,%d,%d,%d)", type, world, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
}
}
/**
* Return the mob a player is looking at
*
* @param player The player to check
* @return null or LivingEntity
*/
public static LivingEntity getMobInLineOfSight(Player player) {
List<Entity> entities = player.getNearbyEntities(5, 1, 5);
Iterator<Entity> iterator = entities.iterator();
while (iterator.hasNext()) {
Entity ent = iterator.next();
if (!(ent instanceof LivingEntity)) iterator.remove();
if (!(ent instanceof LivingEntity))
iterator.remove();
}
Set<Material> nullSet = null;
for (Block block : player.getLineOfSight(nullSet, 6)) {
if (block.getType() != Material.AIR) break; //view is obstructed
if (block.getType() != Material.AIR)
break; // view is obstructed
for (Entity ent : entities) {
Vector b = block.getLocation().toVector();
Vector head = ent.getLocation().toVector().add(new Vector(0, 1, 0));
Vector foot = ent.getLocation().toVector();
if (head.isInSphere(b, 1.25) || foot.isInSphere(b, 1.25)) {
return (LivingEntity) ent;
}
}
}
return null;
}
return (LivingEntity) ent;
}
}
}
return null;
}
/**
* Build a HashMap of entity types and their counts in a chunk
*
* @param chunk The chunk to check
* @return A HashMap summarizing the entity breakdown of a chunk
*/
public static HashMap<String, Integer> summarizeMobsInChunk(Chunk chunk) {
HashMap<String, Integer> chunkCounts = new HashMap<String, Integer>();
for (Entity e : chunk.getEntities()) {
if (e.isDead() || !isLimitableMob(e)) continue;
if (e.isDead() || !isLimitableMob(e))
continue;
ConfiguredMob mob = getConfiguration().getLimits(e);
if (chunkCounts.containsKey(mob.getKey())) {
int count = chunkCounts.get(mob.getKey()) + 1;
chunkCounts.put(mob.getKey(), count);
} else {
chunkCounts.put(mob.getKey(), 1);
}
}
return chunkCounts;
}
} else {
chunkCounts.put(mob.getKey(), 1);
}
}
return chunkCounts;
}
/**
* Build a HashMap of entity types and their counts in a chunk radius
* @param start The center of the chunk radius
*
* @param start The center of the chunk radius
* @param radius The radius of chunks to check
* @return A HashMap summarizing the entity breakdown within a chunk radius
*/
public static HashMap<String, Integer> summarizeMobsInRadius(Chunk start, int radius) {
HashMap<String, Integer> radCounts = new HashMap<String, Integer>();
World world = start.getWorld();
for (int x = start.getX() - radius; x <= start.getX() + radius; x++) {
for (int z = start.getZ() - radius; z <= start.getZ() + radius; z++) {
Chunk c = world.getChunkAt(x, z);
for (Entity e : c.getEntities()) {
if (e.isDead() || !isLimitableMob(e)) continue;
if (e.isDead() || !isLimitableMob(e))
continue;
ConfiguredMob mob = getConfiguration().getLimits(e);
if (radCounts.containsKey(mob.getKey())) {
int count = radCounts.get(mob.getKey()) + 1;
radCounts.put(mob.getKey(), count);
} else {
radCounts.put(mob.getKey(), 1);
}
}
}
}
return radCounts;
}
} else {
radCounts.put(mob.getKey(), 1);
}
}
}
}
return radCounts;
}
/**
* Convenience method to get the plugin configuration
*/
public static Configuration getConfiguration() {
return MobLimiter.instance.getConfiguration();
}
/**
* Log/broadcast a despawn event per configuration. Console log gated by debug;
* player message gated by despawn_notify_players and moblimiter.notify
* permission.
*
* @param entity the entity that was despawned
* @param reason short reason string (e.g. "age limit", "chunk cull", "spawn cap
* chunk")
*/
public static void notifyDespawn(Entity entity, String reason) {
Configuration cfg = getConfiguration();
if (!cfg.debug() && !cfg.despawnNotifyPlayers()) {
return;
}
Location loc = entity.getLocation();
String mob = entity.getType().toString();
String world = loc.getWorld() != null ? loc.getWorld().getName() : "unknown";
String raw = cfg.getDespawnNotifyMessage().replace("%mob%", mob).replace("%reason%", reason)
.replace("%world%", world).replace("%x%", String.valueOf(loc.getBlockX()))
.replace("%y%", String.valueOf(loc.getBlockY())).replace("%z%", String.valueOf(loc.getBlockZ()));
Component component = LegacyComponentSerializer.legacyAmpersand().deserialize(raw);
if (cfg.debug()) {
String plain = PlainTextComponentSerializer.plainText().serialize(component);
MobLimiter.instance.getLogger().info(plain);
}
if (cfg.despawnNotifyPlayers()) {
for (Player p : Bukkit.getOnlinePlayers()) {
if (p.hasPermission("moblimiter.notify")) {
p.sendMessage(component);
}
}
}
}
}
@@ -9,18 +9,15 @@ import nu.nerd.moblimiter.listeners.EntityEvents;
import org.bukkit.Location;
import org.bukkit.plugin.java.JavaPlugin;
public class MobLimiter extends JavaPlugin {
public static MobLimiter instance;
private Configuration configuration;
private EntityUnloadLimiter chunkUnloadLimiter;
private AgeLimiter ageLimiter;
public void onEnable() {
MobLimiter.instance = this;
configuration = new Configuration();
chunkUnloadLimiter = new EntityUnloadLimiter();
@@ -29,31 +26,32 @@ public class MobLimiter extends JavaPlugin {
new ClickEvents();
new EntityEvents();
new CommandHandler();
System.out.println(configuration.getSpawnEggs());
}
}
public void onDisable() {
chunkUnloadLimiter.removeAllMobs();
}
chunkUnloadLimiter.removeAllMobs();
}
public Configuration getConfiguration() {
return configuration;
}
public AgeLimiter getAgeLimiter() {
return ageLimiter;
}
public static String locationToString(Location location) {
return String.format("X: %.2f Y: %.2f Z: %.2f World: %s",
location.getX(),
location.getY(),
location.getZ(),
return String.format("X: %.2f Y: %.2f Z: %.2f World: %s", location.getX(), location.getY(), location.getZ(),
location.getWorld().getName());
}
}
@@ -1,6 +1,5 @@
package nu.nerd.moblimiter.configuration;
import nu.nerd.moblimiter.MobLimiter;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
@@ -14,7 +13,6 @@ import java.util.List;
public class Configuration {
private MobLimiter plugin;
private int radius;
private boolean debug;
@@ -22,17 +20,24 @@ public class Configuration {
private int growthTicks;
private boolean logBlock;
private boolean relativeAge;
private boolean limitTamed;
private boolean limitElderGuardian;
private boolean respectPersistence;
private boolean limitArmed;
private double enchantedAgeMultiplier;
private boolean despawnNotifyPlayers;
private String despawnNotifyMessage;
private ConfiguredDefaults defaults;
private HashMap<String, ConfiguredMob> limits;
private List<Material> spawnEggs = new ArrayList<>();
public Configuration() {
plugin = MobLimiter.instance;
plugin.saveDefaultConfig();
load();
}
}
/**
* Reload the configuration from disk
@@ -47,119 +52,224 @@ public class Configuration {
this.defaults = new ConfiguredDefaults(plugin.getConfig());
this.logBlock = plugin.getConfig().getBoolean("logblock", false);
this.relativeAge = plugin.getConfig().getBoolean("relative_age", false);
this.limitTamed = plugin.getConfig().getBoolean("limit_tamed", false);
this.limitElderGuardian = plugin.getConfig().getBoolean("limit_elder_guardian", false);
this.respectPersistence = plugin.getConfig().getBoolean("respect_persistence", true);
this.limitArmed = plugin.getConfig().getBoolean("limit_armed", true);
this.enchantedAgeMultiplier = plugin.getConfig().getDouble("enchanted_age_multiplier", 2.0);
this.despawnNotifyPlayers = plugin.getConfig().getBoolean("despawn_notify_players", false);
this.despawnNotifyMessage = plugin.getConfig().getString("despawn_notify_message",
"&7[MobLimiter] Despawned &e%mob%&7 (%reason%) at &f%world% %x%,%y%,%z%");
spawnEggs = new ArrayList<>();
this.limits = new HashMap<String, ConfiguredMob>();
ConfigurationSection mobLimits = plugin.getConfig().getConfigurationSection("limits");
if (mobLimits != null) {
for (String key : mobLimits.getKeys(false)) {
try {
ConfigurationSection l = mobLimits.getConfigurationSection(key);
ConfiguredMob mob;
if (l != null) {
mob = new ConfiguredMob(l, defaults);
} else {
mob = new ConfiguredMob(key, defaults); //use default values for YAML "key: []" blocks
mob = new ConfiguredMob(key, defaults); // use default values for YAML "key: []" blocks
}
limits.put(key.toUpperCase(), mob);
} catch (ConfigurationException ex) {
plugin.getLogger().warning(ex.getMessage());
}
}
}
for(String egg : plugin.getConfig().getStringList("spawn_eggs")) {
spawnEggs.add(Material.getMaterial(egg));
for (String eggName : plugin.getConfig().getStringList("spawn_eggs")) {
Material material = Material.matchMaterial(eggName);
if (material == null || !material.name().endsWith("_SPAWN_EGG")) {
plugin.getLogger().warning("Ignoring unsupported spawn egg material for this server version: " + eggName);
continue;
}
spawnEggs.add(material);
}
System.out.println(spawnEggs);
}
/**
* Get the "view distance," in chunks, to check for mobs when a new mob spawns.
*/
public int getRadius() {
return radius;
}
return radius;
}
/**
* Is debug mode on?
*/
public boolean debug() {
return debug;
}
return debug;
}
/**
* Ticks until a farm animal is ready to breed again.
*/
public int getBreedingTicks() {
return breedingTicks;
}
return breedingTicks;
}
/**
* Ticks until a farm animal grows up.
*/
public int getGrowthTicks() {
return growthTicks;
}
return growthTicks;
}
/**
* Whether support for LogBlock entity removal logging is enabled
*
* @return true if enabled
*/
public boolean logBlockEnabled() {
return logBlock;
}
return logBlock;
}
/**
* Whether mobs' age should be relative to the last time they target a player
*
* @return true if enabled
*/
public boolean relativeAgeEnabled() {
return relativeAge;
}
/** Include tamed mobs in limits. */
public boolean limitTamed() {
return limitTamed;
}
/** Include elder guardians in limits. */
public boolean limitElderGuardian() {
return limitElderGuardian;
}
/** Honor vanilla PersistenceRequired (spawn eggs, /summon, picked-up items). */
public boolean respectPersistence() {
return respectPersistence;
}
/**
* Cull mobs carrying armor or hand items. Overrides respect_persistence for
* armed mobs.
*/
public boolean limitArmed() {
return limitArmed;
}
/** Age-limit multiplier for mobs holding or wearing an enchanted item. */
public double getEnchantedAgeMultiplier() {
return enchantedAgeMultiplier;
}
/** Notify online players holding moblimiter.notify on despawn. */
public boolean despawnNotifyPlayers() {
return despawnNotifyPlayers;
}
/** Raw message template with &-color codes and placeholders. */
public String getDespawnNotifyMessage() {
return despawnNotifyMessage;
}
/**
* Get the global default limits
*/
public ConfiguredDefaults getDefaults() {
return defaults;
}
/**
* Gets a list of spawn eggs as defined in the config
*
* @return A list of spawn eggs as Strings
*/
public List<Material> getSpawnEggs(){return spawnEggs;}
public List<Material> getSpawnEggs() {
return spawnEggs;
}
/**
* Get the limits for a specific mob type, gracefully falling back to values from the "default" block
* Get the limits for a specific mob type, gracefully falling back to values
* from the "default" block
*/
public ConfiguredMob getLimits(Entity entity) {
String key = entity.getType().toString();
if (entity instanceof Sheep) {
key = key + "_" + ((Sheep) entity).getColor().name().toUpperCase();
}
if (limits.containsKey(key)) {
return limits.get(key);
} else {
return new ConfiguredMob(key, defaults);
}
}
key = key + "_" + ((Sheep) entity).getColor().name().toUpperCase();
}
if (limits.containsKey(key)) {
return limits.get(key);
} else {
return new ConfiguredMob(key, defaults);
}
}
public HashMap<String, ConfiguredMob> getAllLimits() {
return limits;
}
}
@@ -3,68 +3,74 @@ package nu.nerd.moblimiter.configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import javax.naming.ConfigurationException;
/**
* Represents global default limits that apply to any mob type, unless a ConfiguredMob overrides.
* Represents global default limits that apply to any mob type, unless a
* ConfiguredMob overrides.
*/
public class ConfiguredDefaults {
private int age;
private int max;
private int chunkMax;
private int cull;
public ConfiguredDefaults(FileConfiguration config) {
ConfigurationSection section = config.getConfigurationSection("defaults");
if (section != null) {
age = section.getInt("age", -1);
max = section.getInt("max", -1);
chunkMax = section.getInt("chunk_max", -1);
cull = section.getInt("cull", -1);
} else {
age = -1;
max = -1;
chunkMax = -1;
cull = -1;
}
}
}
}
/**
* The maximum age, in ticks, that this mob should live for before being removed
*/
public int getAge() {
return age;
}
return age;
}
/**
* The maximum number of this mob type that should be allowed to exist within a
* view distance centered on a spawning mob.
*/
public int getMax() {
return max;
}
return max;
}
/**
* The maximum number of this mob type that should be allowed to exist in a single chunk.
* The maximum number of this mob type that should be allowed to exist in a
* single chunk.
*/
public int getChunkMax() {
return chunkMax;
}
return chunkMax;
}
/**
* The number of mobs that should be left after a chunk unload cull
*/
public int getCull() {
return cull;
}
}
@@ -1,19 +1,15 @@
package nu.nerd.moblimiter.configuration;
import nu.nerd.moblimiter.MobLimiter;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.EntityType;
import javax.naming.ConfigurationException;
/**
* Represents configured limits for a specific mob type
*/
public class ConfiguredMob {
private String key;
private EntityType type;
private int age;
@@ -21,102 +17,125 @@ public class ConfiguredMob {
private int chunkMax;
private int cull;
/**
* Construct a ConfiguredMob from the YAML configuration. Unspecified values will fall back to the values
* defined in the default block.
* @param mob The configuration section
* Construct a ConfiguredMob from the YAML configuration. Unspecified values
* will fall back to the values defined in the default block.
*
* @param mob The configuration section
* @param defaults The default values to fall back to
* @throws ConfigurationException
*/
public ConfiguredMob(ConfigurationSection mob, ConfiguredDefaults defaults) throws ConfigurationException{
public ConfiguredMob(ConfigurationSection mob, ConfiguredDefaults defaults) throws ConfigurationException {
try {
String name;
if (mob.getName().toUpperCase().startsWith("SHEEP_")) {
name = "SHEEP";
} else {
name = mob.getName();
}
key = mob.getName().toUpperCase();
type = EntityType.valueOf(name.toUpperCase());
type = resolveEntityType(mob.getName());
age = mob.getInt("age", defaults.getAge());
max = mob.getInt("max", defaults.getMax());
chunkMax = mob.getInt("chunk_max", defaults.getChunkMax());
cull = mob.getInt("cull", defaults.getCull());
} catch (Exception ex) {
throw new ConfigurationException("Invalid configuration for mob type: " + mob.getName());
}
}
/**
* Create a dummy ConfiguredMob with default values, for when one isn't actually configured
* @param mobKey The key this mob type would have in the config
* Create a dummy ConfiguredMob with default values, for when one isn't actually
* configured
*
* @param mobKey The key this mob type would have in the config
* @param defaults The default values
*/
public ConfiguredMob(String mobKey, ConfiguredDefaults defaults) {
String name;
if (mobKey.toUpperCase().startsWith("SHEEP_")) {
name = "SHEEP";
} else {
name = mobKey.toUpperCase();
}
key = mobKey.toUpperCase();
type = EntityType.valueOf(name.toUpperCase());
type = resolveEntityType(mobKey);
age = defaults.getAge();
max = defaults.getMax();
chunkMax = defaults.getChunkMax();
cull = defaults.getCull();
}
}
/**
* Get the key identifying this mob classification
*/
public String getKey() {
return key;
}
return key;
}
/**
* The Bukkit EntityType these limits apply to
*/
public EntityType getType() {
return type;
}
return type;
}
/**
* The maximum age, in ticks, that this mob should live for before being removed
*/
public int getAge() {
return age;
}
return age;
}
/**
* The maximum number of this mob type that should be allowed to exist within a
* view distance centered on a spawning mob.
*/
public int getMax() {
return max;
}
return max;
}
/**
* The maximum number of this mob type that should be allowed to exist in a single chunk.
* The maximum number of this mob type that should be allowed to exist in a
* single chunk.
*/
public int getChunkMax() {
return chunkMax;
}
return chunkMax;
}
/**
* The number of mobs that should be left after a chunk unload cull
*/
public int getCull() {
return cull;
}
/**
* Resolve config keys to current Bukkit entity enum names.
*/
private static EntityType resolveEntityType(String mobKey) {
String name = mobKey.toUpperCase();
if (name.startsWith("SHEEP_")) {
name = "SHEEP";
} else if (name.equals("PIG_ZOMBIE") || name.equals("ZOMBIE_PIGMAN")) {
name = "ZOMBIFIED_PIGLIN";
}
return EntityType.valueOf(name);
}
}
@@ -13,114 +13,158 @@ import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Sweep loaded chunks every 30 seconds and remove applicable mobs over their type's age limit
* Sweep loaded chunks every 30 seconds and remove applicable mobs over their
* type's age limit
*/
public class AgeLimiter extends BukkitRunnable implements Listener {
private MobLimiter plugin;
private int removed;
private Map<UUID, Integer> lastTargeted;
public AgeLimiter() {
plugin = MobLimiter.instance;
lastTargeted = new HashMap<UUID, Integer>();
this.runTaskTimer(plugin, 600L, 600L); //sweep every 30 seconds
}
this.runTaskTimer(plugin, 600L, 600L); // sweep every 30 seconds
}
/**
* Call the sweep on every loaded chunk in every world
*/
public void run() {
removed = 0;
for (World world: plugin.getServer().getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) {
sweepChunk(chunk);
}
}
if (plugin.getConfiguration().debug()) {
plugin.getLogger().info(String.format("Age limit sweep removed %d entities", removed));
}
}
removed = 0;
for (World world : plugin.getServer().getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) {
sweepChunk(chunk);
}
}
if (plugin.getConfiguration().debug()) {
plugin.getLogger().info(String.format("Age limit sweep removed %d entities", removed));
}
}
/**
* Sweep the chunk and remove mobs that are past their shelf life
*
* @param chunk the chunk to check
*/
private void sweepChunk(Chunk chunk) {
for (Entity entity : chunk.getEntities()) {
// Constrain entities to be removed to limitable mobs, excluding villagers
if (entity.isDead() || !EntityHelper.isLimitableMob(entity) || entity instanceof Villager) continue;
if (entity.isDead() || !EntityHelper.isLimitableMob(entity) || entity instanceof Villager)
continue;
// Exempt special mobs
if (EntityHelper.isSpecialMob((LivingEntity) entity)) {
if (plugin.getConfiguration().debug()) {
plugin.getLogger().info("Special mob exempted from removal: " + EntityHelper.getMobDescription(entity));
plugin.getLogger()
.info("Special mob exempted from removal: " + EntityHelper.getMobDescription(entity));
}
continue;
}
// Leave two of any farm animals
if (EntityHelper.isBreedingPair(entity)) continue;
if (EntityHelper.isBreedingPair(entity))
continue;
// If relative ages are on, and the mob is targeting a player, don't remove it
if (isTargetingPlayer(entity)) continue;
if (isTargetingPlayer(entity))
continue;
// Remove mobs
// Remove mobs. Enchanted-item mobs get a multiplied age threshold.
ConfiguredMob limits = plugin.getConfiguration().getLimits(entity);
if (!entity.isDead() && adjustedAge(entity) > limits.getAge() && limits.getAge() > -1) {
int ageLimit = limits.getAge();
if (ageLimit > -1 && EntityHelper.hasEnchantedItem((LivingEntity) entity)) {
ageLimit = (int) (ageLimit * plugin.getConfiguration().getEnchantedAgeMultiplier());
}
if (!entity.isDead() && adjustedAge(entity) > ageLimit && ageLimit > -1) {
EntityHelper.notifyDespawn(entity, "age limit");
((LivingEntity) entity).damage(1000); // Kill the entity and drop its items
removed++;
lastTargeted.remove(entity.getUniqueId());
if (plugin.getConfiguration().debug()) {
plugin.getLogger().info("Removed mob (age limit): " + EntityHelper.getMobDescription(entity));
}
}
}
}
/**
* Don't let the sweep kill entities currently targeting a player.
* Also, keep resetting the relative age, since EntityTargetEvent
* only fires when the target changes.
* Don't let the sweep kill entities currently targeting a player. Also, keep
* resetting the relative age, since EntityTargetEvent only fires when the
* target changes.
*
* @param entity the entity to check
* @return true if the Entity is a Creature type and is targeting a player.
*/
private boolean isTargetingPlayer(Entity entity) {
if (!plugin.getConfiguration().relativeAgeEnabled()) return false;
if (!plugin.getConfiguration().relativeAgeEnabled())
return false;
if (entity instanceof Creature) {
Creature creature = (Creature) entity;
if (creature.getTarget() != null && creature.getTarget().getType().equals(EntityType.PLAYER)) {
lastTargeted.put(entity.getUniqueId(), entity.getTicksLived());
return true;
}
}
return false;
}
/**
* Returns the adjusted age of a mob.
* If relative age is on, and the mob recently tageted a player, the delta ticks will be returned.
* Otherwise, the regular getTicksLived() will be used.
* Returns the adjusted age of a mob. If relative age is on, and the mob
* recently tageted a player, the delta ticks will be returned. Otherwise, the
* regular getTicksLived() will be used.
*
* @param entity the entity to check
* @return the age in ticks
*/
public int adjustedAge(Entity entity) {
if (lastTargeted.containsKey(entity.getUniqueId())) {
return entity.getTicksLived() - lastTargeted.get(entity.getUniqueId());
} else {
return entity.getTicksLived();
}
}
}
@@ -15,48 +15,57 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Cull applicable mobs on chunk unload.
* This is based on behavior from the original MobLimiter 1.x.
* Cull applicable mobs on chunk unload. This is based on behavior from the
* original MobLimiter 1.x.
*/
public class EntityUnloadLimiter implements Listener {
private MobLimiter plugin;
private Chunk chunk;
public EntityUnloadLimiter() {
plugin = MobLimiter.instance;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@EventHandler
public void onEntitiesUnloaded(EntitiesUnloadEvent event) {
removeMobs(event.getEntities());
}
/**
* Remove excess mobs in a chunk, if chunk unload culling is enabled for the mob type.
* This is the default behavior from MobLimiter 1.x.
* Remove excess mobs in a chunk, if chunk unload culling is enabled for the mob
* type. This is the default behavior from MobLimiter 1.x.
*
* @param entities The list of entities being unloaded
*/
private void removeMobs(List<Entity> entities) {
Map<String, Integer> count = new HashMap<String, Integer>();
for (Entity entity : entities) {
// Constrain entities to be removed to limitable mobs, excluding villagers
if (entity.isDead() || !EntityHelper.isLimitableMob(entity) || entity instanceof Villager) continue;
if (entity.isDead() || !EntityHelper.isLimitableMob(entity) || entity instanceof Villager)
continue;
// Exempt special mobs
if (EntityHelper.isSpecialMob((LivingEntity) entity)) {
if (plugin.getConfiguration().debug()) {
plugin.getLogger().info("Special mob exempted from removal: " + EntityHelper.getMobDescription(entity));
plugin.getLogger()
.info("Special mob exempted from removal: " + EntityHelper.getMobDescription(entity));
}
continue;
}
ConfiguredMob limits = plugin.getConfiguration().getLimits(entity);
@@ -68,26 +77,36 @@ public class EntityUnloadLimiter implements Listener {
count.put(key, mobCount);
if (cap > -1 && mobCount > cap) {
EntityHelper.notifyDespawn(entity, "chunk unload cull");
entity.remove();
if (plugin.getConfiguration().debug()) {
plugin.getLogger().info("Chunk unload removed: " + EntityHelper.getMobDescription(entity));
}
}
}
}
}
/**
* Run removeMobs() on all loaded chunks
*/
public void removeAllMobs() {
for (World world : plugin.getServer().getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) {
removeMobs(Arrays.asList(chunk.getEntities()));
}
}
}
}
@@ -15,18 +15,16 @@ import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import java.util.ArrayList;
import java.util.List;
/**
* Limit newly spawning mobs if there are too many
*/
public class SpawnLimiter implements Listener {
private MobLimiter plugin;
private List<SpawnReason> reasons;
public SpawnLimiter() {
plugin = MobLimiter.instance;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
reasons = new ArrayList<>();
@@ -34,8 +32,8 @@ public class SpawnLimiter implements Listener {
reasons.add(SpawnReason.DEFAULT);
reasons.add(SpawnReason.NATURAL);
reasons.add(SpawnReason.SPAWNER);
}
}
/**
* Handle mob limting on creature spawn
@@ -46,77 +44,104 @@ public class SpawnLimiter implements Listener {
SpawnReason reason = event.getSpawnReason();
ConfiguredMob limits = plugin.getConfiguration().getLimits(event.getEntity());
if (!reasons.contains(reason)) return;
if (!reasons.contains(reason))
return;
// Cancel spawn of mobs over the radius limit
if (countEntitiesInSpawnRadius(event.getEntity()) >= limits.getMax() && limits.getMax() > -1) {
log(event.getEntity(), reason, "radius", limits.getMax());
EntityHelper.notifyDespawn(event.getEntity(), "spawn cap radius");
event.getEntity().remove();
}
// Cancel spawn of mobs over the chunk limit
if (countEntitiesInChunk(event.getEntity()) >= limits.getChunkMax() && limits.getChunkMax() > -1) {
log(event.getEntity(), reason, "chunk", limits.getChunkMax());
EntityHelper.notifyDespawn(event.getEntity(), "spawn cap chunk");
event.getEntity().remove();
}
}
/**
* Count entities of the same type within a "view distance" in chunks from the original entity
* Count entities of the same type within a "view distance" in chunks from the
* original entity
*
* @param entity the entity to check
* @return number of matching entities
*/
private int countEntitiesInSpawnRadius(Entity entity) {
int count = 0;
int radius = plugin.getConfiguration().getRadius();
ConfiguredMob mob = plugin.getConfiguration().getLimits(entity);
World world = entity.getWorld();
Chunk start = entity.getLocation().getChunk();
for (int x = start.getX() - radius; x <= start.getX() + radius; x++) {
for (int z = start.getZ() - radius; z <= start.getZ() + radius; z++) {
Chunk c = world.getChunkAt(x, z);
for (Entity e : c.getEntities()) {
ConfiguredMob m = plugin.getConfiguration().getLimits(e);
if (m.getKey().equals(mob.getKey()) && !e.isDead()) {
count++;
}
}
}
}
return count;
}
count++;
}
}
}
}
return count;
}
/**
* Count entities of the same type in an individual chunk
*
* @param entity The entity to check
* @return number of matching entities
*/
private int countEntitiesInChunk(Entity entity) {
int count = 0;
ConfiguredMob mob = plugin.getConfiguration().getLimits(entity);
for (Entity e : entity.getLocation().getChunk().getEntities()) {
ConfiguredMob m = plugin.getConfiguration().getLimits(e);
if (m.getKey().equals(mob.getKey()) && !e.isDead()) {
count++;
}
}
return count;
}
count++;
}
}
return count;
}
/**
* Log entity removal for diagnostic purposes if debug mode is on
*/
private void log(Entity entity, SpawnReason reason, String capType, int cap) {
if (!plugin.getConfiguration().debug()) return;
if (!plugin.getConfiguration().debug())
return;
String mob = EntityHelper.getMobDescription(entity);
String details = String.format("[reason: %s, cap type: %s, cap: %d]", reason.toString(), capType, cap);
String msg = String.format("Cancelled spawn of %s %s", mob, details);
plugin.getLogger().info(msg);
}
}
@@ -1,13 +1,11 @@
package nu.nerd.moblimiter.listeners;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import nu.nerd.moblimiter.MobLimiter;
import org.bukkit.Material;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
@@ -22,47 +20,58 @@ public class ClickEvents implements Listener {
private List<Material> spawnEggs;
public ClickEvents() {
plugin = MobLimiter.instance;
spawnEggs = MobLimiter.instance.getConfiguration().getSpawnEggs();
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
// --------------------------------------------------------------------------------------------
/**
* Formerly part of LimitSpawnEggs
* Stops players from changing the mob of a spawner with spawn eggs
* Formerly part of LimitSpawnEggs Stops players from changing the mob of a
* spawner with spawn eggs
*/
@EventHandler(ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
if(player.hasPermission("moblimiter.spawners.bypass")) {
if (player.hasPermission("moblimiter.spawners.bypass")) {
return;
}
if(event.getAction() == Action.RIGHT_CLICK_BLOCK &&
event.getClickedBlock().getType() == Material.SPAWNER &&
event.getItem() != null &&
spawnEggs.contains(event.getItem().getType())) {
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == Material.SPAWNER
&& event.getItem() != null && spawnEggs.contains(event.getItem().getType()))
{
event.setCancelled(true);
player.sendMessage(Component.text("You don't have permission to edit spawners.")
.color(TextColor.fromHexString("#FF5555")));
}
}
// --------------------------------------------------------------------------------------------
/**
* Formerly part of KeepBabyMobs
* Handles the locking of baby mobs. Originally handled by KeepBabyMobs.
* Formerly part of KeepBabyMobs Handles the locking of baby mobs. Originally
* handled by KeepBabyMobs.
*/
@EventHandler
public void onPlayerInteractAtEntityEvent(PlayerInteractAtEntityEvent event) {
Entity entity = event.getRightClicked();
Player player = event.getPlayer();
ItemStack hand = player.getEquipment().getItemInMainHand();
if(hand.getType().equals(Material.NAME_TAG)) {
if (hand.getType().equals(Material.NAME_TAG)) {
lockMobAge(entity, player, hand);
}
}
@@ -71,49 +80,63 @@ public class ClickEvents implements Listener {
/**
* Locks the mob's age and makes it a baby forever
*
* @param entity The entity being age locked
* @param player The player initiating the age lock
* @param hand The item being used to lock the entity's age
* @param hand The item being used to lock the entity's age
*/
public void lockMobAge(Entity entity, Player player, ItemStack hand) {
// If the held item is a nametag
if(hand.getItemMeta().hasDisplayName()) {
// Tadpoles are checked separately due to a different handling of their growth.
if(entity.getType().equals(EntityType.TADPOLE)) {
((Tadpole) entity).setAgeLock(true);
logLock(player, entity);
if (hand.getItemMeta().hasDisplayName()) {
// Check other mobs that can age and breed after.
} else if(entity instanceof Breedable) {
Breedable breedable = (Breedable) entity;
// Tadpoles are checked separately due to a different handling of their growth.
if (entity.getType().equals(EntityType.TADPOLE)) {
if(!breedable.isAdult()) {
breedable.setAgeLock(true);
((Tadpole) entity).setAgeLock(true);
logLock(player, entity);
if(!(breedable instanceof Horse)) {
breedable.setAge(Integer.MIN_VALUE);
// Check other mobs that can age and breed after.
} else if (entity instanceof Breedable) {
Breedable breedable = (Breedable) entity;
if (!breedable.isAdult()) {
breedable.setAgeLock(true);
if (!(breedable instanceof Horse)) {
breedable.setAge(Integer.MIN_VALUE);
}
logLock(player, breedable);
}
logLock(player, breedable);
}
}
}
}
// --------------------------------------------------------------------------------------------
/**
* Logs any locked mobs to console.
*
* @param player The player locking the mob
* @param entity The entity being locked
*/
public void logLock(Player player, Entity entity) {
player.sendMessage(Component.text("That mob has now been age locked. How adorable!")
.color(TextColor.fromHexString("#FFAA00")));
MobLimiter.instance.getComponentLogger().info(Component.text(String.format("%s age locked %s named %s at %s",
player.getName(), entity.getType(), entity.customName(),
MobLimiter.locationToString(entity.getLocation()))));
MobLimiter.instance.getComponentLogger()
.info(Component.text(String.format("%s age locked %s named %s at %s", player.getName(),
entity.getType(), entity.customName(), MobLimiter.locationToString(entity.getLocation()))));
}
}
@@ -15,40 +15,55 @@ public class EntityEvents implements Listener {
MobLimiter plugin;
public EntityEvents() {
plugin = MobLimiter.instance;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
// --------------------------------------------------------------------------------------------
/**
* Formerly part of KeepBabyMobs.
* Logs the death of an entity.
* Formerly part of KeepBabyMobs. Logs the death of an entity.
*/
@EventHandler(priority = EventPriority.NORMAL)
public void onEntityDeath(EntityDeathEvent event) {
Entity entity = event.getEntity();
Player player = event.getEntity().getKiller();
if(player == null || !(entity instanceof Breedable)) {
if(entity instanceof Tadpole) {
if (player == null || !(entity instanceof Breedable)) {
if (entity instanceof Tadpole) {
logDeath(player, entity, "");
}
return;
}
Breedable breedable = (Breedable) entity;
if(breedable.getAgeLock()) {
if (breedable.getAgeLock()) {
String extrainfo = "";
if(entity instanceof Cat) {
if (entity instanceof Cat) {
extrainfo = "type: " + ((Cat) entity).getCatType().name();
}
if(entity instanceof Tameable) {
if (entity instanceof Tameable) {
Tameable tameable = ((Tameable) entity);
if(tameable.isTamed()) {
if (tameable.isTamed()) {
extrainfo = "owner: " + ((Tameable) entity).getOwner().getName();
}
}
logDeath(player, breedable, extrainfo);
@@ -61,17 +76,18 @@ public class EntityEvents implements Listener {
/**
* Outputs the death details of an age locked baby animal
* @param player The player who killed the animal
* @param entity The mob killed
*
* @param player The player who killed the animal
* @param entity The mob killed
* @param extrainfo Extra information like coat colour and owner
*/
public void logDeath(Player player, Entity entity, String extrainfo) {
MobLimiter.instance.getComponentLogger().info(Component.text(String.format("%s killed %s named %s at %s %s",
player.getName(),
entity.getType(),
PlainTextComponentSerializer.plainText().serialize(entity.customName()),
MobLimiter.locationToString(entity.getLocation()),
extrainfo)));
MobLimiter.instance.getComponentLogger()
.info(Component.text(String.format("%s killed %s named %s at %s %s", player.getName(), entity.getType(),
PlainTextComponentSerializer.plainText().serialize(entity.customName()),
MobLimiter.locationToString(entity.getLocation()), extrainfo)));
}
// --------------------------------------------------------------------------------------------
@@ -81,49 +97,73 @@ public class EntityEvents implements Listener {
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onCreatureSpawnEvent(final CreatureSpawnEvent event) {
CreatureSpawnEvent.SpawnReason reason = event.getSpawnReason();
if ((reason == CreatureSpawnEvent.SpawnReason.BREEDING || reason == CreatureSpawnEvent.SpawnReason.EGG || reason == CreatureSpawnEvent.SpawnReason.DISPENSE_EGG)) {
if ((reason == CreatureSpawnEvent.SpawnReason.BREEDING || reason == CreatureSpawnEvent.SpawnReason.EGG
|| reason == CreatureSpawnEvent.SpawnReason.DISPENSE_EGG))
{
if (isFarmAnimal(event.getEntity())) {
applyBreedingChanges((Animals) event.getEntity());
for (Entity en : event.getEntity().getNearbyEntities(4, 4, 4)) {
if (isFarmAnimal(en)) {
applyBreedingChanges((Animals) en);
}
}
}
}
}
// --------------------------------------------------------------------------------------------
/**
* Controls the rate farm animals grow up and the length of their breeding cooldown.
* growthTicks: How many ticks until the animal becomes an adult.
* breedingTicks: How many ticks until the animal can breed again.
* A value of zero makes the condition instant, and -1 disables tampering with vanilla behavior.
* This functionality was called "agecapbaby" and "agecapbaby" respectively in MobLimiter 1.x.
* Controls the rate farm animals grow up and the length of their breeding
* cooldown. growthTicks: How many ticks until the animal becomes an adult.
* breedingTicks: How many ticks until the animal can breed again. A value of
* zero makes the condition instant, and -1 disables tampering with vanilla
* behavior. This functionality was called "agecapbaby" and "agecapbaby"
* respectively in MobLimiter 1.x.
*
* @param animal the animal to apply the changes to
*/
private void applyBreedingChanges(Animals animal) {
if (animal.getAgeLock()) return;
if (animal.getAgeLock())
return;
int growthTicks = plugin.getConfiguration().getGrowthTicks();
int breedingTicks = plugin.getConfiguration().getBreedingTicks();
if (growthTicks > -1 && !animal.isAdult()) {
animal.setAge(Math.max(animal.getAge(), -growthTicks));
} else if (breedingTicks > -1 && animal.isAdult()) {
animal.setAge(Math.min(animal.getAge(), breedingTicks));
}
}
// --------------------------------------------------------------------------------------------
/**
* Whether the entity in question is a farm animal or not
*
* @param entity the entity to check
* @return true if this is a farm animal
*/
private boolean isFarmAnimal(Entity entity) {
return (entity instanceof Animals) && !(entity instanceof Tameable);
}
}
+255
View File
@@ -0,0 +1,255 @@
# Spawn-radius check in chunks. 2 = 5x5 = 25 chunks around a spawn event.
# Smaller radius lets multiple nearby farms coexist without starving each other.
radius: 2
# Breeding cooldown / baby growth accelerators. -1 disables override (vanilla timings).
# Vanilla: breeding cooldown 5 min (6000t), baby growth 20 min (24000t).
breeding_ticks: -1
growth_ticks: -1
logblock: false
relative_age: false
debug: false
# Tamed mobs (pets, horses, etc.) are exempt from limits. 200-player servers
# accumulate lots of pets; leaving this false prevents silent pet losses.
limit_tamed: false
# Elder guardians are rare/unique.
limit_elder_guardian: true
# Honor vanilla PersistenceRequired. Spawn-egg / /summon / picked-up-item mobs
# survive limits, matching vanilla "never despawn" semantics. Natural-spawn
# (farm spawners, mob spawning) is unaffected — those are not persistent.
respect_persistence: true
# Cull mobs carrying armor or hand items. Closes the abuse path where players
# drop gear to mobs (which sets PersistenceRequired in vanilla) to keep them
# loaded indefinitely. When true this overrides respect_persistence for armed
# mobs. Named / tamed / elder guardian exemptions still apply.
limit_armed: true
# Multiplier applied to a mob's configured age limit when it is holding or
# wearing an enchanted item. Lets admins give rarer geared mobs more airtime
# without disabling limits. 1.0 = no extra time, 2.0 = double. Mobs with
# age: -1 are unaffected.
enchanted_age_multiplier: 2.0
# In-game broadcast to moblimiter.notify holders. At 200 players + stacked farms
# this spams chat heavily. Leave off; /moblimiter stats is enough.
despawn_notify_players: false
despawn_notify_message: "&7[&cMobLimiter&f-&4OG&7] Despawned &e%mob%&7 (%reason%) at &f%world% %x%,%y%,%z%"
# Defaults tuned for farm-friendly + stacker-friendly operation.
# ChunkCap-OG enforces the hard per-chunk total entity ceiling; these caps are
# the per-type soft caps that only trip on excessive single-type density.
defaults:
age: -1 # Passive/unspecified mobs do not age-despawn (vanilla)
max: 800 # Same-type cap across the 5x5 spawn radius
chunk_max: 200 # Same-type cap per chunk (stackers easily stay under)
cull: 12 # Keep this many per type per chunk on chunk unload
# Per-mob overrides. age in ticks (20t = 1s, 24000t = 20min).
# cull = count retained per chunk on unload cull; -1 = never cull.
limits:
bat:
cull: 8
blaze:
age: 36000
cull: 8
cave_spider:
age: 48000
cull: 8
chicken: []
cod: []
cow: []
creeper:
age: 48000
cull: 16
dolphin: []
ender_dragon: []
enderman:
age: 36000
cull: 8
endermite:
cull: 6
evoker: []
ghast:
cull: 4
giant:
cull: -1
guardian:
age: 24000
max: 600
chunk_max: 100
cull: 12
horse:
age: -1
illusioner: []
iron_golem: []
llama:
cull: 8
magma_cube:
age: 48000
cull: 12
mushroom_cow: []
ocelot:
cull: 8
parrot:
cull: 8
phantom: []
pig: []
zombified_piglin:
age: 36000
cull: 16
pufferfish: []
rabbit:
cull: 8
salmon: []
sheep_white:
cull: 8
sheep_orange:
cull: 8
sheep_magenta:
cull: 8
sheep_light_blue:
cull: 8
sheep_yellow:
cull: 8
sheep_lime:
cull: 8
sheep_pink:
cull: 8
sheep_silver:
cull: 8
sheep_cyan:
cull: 8
sheep_purple:
cull: 8
sheep_blue:
cull: 8
sheep_brown:
cull: 8
sheep_green:
cull: 8
sheep_red:
cull: 8
sheep_black:
cull: 8
shulker: []
silverfish:
cull: 6
skeleton:
age: 48000
cull: 16
slime:
age: 48000
cull: 12
snowman:
cull: -1
spider:
age: 48000
cull: 12
squid:
age: 24000
max: 400
chunk_max: 50
cull: 8
tropical_fish: []
turtle: []
vex: []
villager:
chunk_max: 40
age: -1
vindicator: []
witch:
age: 48000
cull: 12
wither:
cull: -1
wolf:
cull: 8
zombie:
age: 48000
cull: 16
spawn_eggs:
- 'ALLAY_SPAWN_EGG'
- 'AXOLOTL_SPAWN_EGG'
- 'BAT_SPAWN_EGG'
- 'BEE_SPAWN_EGG'
- 'BLAZE_SPAWN_EGG'
- 'CAT_SPAWN_EGG'
- 'CAMEL_SPAWN_EGG'
- 'CAVE_SPIDER_SPAWN_EGG'
- 'COD_SPAWN_EGG'
- 'COW_SPAWN_EGG'
- 'CREEPER_SPAWN_EGG'
- 'DOLPHIN_SPAWN_EGG'
- 'DONKEY_SPAWN_EGG'
- 'DROWNED_SPAWN_EGG'
- 'ELDER_GUARDIAN_SPAWN_EGG'
- 'ENDER_DRAGON_SPAWN_EGG'
- 'ENDERMAN_SPAWN_EGG'
- 'ENDERMITE_SPAWN_EGG'
- 'EVOKER_SPAWN_EGG'
- 'FOX_SPAWN_EGG'
- 'FROG_SPAWN_EGG'
- 'GHAST_SPAWN_EGG'
- 'GLOW_SQUID_SPAWN_EGG'
- 'GOAT_SPAWN_EGG'
- 'GUARDIAN_SPAWN_EGG'
- 'HOGLIN_SPAWN_EGG'
- 'HORSE_SPAWN_EGG'
- 'HUSK_SPAWN_EGG'
- 'IRON_GOLEM_SPAWN_EGG'
- 'LLAMA_SPAWN_EGG'
- 'MAGMA_CUBE_SPAWN_EGG'
- 'MOOSHROOM_SPAWN_EGG'
- 'MULE_SPAWN_EGG'
- 'OCELOT_SPAWN_EGG'
- 'PANDA_SPAWN_EGG'
- 'PARROT_SPAWN_EGG'
- 'PHANTOM_SPAWN_EGG'
- 'PIG_SPAWN_EGG'
- 'PIGLIN_SPAWN_EGG'
- 'PIGLIN_BRUTE_SPAWN_EGG'
- 'PILLAGER_SPAWN_EGG'
- 'POLAR_BEAR_SPAWN_EGG'
- 'PUFFERFISH_SPAWN_EGG'
- 'RABBIT_SPAWN_EGG'
- 'RAVAGER_SPAWN_EGG'
- 'SALMON_SPAWN_EGG'
- 'SHEEP_SPAWN_EGG'
- 'SHULKER_SPAWN_EGG'
- 'SILVERFISH_SPAWN_EGG'
- 'SKELETON_SPAWN_EGG'
- 'SKELETON_HORSE_SPAWN_EGG'
- 'SLIME_SPAWN_EGG'
- 'SNIFFER_SPAWN_EGG'
- 'SNOW_GOLEM_SPAWN_EGG'
- 'SPIDER_SPAWN_EGG'
- 'SQUID_SPAWN_EGG'
- 'STRAY_SPAWN_EGG'
- 'STRIDER_SPAWN_EGG'
- 'TADPOLE_SPAWN_EGG'
- 'TRADER_LLAMA_SPAWN_EGG'
- 'TROPICAL_FISH_SPAWN_EGG'
- 'TURTLE_SPAWN_EGG'
- 'VEX_SPAWN_EGG'
- 'VILLAGER_SPAWN_EGG'
- 'VINDICATOR_SPAWN_EGG'
- 'WANDERING_TRADER_SPAWN_EGG'
- 'WARDEN_SPAWN_EGG'
- 'WITCH_SPAWN_EGG'
- 'WITHER_SPAWN_EGG'
- 'WITHER_SKELETON_SPAWN_EGG'
- 'WOLF_SPAWN_EGG'
- 'ZOGLIN_SPAWN_EGG'
- 'ZOMBIE_SPAWN_EGG'
- 'ZOMBIE_HORSE_SPAWN_EGG'
- 'ZOMBIE_VILLAGER_SPAWN_EGG'
- 'ZOMBIFIED_PIGLIN_SPAWN_EGG'
+6 -2
View File
@@ -1,9 +1,9 @@
name: MobLimiter
version: ${project.version}
version: '${version}'
description: Control the mob population
authors: ["Travis Watkins", "redwall_hp"]
softdepend: ["LogBlock"]
api-version: '1.20'
api-version: '${apiVersion}'
main: nu.nerd.moblimiter.MobLimiter
@@ -17,9 +17,13 @@ permissions:
moblimiter.limits: true
moblimiter.check: true
moblimiter.spawners.bypass: true
moblimiter.notify: true
moblimiter.reload:
description: Access to reload command
default: op
moblimiter.notify:
description: Receive in-game despawn notifications
default: op
moblimiter.count:
description: Access to count command
default: op