Compare commits

..

6 commits

Author SHA1 Message Date
16e4f0a923
fix using durability for max damage
Some checks are pending
Java CI with Gradle / build (push) Waiting to run
2026-07-08 04:26:13 +02:00
403c990083
migrate configanvil to ca config 2026-07-08 04:26:13 +02:00
e61baba175
remove use of useInFuture 2026-07-08 04:26:12 +02:00
cf768d67bd
more proper comment [ci skip] 2026-07-08 03:42:37 +02:00
4fc037ebce
version bump 2026-07-08 03:28:41 +02:00
a202df9f0a
add /ca enchant 2026-07-08 03:28:10 +02:00
36 changed files with 231 additions and 106 deletions

View file

@ -39,7 +39,8 @@ ca.bypass.level: Allow player to bypass every level limit (no custom limit)
# Command permissions
ca.command.reload: Allow administrator to reload the plugin's configs
ca.command.diagnostic: Allow adminastator to diagnistic some simple problem with the plugin
ca.command.diagnostic: Get debug information about the plugin and server
ca.command.enchantment: Allows to set enchantment to holden item
ca.config.edit: Allow administrator to edit the plugin's config in game
# -----------------------------------------------------------------------------

View file

@ -22,7 +22,7 @@ plugins {
}
group = "xyz.alexcrea"
version = "1.17.7"
version = "1.18.0"
val isDevBuild = System.getenv("SMALL_COMMIT_HASH") != null
val isPreRelease = System.getenv("IS_GITHUB_PRERELEASE") == "true"

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#
@ -160,13 +160,6 @@ restriction_sweeping_edge:
enchantments: [ minecraft:sweeping, minecraft:sweeping_edge ]
notAffectedGroups: [ enchanted_book, swords ]
# Do not exist in 1.18, that mean useInFuture will be set to true
# useInFuture set to true also mean it will not warn if there is an issue
restriction_swift_sneak:
useInFuture: true
enchantments: [ minecraft:swift_sneak ]
notAffectedGroups: [ enchanted_book, leggings ]
restriction_thorns:
enchantments: [ minecraft:thorns ]
notAffectedGroups: [ enchanted_book, armors ]

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#
@ -275,10 +275,7 @@ restriction_sweeping_edge:
- enchanted_book
- swords
# Do not exist in 1.18, that mean useInFuture will be set to true
# useInFuture set to true also mean it will not warn if there is an issue
restriction_swift_sneak:
useInFuture: true
enchantments:
- minecraft:swift_sneak
notAffectedGroups:

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#
@ -272,10 +272,7 @@ restriction_sweeping_edge:
- enchanted_book
- swords
# Do not exist in 1.18, that mean useInFuture will be set to true
# useInFuture set to true also mean it will not warn if there is an issue
restriction_swift_sneak:
useInFuture: true
enchantments:
- minecraft:swift_sneak
notAffectedGroups:

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,6 @@
package xyz.alexcrea.cuanvil.util
import org.bukkit.Material
import org.bukkit.inventory.meta.Damageable
// I LOVE support of old versions and needing to do modules like that
@ -10,8 +11,8 @@ object MaxDamageCheckerUtil {
/**
* @return max damage or int max if not set
*/
fun getMaxDamage(meta: Damageable): Int {
if(!meta.hasMaxDamage()) return Integer.MAX_VALUE
fun getMaxDamage(type: Material, meta: Damageable): Int {
if(!meta.hasMaxDamage()) return type.maxDurability.toInt()
return meta.maxDamage
}

View file

@ -30,10 +30,11 @@ public class UpdateHandler {
new Version(1, 11, 0), PUpdate_1_11_0::handleUpdate,
new Version(1, 15, 5), PUpdate_1_15_5::handleUpdate,
new Version(1, 15, 6), PUpdate_1_15_6::handleUpdate,
new Version(1, 17, 7), PUpdate_1_17_7::handleUpdate
new Version(1, 18, 0), PUpdate_1_18::handleUpdate
);
private static final List<MCUpdate> mcUpdateMap = List.of(
new Update_1_19(),
new Update_1_20_5(),
new Update_1_21(),
new Update_1_21_9(),

View file

@ -0,0 +1,40 @@
package xyz.alexcrea.cuanvil.update.minecraft;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.update.Version;
import java.util.HashSet;
import java.util.Set;
import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
public class Update_1_19 extends MCUpdate {
public Update_1_19() {
super(new Version(1, 19));
}
@Override
protected void doUpdate() {
var tosave = new HashSet<ConfigHolder>();
updateName(tosave);
for (ConfigHolder holder : tosave) {
holder.saveToDisk(true);
}
}
public static void updateName(@NotNull Set<ConfigHolder> tosave) {
var conflict = ConfigHolder.CONFLICT_HOLDER.getConfig();
addAbsentToList(conflict, "restriction_swift_sneak.enchantments", "minecraft:swift_sneak");
addAbsentToList(conflict, "restriction_swift_sneak.notAffectedGroups", "leggings", "enchanted_book");
ConfigHolder.ITEM_GROUP_HOLDER.reload();
tosave.add(ConfigHolder.DEFAULT_CONFIG);
tosave.add(ConfigHolder.CONFLICT_HOLDER);
}
}

View file

@ -3,19 +3,20 @@ package xyz.alexcrea.cuanvil.update.plugin;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.update.UpdateUtils;
import xyz.alexcrea.cuanvil.update.Version;
import xyz.alexcrea.cuanvil.update.minecraft.Update_1_19;
import xyz.alexcrea.cuanvil.update.minecraft.Update_1_20_5;
import javax.annotation.Nonnull;
import java.util.Set;
public class PUpdate_1_17_7 {
public class PUpdate_1_18 {
public static void handleUpdate(@Nonnull Set<ConfigHolder> toSave) {
// fix only needed for 1.20.5 and above
Version current = UpdateUtils.currentMinecraftVersion();
if (new Version(1, 20, 5).greaterThan(current)) return;
if (new Version(1, 19, 0).greaterThan(current)) return;
Update_1_19.updateName(toSave);
if (new Version(1, 20, 5).greaterThan(current)) return;
Update_1_20_5.updateName(toSave);
}
}

View file

@ -57,6 +57,8 @@ open class CustomAnvil : JavaPlugin() {
// Permission string required to edit the plugin's config
const val editConfigPermission = "ca.config.edit"
// Permission string required to edit the plugin's config
const val giveEnchantmentPermission = "ca.command.enchantment"
// Command Name to reload the config
const val commandReloadName = "anvilconfigreload"

View file

@ -1,9 +1,9 @@
package io.delilaheve.util
import org.bukkit.Material
import org.bukkit.Material.ENCHANTED_BOOK
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.update.UpdateUtils
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import xyz.alexcrea.cuanvil.util.MaxDamageCheckerUtil
@ -21,11 +21,11 @@ object ItemUtil {
*/
fun ItemStack.isEnchantedBook() = type == ENCHANTED_BOOK
private fun maxDamage(damageable: Damageable): Int {
private fun maxDamage(type: Material, damageable: Damageable): Int {
val ver = UpdateUtils.currentMinecraftVersion()
if(ver.major <= 1 && ver.minor <= 20 && ver.patch < 5) return Integer.MAX_VALUE
if(ver.major <= 1 && ver.minor <= 20 && ver.patch < 5) return type.maxDurability.toInt()
return MaxDamageCheckerUtil.getMaxDamage(damageable)
return MaxDamageCheckerUtil.getMaxDamage(type, damageable)
}
/**
@ -37,23 +37,22 @@ object ItemUtil {
first: ItemStack,
second: ItemStack
): Boolean {
(itemMeta as? Damageable)?.let {
val durability = type.maxDurability.toInt()
val firstDamage = (first.itemMeta as? Damageable)?.damage ?: 0
if (firstDamage == 0) return false
val meta = itemMeta
if(meta !is Damageable) return false
val firstDurability = durability - firstDamage
val secondDamage = (second.itemMeta as? Damageable)?.damage ?: 0
val secondDurability = durability - secondDamage
val combinedDurability = firstDurability + secondDurability
val newDurability = min(combinedDurability, durability)
val maxDamage = maxDamage(type, meta)
val damage = (first.itemMeta as? Damageable)?.damage ?: 0
if (damage == 0) return false
val maxDamage = maxDamage(it)
it.damage = min(durability - newDurability, maxDamage)
itemMeta = it
return true
}
return false
val firstDurability = maxDamage - damage
val secondDamage = (second.itemMeta as? Damageable)?.damage ?: 0
val secondDurability = maxDamage - secondDamage
val combinedDurability = firstDurability + secondDurability
val newDurability = min(combinedDurability, maxDamage)
meta.damage = min(maxDamage - newDurability, maxDamage)
this.itemMeta = meta
return true
}
fun ItemStack.unitRepair(

View file

@ -4,6 +4,7 @@ import org.bukkit.ChatColor
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
abstract class CASubCommand: CommandExecutor {
@ -43,4 +44,14 @@ abstract class CASubCommand: CommandExecutor {
return "no description"
}
protected fun allEnchantmentsByName(): Collection<String> {
val names = mutableSetOf<String>()
for (enchantment in CAEnchantmentRegistry.getInstance().values()) {
names.add(enchantment.name)
names.add(enchantment.key.toString())
}
return names
}
}

View file

@ -7,7 +7,6 @@ import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.command.TabCompleter
import xyz.alexcrea.cuanvil.util.MetricsUtil
import java.util.ArrayList
class CustomAnvilCommand(plugin: CustomAnvil) : CommandExecutor, TabCompleter {
@ -18,13 +17,14 @@ class CustomAnvilCommand(plugin: CustomAnvil) : CommandExecutor, TabCompleter {
private val editConfigCommand = EditConfigExecutor()
private val helpCommand = HelpExecutor()
private val commands = ImmutableMap.of(
private val commands: ImmutableMap<String, CASubCommand> = ImmutableMap.of(
"gui", editConfigCommand,
"config", editConfigCommand,
"reload", ReloadExecutor(),
"diagnostic", DiagnosticExecutor(),
"debug", DebugToggleExecutor(),
"help", helpCommand,
"enchant", EnchantExecutor(),
)
init {
@ -95,9 +95,11 @@ class CustomAnvilCommand(plugin: CustomAnvil) : CommandExecutor, TabCompleter {
}
//assumed all provided tab completed string are lowercase
val prefix = args[args.size - 1]
return result.stream()
.filter { it.startsWith(args[args.size - 1]) }
.filter { it.startsWith(prefix) }
.sorted()
.toList()
}
}

View file

@ -7,7 +7,6 @@ import org.bukkit.entity.HumanEntity
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
import xyz.alexcrea.cuanvil.gui.config.global.EnchantConfigGui
import xyz.alexcrea.cuanvil.gui.config.global.ItemConfigGui
@ -103,16 +102,6 @@ class EditConfigExecutor : CASubCommand() {
// Tab completer
// ---------------
private fun allEnchantmentsByName(): Collection<String> {
val names = mutableSetOf<String>()
for (enchantment in CAEnchantmentRegistry.getInstance().values()) {
names.add(enchantment.name)
names.add(enchantment.key.toString())
}
return names
}
override fun tabCompleter(sender: CommandSender, args: Array<out String>, list: MutableList<String>) {
list.addAll(
when (args.size) {
@ -127,7 +116,6 @@ class EditConfigExecutor : CASubCommand() {
else -> listOf()
}
)
}
}

View file

@ -0,0 +1,113 @@
package xyz.alexcrea.cuanvil.command
import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.entity.HumanEntity
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir
class EnchantExecutor : CASubCommand() {
override fun allowed(sender: CommandSender): Boolean {
return sender.hasPermission(CustomAnvil.giveEnchantmentPermission)
}
override fun description(): String {
return "Allows to set enchantment to holden item"
}
override fun executeCommand(
sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>
): Boolean {
if (sender !is HumanEntity) return true
if (!allowed(sender)) {
sender.sendMessage("No permission to execute this command")
return true
}
when (args.size) {
0 -> {
sender.sendMessage("Missing enchantment parameter")
return true
}
1 -> {
sender.sendMessage("Missing level parameter")
return true
}
}
val enchants = EnchantmentApi.getListByName(args[0].lowercase())
if (enchants.isEmpty()) {
sender.sendMessage("Enchantment not found: ${args[0]}")
return true
}
val enchant = enchants.iterator().next()
val level = args[1].toIntOrNull()?.coerceIn(0, ConfigOptions.ENCHANT_LIMIT)
if (level == null) {
sender.sendMessage("Invalid number: ${args[1]}")
return true
}
val inHand = sender.inventory.itemInMainHand
if (inHand.isAir) {
sender.sendMessage("Cannot enchant this item")
return true
}
if (level == 0) {
enchant.removeFrom(inHand)
sender.sendMessage("${enchant.prettyName} removed")
} else {
enchant.addEnchantmentUnsafe(inHand, level)
sender.sendMessage("${enchant.prettyName} set to level $level")
}
return true
}
override fun tabCompleter(sender: CommandSender, args: Array<out String>, list: MutableList<String>) {
if (sender !is HumanEntity) return
list.addAll(
when (args.size) {
1 -> allEnchantmentsByName()
2 -> findEnchantmentLevels(sender, args[0])
else -> listOf()
}
)
}
private fun findEnchantmentLevels(
sender: HumanEntity,
name: String
): Collection<String> {
val enchants = EnchantmentApi.getListByName(name.lowercase())
if (enchants.isEmpty()) return listOf()
val enchant = enchants.iterator().next()
val limit = ConfigOptions.enchantLimit(enchant)
val result = mutableListOf<String>()
for (i in 1 until limit + 1) {
result.add(i.toString())
}
val inHand = sender.inventory.itemInMainHand
if (enchant.isEnchantmentPresent(inHand))
result.add("0")
return result
}
}

View file

@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.command
import io.delilaheve.CustomAnvil
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import xyz.alexcrea.cuanvil.api.event.CAConfigReadyEvent
import xyz.alexcrea.cuanvil.config.ConfigHolder

View file

@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.group
import io.delilaheve.CustomAnvil
import org.bukkit.NamespacedKey
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment
@ -33,10 +32,6 @@ class EnchantConflictManager {
// Path for the maximum number of enchantment before validating the conflict
const val ENCH_MAX_PATH = "maxEnchantmentBeforeConflict"
// Path for a flag: if the enchantment will be used in the last supported version
// TODO maybe replace this system by a list of "future" enchantment.
private const val FUTURE_USE_PATH = "useInFuture"
// Default name for a joining group
const val DEFAULT_GROUP_NAME = "joinedGroup"
}
@ -101,8 +96,6 @@ class EnchantConflictManager {
itemManager: ItemGroupManager,
conflictName: String
): EnchantConflictGroup {
// Is it planed for the future
val futureUse = section.getBoolean(FUTURE_USE_PATH, false)
// Create conflict
val conflict = createConflictObject(section, itemManager, conflictName)
// Read and add enchantment to conflict
@ -110,17 +103,13 @@ class EnchantConflictManager {
for (enchantName in enchantList) {
val enchants = getEnchantByIdentifier(enchantName)
if (enchants.isEmpty()) {
if (!futureUse) { //TODO future use will be deprecated once the new update system is finished
CustomAnvil.instance.logger.warning("Enchantment $enchantName do not exist but was asked for conflict $conflictName")
}
CustomAnvil.instance.logger.warning("Enchantment $enchantName do not exist but was asked for conflict $conflictName")
continue
}
conflict.addEnchantments(enchants)
}
if (conflict.getEnchants().isEmpty()) {
if (!futureUse) { //TODO future use will be deprecated once the new update system is finished
CustomAnvil.instance.logger.warning("Conflict $conflictName do not have valid enchantment, it will not do anything")
}
CustomAnvil.instance.logger.warning("Conflict $conflictName do not have valid enchantment, it will not do anything")
}
val conflictsAfterLevel = section.getConfigurationSection(CONFLICT_AFTER_LEVEL_LIST_PATH)

View file

@ -5,16 +5,11 @@ import io.delilaheve.util.ConfigOptions
object AnvilUseTypeUtil {
/*
By stupidity of kotlin not allowing static function outside of companion object I need to do another util class only for 1 function:
By kotlin not having static function outside of companion object I need to do another util class only for 1 function:
Companion object on enum class seems to get initialized AFTER the enum itself
that mean. if you want to call a static function on the enum class itself YOU CAN'T. you need to do this stupid thing
or you can make a stupid top level function OR object that even worse because of global scope pollution
(btw was gpt ""solution"". fortunately I do not "vibe code" so I do what I know instead of stupid AI solution & code)
I mean, this is still global scope pollution bc of a USELESS class that SHOULD not exist but as a class is better than a random *ss function
sorry for the rent but this made me frustrated
Note: I still like a lot of part of kotlin compared to java but this part is one that I hate
that mean. if you want to call a static function on enum construction YOU CAN'T. you need to do this stupid thing,
or you can make a stupid top level function or object that at least as bad as this
I mean, this is still an unnecessary class that should not exist but as a class is better than a random function in my opinion
*/
/**
* Get config path for normal anvil use

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit most of these config.
# It is recommended that you use /ca config to edit most of these config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#
@ -160,13 +160,6 @@ restriction_sweeping_edge:
enchantments: [ minecraft:sweeping, minecraft:sweeping_edge ]
notAffectedGroups: [ enchanted_book, swords ]
# Do not exist in 1.18, that mean useInFuture will be set to true
# useInFuture set to true also mean it will not warn if there is an issue
restriction_swift_sneak:
useInFuture: true
enchantments: [ minecraft:swift_sneak ]
notAffectedGroups: [ enchanted_book, leggings ]
restriction_thorns:
enchantments: [ minecraft:thorns ]
notAffectedGroups: [ enchanted_book, armors ]

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#

View file

@ -43,6 +43,9 @@ permissions:
ca.command.diagnostic:
default: op
description: Get debug information about the plugin and server
ca.command.enchantment:
default: op
description: Allows to set enchantment to holden item
ca.config.edit:
default: op
description: Allow administrator to edit the plugin's config in game

View file

@ -1,5 +1,5 @@
#
# It is recommended that you use /configanvil to edit theses config.
# It is recommended that you use /ca config to edit theses config.
# You can still manually edit here if you like to. but if you do, don't forget to /ca reload after you changes !
#