Add HiddenOre

This commit is contained in:
okx-code
2024-02-22 21:31:00 +00:00
30 changed files with 7974 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
# Editorconfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
indent_style = tab
indent_size = 4
end_of_line = lf
insert_final_newline = true
continuation_indent_size = 8
[*.java]
indent_style = tab
indent_size = 4
[*.xml]
indent_style = tab
indent_size = 2
[*.yml]
indent_style = space
indent_size = 2

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Devoted
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,152 @@
# HiddenOre
Brought to you by the developers from https://www.reddit.com/r/Devoted and https://www.github.com/DevotedMC -- check out our server at play.devotedmc.com and our website at www.devotedmc.com .
Massively configurable, low-impact plugin to allow post world generation ore balance, via either drops, or ad-hoc generation of ores into the blocks of the world on-demand. It works as a wonderful anti-xray plugin, and as a powerful incentive tool for mining.
##Notes on Minecraft 1.18
HiddenOre now supports the overworlds new 384 block height in total. The `clear_ores` feature of HiddenOre has now also been updated to support replacing specific ores with specific replacements. Default confiurations have been updated to reflect this with an example being:
```yaml
clear_ores:
main_world:
world: world
replace:
IRON_ORE: STONE
REDSTONE_ORE: STONE
GOLD_ORE: STONE
COAL_ORE: STONE
DIAMOND_ORE: STONE
EMERALD_ORE: STONE
LAPIS_ORE: STONE
DEEPSLATE_IRON_ORE: DEEPSLATE
DEEPSLATE_REDSTONE_ORE: DEEPSLATE
DEEPSLATE_GOLD_ORE: DEEPSLATE
DEEPSLATE_COAL_ORE: DEEPSLATE
DEEPSLATE_DIAMOND_ORE: DEEPSLATE
DEEPSLATE_EMERALD_ORE: DEEPSLATE
DEEPSLATE_LAPIS_ORE: DEEPSLATE
end_world:
world: world_nether
replace:
NETHER_QUARTZ_ORE: NETHERRACK
```
Do be aware if you are adding ores into deepslate, you cannot add `DEEPSLATE` as an aggregate of stone, it must have its own section under the `blocks` ConfigurationSection
## Notes on Minecraft 1.14
Largely unchanged from 1.13, however it is worth noting that if you previouly used the durability match for "first hit", "last hit" or specific durability hit bonuses, you will need to recreate those configs, as durability is now in the metadata for items.
## Notes on Minecraft 1.13
Minecraft 1.13, and Bukkit/Spigot's implementation, introduces a wrinkle for my admittedly "deprecated" approach to sub types of materials; namely,
if you're defining a normal ore sequence, you can no longer just define a block config for "STONE" -- you now need to define a block config for
STONE, ANDESITE, DIORITE, and GRANITE, and if you use generation, you need to define allowed types for each.
The benefit with this approach, however, is even greater control over every detail of ore generation within your worlds. I wanted to include this note
at the top, however, to highlight the importance of this change. You can find an example of this done in detail, in config.yml.
Additional 1.13 notes are included below in the normal place.
## Overview
Every time you break a block, naturally placed, there will be a lottery run. This will occasionally drop cool stuff instead of the block you broke, or generate new blocks around the miner.
Typical use case is for stone -- instead of dropping stone, occasionally drop ore instead, or generate awesome blocks around the miner.
For example:
1:100,000 chance to find prismarine.
1:3000 chance to find a diamond.
1:10000 chance to find a vein of 10 diamond ore.
Etc.
This plugin is fully configurable with biome-specific settings, tool restrictions, and limited player state restrictions, allowing a significant degree of options and specificity.
As of 1.5.2, you can upend this regime and construct intersecting probability fields via "Noise" generators, which allow a persistent hidden map of drop density regions. The probabilities are a bit harder to constrain, but still quite measurable with some trial and error. Unfortunately, due to the nature of noise generators, once you choose a config it will be difficult to rebalance smoothly -- e.g. player's discoveries of high density regions will likely be rendered invalid on a config change. The traditional probability approach doesn't really suffer from this as players would simply notice changes globally or within defined regions, and the "pain" would be shared, without impacting much secret knowledge. Regardless, both are excellent options for administrators and we're proud to finally support it on the mainline w/ all of our existing performance and anti-exploit features intact.
Note that unlike other branches, our noise-based generation approach is embedded in the existing configuration flow, and is handled as a new feature regime instead of a completely "other" element. This allows you to freely mix and match these "veins", drops, transforms, commands and the like without loss of functionality. As always, the goal is max features and control for you.
## Details
The raw technical details:
On block break, checks the config to see if a lottery has been defined for this block. Optionally, a quick check for presence of Silk Touch is done.
You can specify multiple drops, where each drop is computed as an independent probability against the break, and the sum of drops is spawned into the world.
Alternatively, you can indicate only a single drop-type is allowed. In this case a single random number is generated and tested against a probability distribution of possible drops. This allows replication of Minecraft behavior (in terms of how ores are generated).
The chance to drop for each type of drop against a type of broken block is configurable.
With Minecraft 1.13, subtype specification is no longer directly possible; future releases may add groups of native materials to emulate this prior behavior, but for now it has been removed.
You can apply biome-level chance, level, and amount modifiers.
Drops can be restricted to specific Y levels.
Drops can be restricted to specific tools, with a high degree of configure-ability. Check `config.yml` and `config-advanced.yml`.
Drops chances can be modifed based on player potion / effect state and level of that state. Currently haste, mining fatigue, nausea, blindness, and luck / bad luck states are supported. See `config.yml`.
Included is a default config that effectively mirrors Minecraft Vanilla orespawn; it should be possible to generate a normal MC world with no ores or caves, but with this plugin allow effectively normal vanilla riches. Consider it the ultimate XRay defense; you cannot see what literally doesn't exist.
Supports tracking of breaks to prevent "gaming" the system for more drops. Extra event handlers watch for game attempts and directly penalize that "chunk" (technically, the chunk slice). An extra "highly localized" round robin list keeps track of recent breaks and places to _completely_ prevent break-place based attempts at exploits. Finally, a new tracker keeps track of _each block_ that is broken or interacted, and prevents it from being converted into ore or producing drops.
You can specify more then one config per block type, although as of Minecraft 1.13 only the first will be used. Also note that in terms of drops, the first matching config to be encountered will be used; so keep that in mind.
You can specify custom messages for specific types of drops, allowing "uber"-finds to have a unique message.
You can turn on or off the debug using `/hiddenore debug true` or `false`. Be warned, if people are digging or pistons are active, this will spam the console _very_ badly.
Supports saving and loading of the tracking database, and fully adheres to /reload with no difficulties.
As of 1.4.2, full multiple world support, via name or UUID.
As of 1.5.2, you can specify "veinNatures" which allow complex simplex-noise based distributions of ore probabilities. These are hard to compute as the mechanics aren't as straightforward, but the end result can be quite nice, allowing for layered, overlapping, or otherwise clever intersections of ores. Players like it too, as it allows a kind of density distribution, which once "found" can be leveraged.
In mainline as with all our other features, the full suite of configuration options, including drops, transforms, commands, biome and state modifiers, etc can be applied to veinNature configs. See config-veins.yml for examples.
I'm probably missing some other details but that's it for now.
### TODO / In progress features:
* Configure which tool to "use" for cave dusting. Default right now is Diamond Pickaxe.
* Better documentation
### Feature Augment List:
**v2.0.0** Updated to Minecraft 1.18
**v1.6.0** Generaly update to 1.14.4. Thanks Maxopoly!
**v1.5.3** Some general fixes and improvements to block-level exploit tracking.
**v1.5.2** Adding in CivClassic style "veins", which are basically just distributions of ore backed by persistent noise fields instead of the classic HiddenOre probability distribution functions. The outcomes are probabilistically compatible, but the generation is quite a bit different. Check out config-veins.yml for a hopefully clear example. Note that due to fundamental implementation differences, configurations made for CivClassic veins are not portable to this implementation, as their implementation refactors the configs and introduces veins as a separate flow, with only some of the tradition drop or generation capabilities; our approach to veins preserves all the existing feature-rich environment and simply implements veins as a probability distribution modifier to existing drop configs (Expanding the features in 1.5.1 for global drop config design as well).
**v1.5.1** Adding in a few key QoL features for continued 1.13 support. Specifically (and see config.yml for examples and details) you can now specify drops in a global section, and pick those drops by name in the block drop configurations. You can also specify multiple blocks for whom the drop config should apply, all in the same block config. This should allow significantly improved "terseness" to configs, above and beyond what the prior subtype system allowed. Note you can mix and match global drops and local drop configs for a block, but you cannot mix single or multiple block types for a single block config.
**v1.5.0** Added Minecraft 1.13 support. Note that you WILL need to REDO your config. Although your config will load _as is_ it is unlikely to work as expected. Subtype support has been entirely removed and declarations of subtypes will be ignored. Spigot itself has renamed **many** materials to match their Minecraft normative style, and there is no backwards compatibility in HiddenOre. As of 1.5.0, no prior release of Minecraft is guaranteed active support, and will only be provided on a best-effort basis, if at all. IMPORTANT NOTE on config: All ItemStack serialization has been changed! There is a new `v:` specification. I've emulated it in the demonstration configs, but to avoid messy and unsupported prior version settings, re-export your serialized drops.
**v1.4.2** Added full multiple world support. Standard config is used as default for any world that does not have a specific config. A new section, `worlds`
can be specified, each subsection is either the UUID or name of the world with a specific config. A single `blocks` subsection under the world identifier contains all the block configurations for that world. It is configured like the default, but within the world's blocks subsection. Check the configs for examples.
**v1.4.1** Live use of new tracker shows its disk use is much increased. Added a configuration option to explicitly disable it. Added config example of Command "drops" and some fixes.
**v1.4.0** New exploit tracker that tracks the actual blocks broken or exposed. This will fully prevent the "but I already checked that block" problem. Heuristic tracking is, for now, still active.
**v1.3.2** You can now run a command on block break. If you use reward crates, could gift, or custom /give, etc -- runs as SERVER so be careful. Use %player% to inject the player name into the command, or %uuid% to inject the player's Minecraft UUID.
**v1.3.1** Added a command for OPs only that generates all the drops configured. It has some quirks, but type /hiddenore generate to spawn the items.
**v1.2.9** Support for dusting the caves in your map with ore based on your active config; kind of an addendum to v1.2.7's feature. Can be used to prevent the "boring caves" problem when a world is otherwise devoid of ore from the 1.2.7 feature. Don't use your final config, do use a Generate only config, and do turn off drops if generate fails.
**v1.2.8** Support for altering drop chance based on 6 basic player effect states, generated by potion or beacon. Configurable by drop and biome (biome acts as override)
**v1.2.7** Experimental feature to allow stripping a world of ores during the generation phase. Fully configurable per-world by name; you can
set it to replace any set of materials with a single material. Also includes a new anti-bypass method to directly target that "initial return" that can still occur from generators and place-break-place-break cycles

View File

@@ -0,0 +1,9 @@
plugins {
id("io.papermc.paperweight.userdev")
}
version = "2.0.0-SNAPSHOT"
dependencies {
paperDevBundle("1.18.2-R0.1-SNAPSHOT")
}

View File

@@ -0,0 +1,135 @@
package com.github.devotedmc.hiddenore;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class BlockConfig {
private NamespacedKey material;
public Collection<NamespacedKey> validGenTypes;
public boolean dropMultiple;
public boolean suppressDrops;
private Map<String, DropConfig> dropConfigs;
private String prefix;
public BlockConfig(NamespacedKey material, boolean dropMultiple, boolean suppressDrops, String prefix, Collection<NamespacedKey> validGenTypes) {
this.material = material;
this.dropMultiple = dropMultiple;
this.suppressDrops = suppressDrops;
this.dropConfigs = new HashMap<String, DropConfig>();
this.prefix = prefix;
this.validGenTypes = validGenTypes;
}
public NamespacedKey getMaterialKey() {
return this.material;
}
public boolean checkBlock(Block check) {
return material.equals(check.getType().getKey());
}
/**
* Allows check against an expanded list of valid blocks that ores can generate in -- companion blocks, effectively.
* For instance, if you separately configure stone types but want generation in stone of all types;
*
* @param check the block to check
* @return true if we can generate into this block
*/
public boolean checkGenerateBlock(Block check) {
if (checkBlock(check)) return true;
if (validGenTypes == null) return false;
for (NamespacedKey wrapper : validGenTypes) {
if (wrapper.equals(check.getType().getKey())) return true;
}
return false;
}
public String getPrefix(String drop) {
if (drop == null) return prefix;
DropConfig dc = dropConfigs.get(drop);
return (dc == null || dc.prefix == null) ? prefix : dc.prefix;
}
public boolean hasCustomPrefix(String drop) {
if (drop == null) return false;
DropConfig dc = dropConfigs.get(drop);
return (dc == null || dc.prefix == null) ? (prefix != null) : true;
}
public void addDropConfig(String drop, DropConfig dropConfig) {
dropConfigs.put(drop, dropConfig);
}
public Set<String> getDrops() {
return dropConfigs.keySet();
}
public DropConfig getDropConfig(String drop) {
return dropConfigs.get(drop);
}
public String getDropConfig(double dice, String biome, ItemStack tool, Player player, Location loc) {
// accrue possible drops based on biome / tool
// check dice against stacked probabilities
double cumChance = 0.0d;
double localChance = 0.0d;
int counted = 0;
int blockY = loc.getBlockY();
for (Map.Entry<String, DropConfig> dce : dropConfigs.entrySet()) {
DropConfig dc = dce.getValue();
if (dc.dropsWithTool(biome, tool) && blockY <= dc.getMaxY(biome)
&& blockY >= dc.getMinY(biome)) {
ToolConfig tc = dc.dropsWithToolConfig(biome, tool);
localChance = dc.getChance(biome) * (tc == null ? 1.0 : tc.getDropChanceModifier()) * dc.getStateChance(biome, player);
VeinConfig vc = dc.getVeinNature();
if (vc != null) {
localChance *= vc.getOreChance(loc);
/*DIAGNOSTICS
HiddenOre.getPlugin().getLogger()
.log(Level.INFO, "Base chance {0}| tool mod {1}| vein mod {2}| totalChance {3}| tc {4}",
new Object[] {Double.toString(dc.getChance(biome)),
Double.toString(tc == null ? 1.0 : tc.getDropChanceModifier()),
vc.getOreChance(loc), localChance, (tc == null ? null : tc.toString())});*/
} else {
/*DIAGNOSTICS
HiddenOre.getPlugin().getLogger()
.log(Level.INFO, "Base chance {0}| tool mod {1}| totalChance {2}| tc {3}",
new Object[] {Double.toString(dc.getChance(biome)),
Double.toString(tc == null ? 1.0 : tc.getDropChanceModifier()),
localChance, (tc == null ? null : tc.toString())});*/
}
if(localChance < 0){
continue;
}
if (dice >= cumChance && dice < cumChance + localChance) {
return dce.getKey();
}
cumChance += localChance;
counted++;
}
}
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger()
.log(Level.INFO, "{0} tested {1} cumm {2} dice", new Object[] {counted, Double.toString(cumChance), Double.toString(dice)});
}
return null;
}
}

View File

@@ -0,0 +1,515 @@
package com.github.devotedmc.hiddenore;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.data.BlockData;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.inventory.ItemStack;
import com.github.devotedmc.hiddenore.listeners.ConfigDeferralListener;
/**
* Someday it might be nice to refactor this to be a proper object.
*
* For now, this holds the configs for HiddenOre, in an instance-backed semi-private collective.
*
* @author soexpso, programmerdan
*
*/
public final class Config {
public static Config instance;
public static boolean isDebug;
public String defaultPrefix;
public boolean alertUser;
public boolean listDrops;
public boolean ignoreSilktouch;
public Map<String, DropConfig> dropConfigs;
public Map<UUID, Map<NamespacedKey, List<BlockConfig>>> blockConfigs;
public Map<String, Map<NamespacedKey, List<BlockConfig>>> preloadBlockConfigs;
public Map<String, NameConfig> prettyNames;
public Map<String, PlayerStateConfig> stateMasterList;
private static FileConfiguration file;
private static String trackFileName;
private static File trackFile;
public static long trackSave;
private static boolean useMapSave;
private static String mapFileName;
private static File mapFile;
public static long mapSave;
public int transformAttemptMultiplier = 3;
public static boolean caveOres = false;
private Config() {
dropConfigs = new HashMap<String, DropConfig>();
blockConfigs = new HashMap<UUID, Map<NamespacedKey, List<BlockConfig>>>();
preloadBlockConfigs = new HashMap<String, Map<NamespacedKey, List<BlockConfig>>>();
prettyNames = new HashMap<String, NameConfig>();
stateMasterList = new HashMap<String, PlayerStateConfig>();
trackFileName = "tracking.dat";
trackSave = 90000l;
useMapSave = true;
mapFileName = "map.dat";
mapSave = 90000l;
alertUser = false;
listDrops = false;
isDebug = false;
ignoreSilktouch = false;
defaultPrefix = "You found hidden ore!";
transformAttemptMultiplier = 3;
}
public static void loadConfig() {
try {
file = HiddenOre.getPlugin().getConfig();
doLoad();
} catch (Exception e) {
HiddenOre.getPlugin().getLogger().log(Level.WARNING, "An error occured while loading config!", e);
}
}
public static void doLoad() {
Config i = new Config();
isDebug = file.getBoolean("debug", isDebug);
caveOres = file.getBoolean("caveOres", caveOres);
trackFileName = file.getString("track_file", trackFileName);
trackFile = new File(HiddenOre.getPlugin().getDataFolder(), trackFileName);
trackSave = file.getLong("track_save_ticks", trackSave);
useMapSave = file.getBoolean("map_save_active", useMapSave);
mapFileName = file.getString("map_file", mapFileName);
mapFile = new File(HiddenOre.getPlugin().getDataFolder(), mapFileName);
mapSave = file.getLong("map_save_ticks", mapSave);
i.ignoreSilktouch = file.getBoolean("ignore_silktouch", i.ignoreSilktouch);
i.alertUser = file.getBoolean("alert_user", i.alertUser);
i.listDrops = file.getBoolean("list_drops", i.listDrops);
i.defaultPrefix = file.getString("prefix", i.defaultPrefix);
i.transformAttemptMultiplier = file.getInt("transform_attempt_multiplier", i.transformAttemptMultiplier);
ConfigurationSection prettyNames = file.getConfigurationSection("pretty_names");
if (prettyNames != null) {
for (String key : prettyNames.getKeys(false)) {
NameConfig nc = null;
/*
* As of 1.13, subtypes are removed.
*/
if (prettyNames.isConfigurationSection(key)) {
ConfigurationSection pName = prettyNames.getConfigurationSection(key);
String name = pName.getString("name", key);
nc = new NameConfig(name);
// for 1.13, we're ignoring subtypes. We've kept this stub to encourage cross
// compat, but they are ignored if designated.
// No attempt is made to bridge compatibility, even where possible.
} else if (prettyNames.isString(key)) {
String name = prettyNames.getString(key, key);
nc = new NameConfig(name);
}
if (nc != null) {
i.prettyNames.put(key, nc);
}
}
} else {
HiddenOre.getPlugin().getLogger().info("No Pretty Names specified.");
}
ConfigurationSection tools = file.getConfigurationSection("tools");
if (tools != null) {
for (String key : tools.getKeys(false)) {
if (tools.isConfigurationSection(key)) {
ToolConfig.initTool(tools.getConfigurationSection(key));
HiddenOre.getPlugin().getLogger().info("Tool " + key + " initialized");
}
}
} else {
HiddenOre.getPlugin().getLogger().info("No tool configurations specified. This might cause issues.");
}
ConfigurationSection states = file.getConfigurationSection("states");
if (states != null) {
for (String state : states.getKeys(false)) {
if (states.isConfigurationSection(state)) {
ConfigurationSection stateConfig = states.getConfigurationSection(state);
PlayerStateConfig pstateConfig = new PlayerStateConfig();
if (stateConfig.contains("haste")) {
pstateConfig.hasteRates = stateConfig.getDoubleList("haste");
}
if (stateConfig.contains("fatigue" )) {
pstateConfig.fatigueRates = stateConfig.getDoubleList("fatigue");
}
if (stateConfig.contains("nausea")) {
pstateConfig.nauseaRates = stateConfig.getDoubleList("nausea");
}
if (stateConfig.contains("luck")) {
pstateConfig.luckRates = stateConfig.getDoubleList("luck");
}
if (stateConfig.contains("blindness")) {
pstateConfig.blindnessRates = stateConfig.getDoubleList("blindness");
}
if (stateConfig.contains("badluck")) {
pstateConfig.badluckRates = stateConfig.getDoubleList("badluck");
}
i.stateMasterList.put(state, pstateConfig);
HiddenOre.getPlugin().getLogger().info("State " + state + " initialized");
}
}
}
ConfigurationSection drops = file.getConfigurationSection("drops");
if (drops != null) {
for (String drop : drops.getKeys(false)) {
if (drops.isConfigurationSection(drop)) {
i.dropConfigs.put(drop, grabDropConfig(drops, drop));
}
}
}
ConfigurationSection blocks = file.getConfigurationSection("blocks");
if (blocks != null) { // default or legacy
Map<NamespacedKey, List<BlockConfig>> worldBlockConfigs = new HashMap<NamespacedKey, List<BlockConfig>>();
grabBlocks("default", worldBlockConfigs, blocks, i);
i.blockConfigs.put(null, worldBlockConfigs);
}
ConfigurationSection worlds = file.getConfigurationSection("worlds");
if (worlds != null) { // has per-world blocks!
for (String world : worlds.getKeys(false)) {
UUID worlduid = null;
try {
worlduid = UUID.fromString(world);
if (HiddenOre.getPlugin().getServer().getWorld(worlduid) == null) {
System.err.println("Although it seems to be a UUID, " + world + " no match found yet.");
worlduid = null;
}
} catch (Exception e) {
System.out.println("World not defined by UUID");
worlduid = null;
}
if (worlduid == null) {
try {
worlduid = HiddenOre.getPlugin().getServer().getWorld(world).getUID();
} catch (Exception f) {
System.out.println("World not defined by Name; unable to match " + world + " with loaded world.");
}
}
Map<NamespacedKey, List<BlockConfig>> worldBlockConfigs = null;
if (worlduid == null) {
System.err.println("Unable to match world " + world + " with loaded world, registering for post-load.");
worldBlockConfigs = i.preloadBlockConfigs.get(world);
} else {
worldBlockConfigs = i.blockConfigs.get(worlduid);
}
if (worldBlockConfigs == null) {
worldBlockConfigs = new HashMap<NamespacedKey, List<BlockConfig>>();
}
ConfigurationSection worldConfig = worlds.getConfigurationSection(world);
if (worldConfig != null) {
ConfigurationSection worldBlocks = worldConfig.getConfigurationSection("blocks");
if (worldBlocks != null) {
grabBlocks(world, worldBlockConfigs, worldBlocks, i);
}
}
if (worlduid == null) {
i.preloadBlockConfigs.put(world, worldBlockConfigs);
} else {
i.blockConfigs.put(worlduid, worldBlockConfigs);
}
}
}
if (i.preloadBlockConfigs.size() > 0) {
// some world configs won't immediately resolve!
// register a listener for world init / loading to check if we can resolve them!
HiddenOre.getPlugin().getServer().getPluginManager()
.registerEvents(new ConfigDeferralListener(), HiddenOre.getPlugin());
}
instance = i;
}
private static void grabBlocks(String world, Map<NamespacedKey, List<BlockConfig>> worldBlockConfigs, ConfigurationSection blocks, Config i) {
if (blocks != null) {
for (String sourceBlock : blocks.getKeys(false)) {
HiddenOre.getPlugin().getLogger().info("Loading config for " + sourceBlock + " for world " + world);
ConfigurationSection block = blocks.getConfigurationSection(sourceBlock);
String cBlockName = block.getString("material");
List<NamespacedKey> cBlockKeys = new ArrayList<NamespacedKey>();
if (cBlockName == null) {
ConfigurationSection cBlockNames = block.getConfigurationSection("materials");
if (cBlockNames == null) {
HiddenOre.getPlugin().getLogger().warning("Failed to find material or materials for " + sourceBlock);
continue;
} else {
for (String cBlockN : cBlockNames.getKeys(false)) {
ConfigurationSection cBlockS = cBlockNames.getConfigurationSection(cBlockN);
String cBlockName2 = cBlockS.getString("material");
Material cBlockMat = Material.getMaterial(cBlockName2);
if (cBlockMat == null) {
HiddenOre.getPlugin().getLogger().warning("Failed to find material for " + cBlockName2);
continue;
} else {
cBlockKeys.add(cBlockMat.getKey());
}
}
}
} else {
try {
Material cBlockMat = Material.getMaterial(cBlockName);
if (cBlockMat == null) {
HiddenOre.getPlugin().getLogger().warning("Failed to find material for " + cBlockName);
continue;
} else {
cBlockKeys.add(cBlockMat.getKey());
}
} catch (Exception e) {
HiddenOre.getPlugin().getLogger().warning("Failed to find material for " + cBlockName);
continue;
}
}
if (cBlockKeys.isEmpty()) {
HiddenOre.getPlugin().getLogger().warning("No material(s) set for " + sourceBlock);
continue;
}
String cPrefix = block.getString("prefix", null);
Boolean cMultiple = block.getBoolean("dropMultiple", false);
Boolean cSuppress = block.getBoolean("suppressDrops", false);
// add what blocks should be transformed, if transformation is used.
ConfigurationSection validTransforms = block.getConfigurationSection("validTransforms");
List<NamespacedKey> transformThese = new ArrayList<NamespacedKey>();
if (validTransforms != null) {
for (String transformL : validTransforms.getKeys(false)) {
ConfigurationSection transform = validTransforms.getConfigurationSection(transformL);
String tBlockName = transform.getString("material");
try {
Material tBlockMat = Material.getMaterial(tBlockName);
NamespacedKey tBlockKey = tBlockMat == null ? null : tBlockMat.getKey();
if (tBlockKey != null) {
transformThese.add(tBlockKey);
}
} catch (Exception e) {
HiddenOre.getPlugin().getLogger().warning("Failed to find valid transform material for " + tBlockName);
continue;
}
}
} else {
validTransforms = null;
}
// we can set up multiple per def, here we handle it.
for (NamespacedKey cBlockKey : cBlockKeys) {
BlockConfig bc = new BlockConfig(cBlockKey, cMultiple, cSuppress, cPrefix, new ArrayList<NamespacedKey>(transformThese));
// now add drops.
ConfigurationSection drops = block.getConfigurationSection("drops");
if (drops != null) {
for (String sourceDrop : drops.getKeys(false)) {
bc.addDropConfig(sourceDrop, grabDropConfig(drops, sourceDrop));
}
}
// mix and match in-house drops and "from list" shared drops.
List<String> dropList = block.getStringList("dropList");
if (dropList != null) {
for (String drop : dropList) {
if (i.dropConfigs.containsKey(drop)) {
bc.addDropConfig(drop, i.dropConfigs.get(drop));
}
}
}
List<BlockConfig> bclist = worldBlockConfigs.get(cBlockKey);
if (bclist == null) {
bclist = new LinkedList<BlockConfig>();
}
bclist.add(bc);
worldBlockConfigs.put(cBlockKey, bclist);
}
}
} else {
HiddenOre.getPlugin().getLogger().info("No blocks specified (Why are you using this plugin?)");
}
}
private static DropConfig grabDropConfig(ConfigurationSection drops, String sourceDrop) {
HiddenOre.getPlugin().getLogger().info("Loading config for drop " + sourceDrop);
ConfigurationSection drop = drops.getConfigurationSection(sourceDrop);
String dPrefix = drop.getString("prefix", null);
@SuppressWarnings("unchecked")
List<ItemStack> items = (List<ItemStack>) drop.getList("package", new ArrayList<ItemStack>());
boolean transformIfAble = drop.getBoolean("transformIfAble", false);
boolean transformDropIfFails = drop.getBoolean("transformDropIfFails", false);
int transformMaxDropsIfFails = drop.getInt("transformMaxDropsIfFails", 1);
String command = drop.getString("command", null);
VeinConfig veinNature = null;
ConfigurationSection veinNatureConfig = drop.getConfigurationSection("veinNature");
if (veinNatureConfig != null) {
long densitySeed = veinNatureConfig.getLong("densitySeed", 1);
long heightSeed = veinNatureConfig.getLong("heightSeed", 2);
double density = veinNatureConfig.getDouble("density", 1.0);
double maxSpan = veinNatureConfig.getDouble("maxSpan", 0.0);
double densityBonus = veinNatureConfig.getDouble("densityBonus", 0.0);
double areaHeight = veinNatureConfig.getDouble("areaHeight", 1.0);
double areaSpan = veinNatureConfig.getDouble("areaSpan", 0.0);
double heightLength = veinNatureConfig.getDouble("heightLength", 1.0);
double densityLength = veinNatureConfig.getDouble("densityLength", 1.0);
boolean forceVisible = veinNatureConfig.getBoolean("forceVisibleTransform", false);
veinNature = new VeinConfig(densitySeed, heightSeed, density, maxSpan, densityBonus, areaHeight, areaSpan,
heightLength, densityLength, forceVisible);
}
DropConfig dc = new DropConfig(sourceDrop, DropItemConfig.transform(items), command,
transformIfAble, transformDropIfFails, transformMaxDropsIfFails,
dPrefix, grabLimits(drop, new DropLimitsConfig()), veinNature);
ConfigurationSection biomes = drop.getConfigurationSection("biomes");
if (biomes != null) {
for (String sourceBiome : biomes.getKeys(false)) {
HiddenOre.getPlugin().getLogger().info("Loading config for biome " + sourceBiome);
DropLimitsConfig dlc = grabLimits(biomes.getConfigurationSection(sourceBiome), dc.limits);
dc.addBiomeLimits(sourceBiome, dlc);
}
}
return dc;
}
private static DropLimitsConfig grabLimits(ConfigurationSection drop, DropLimitsConfig parent) {
DropLimitsConfig dlc = new DropLimitsConfig();
dlc.setTools(drop.isSet("tools") ? drop.getStringList("tools") : parent.tools);
dlc.chance = drop.getDouble("chance", parent.chance);
Double amount = drop.isSet("amount") ? drop.getDouble("amount") : null;
if (amount != null) {
dlc.minAmount = amount;
dlc.maxAmount = amount;
} else {
dlc.minAmount = drop.getDouble("minAmount", parent.minAmount);
dlc.maxAmount = drop.getDouble("maxAmount", parent.maxAmount);
}
dlc.minY = drop.getInt("minY", parent.minY);
dlc.maxY = drop.getInt("maxY", parent.maxY);
// Get xp data as well.
ConfigurationSection xp = drop.getConfigurationSection("xp");
if (xp != null) {
XPConfig xpc = new XPConfig();
xpc.chance = xp.getDouble("chance", parent.xp != null ? parent.xp.chance : 0.0d);
Double xpamount = xp.isSet("amount") ? xp.getDouble("amount") : null;
if (xpamount != null) {
xpc.minAmount = xpamount;
xpc.maxAmount = xpamount;
} else {
xpc.minAmount = xp.getDouble("minAmount", parent.xp != null ? parent.xp.minAmount : 0.0d);
xpc.maxAmount = xp.getDouble("maxAmount", parent.xp != null ? parent.xp.maxAmount : 0.0d);
}
dlc.xp = xpc;
}
String state = drop.isSet("state") ? drop.getString("state", parent.state) : parent.state;
dlc.state = state;
HiddenOre.getPlugin().getLogger()
.log(Level.INFO, " loading drop config {0}% {1}-{2} {3}-{4} with {5} tools and {6} state",
new Object[] {dlc.chance*100.0, dlc.minAmount, dlc.maxAmount, dlc.minY, dlc.maxY, dlc.tools.size(), dlc.state});
HiddenOre.getPlugin().getLogger().log(Level.INFO, " tools: {0}", dlc.tools);
if (dlc.xp != null) {
HiddenOre.getPlugin().getLogger().log(Level.INFO, " xp: {0}", dlc.xp.toString());
}
return dlc;
}
public static BlockConfig isDropBlock(UUID world, BlockData block) {
List<BlockConfig> bcs = instance.blockConfigs.getOrDefault(world, instance.blockConfigs.get(null)).get(block.getMaterial().getKey());
if (bcs != null && bcs.size() > 0) {
// return first match
return bcs.get(0);
/*for (BlockConfig bc : bcs) {
return bc;
}*/ // TODO 1.13: can we layer in some new distinguishers that could lead to multi-anchoring by Material?
}
return null;
}
public static String getPrefix(UUID world, BlockData block, String drop) {
BlockConfig bc = isDropBlock(world, block);
String pref = (bc == null) ? instance.defaultPrefix : bc.getPrefix(drop);
return (pref == null ? instance.defaultPrefix : pref);
}
public static String getPrefix() {
return instance.defaultPrefix;
}
public static boolean isAlertUser() {
return instance.alertUser;
}
public static boolean isListDrops() {
return instance.listDrops;
}
public static String getPrettyName(String name) {
NameConfig nc = instance.prettyNames.get(name);
String pref = (nc == null) ? name : nc.getPrettyName();
return (pref == null) ? name : pref;
}
public static File getTrackFile() {
return trackFile;
}
public static boolean isMapActive() {
return useMapSave;
}
public static File getMapFile() {
return mapFile;
}
public static int getTransformAttemptMultiplier() {
return instance.transformAttemptMultiplier;
}
public ConfigurationSection getWorldGenerations() {
return file.getConfigurationSection("clear_ores");
}
public static PlayerStateConfig getState(String state) {
return instance.stateMasterList.get(state);
}
}

View File

@@ -0,0 +1,221 @@
package com.github.devotedmc.hiddenore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class DropConfig {
public List<DropItemConfig> drops;
public String command;
public String dropName;
public String prefix;
public DropLimitsConfig limits;
public boolean transformIfAble;
public boolean dropIfTransformFails;
public int maxDropsIfTransformFails;
private Map<String, DropLimitsConfig> biomeLimits;
private VeinConfig veinNature;
public DropConfig(String dropName, List<DropItemConfig> drops, String command, boolean transformIfAble,
boolean dropIfTransformFails, int maxDropsIfTransformFails, String prefix,
DropLimitsConfig limits, VeinConfig veinNature) {
this.dropName = dropName;
this.drops = drops;
this.command = command;
this.transformIfAble = transformIfAble;
this.dropIfTransformFails = dropIfTransformFails;
this.maxDropsIfTransformFails = maxDropsIfTransformFails;
this.prefix = prefix;
this.limits = limits;
this.biomeLimits = new HashMap<String, DropLimitsConfig>();
this.veinNature = veinNature;
}
public VeinConfig getVeinNature() {
return veinNature;
}
public void addBiomeLimits(String biome, DropLimitsConfig limits) {
biomeLimits.put(biome, limits);
}
public Set<String> getBiomes() {
return biomeLimits.keySet();
}
public DropLimitsConfig getBiomeLimits(String biome) {
return biomeLimits.get(biome);
}
public List<String> getTools(String biome) {
return biomeLimits.containsKey(biome) ? biomeLimits.get(biome).tools : limits.tools;
}
public boolean dropsWithTool(String biome, ItemStack tool) {
List<String> t = getTools(biome);
if (t == null || t.isEmpty()) {
/*DIAGNOSTICS*HiddenOre.getPlugin().getLogger().log(Level.INFO, "Drops with {0} special case: no tools set", tool);*/
return true;
} else {
boolean ret = ToolConfig.dropsWithTool(t, tool);
/*DIAGNOSTICS*HiddenOre.getPlugin().getLogger().log(Level.INFO, "Drops with {0} normal case: {1}",
new Object[] { tool, ret});*/
return ret;
}
}
public ToolConfig dropsWithToolConfig(String biome, ItemStack tool) {
List<String> t = getTools(biome);
if (t == null || t.isEmpty()) {
/*DIAGNOSTICS*HiddenOre.getPlugin().getLogger().log(Level.INFO, "Out of [] picked null for {0}", tool);*/
return null; // ToolConfig.getAnyTool(tool);
} else {
ToolConfig ret = ToolConfig.getTool(t, tool);
/*DIAGNOSTICS*HiddenOre.getPlugin().getLogger().log(Level.INFO, "Out of {0} picked {1} for {2}",
new Object[] {t, ret, tool});*/
return ret;
}
}
public int getMinY(String biome) {
return biomeLimits.containsKey(biome) ? biomeLimits.get(biome).minY : limits.minY;
}
public int getMaxY(String biome) {
return biomeLimits.containsKey(biome) ? biomeLimits.get(biome).maxY : limits.maxY;
}
public double getMinAmount(String biome) {
return biomeLimits.containsKey(biome) ? biomeLimits.get(biome).minAmount : limits.minAmount;
}
public double getMaxAmount(String biome) {
return biomeLimits.containsKey(biome) ? biomeLimits.get(biome).maxAmount : limits.maxAmount;
}
public double getChance(String biome) {
return biomeLimits.containsKey(biome) ? biomeLimits.get(biome).chance : limits.chance;
}
private XPConfig getBiomeXP(String biome) {
return biomeLimits.containsKey(biome) ? biomeLimits.get(biome).xp : limits.xp;
}
/**
* Based on the player and potentially modified per biome, indicate the drop chance rate change for this player.
*
* @param biome the biome the player is in or that you want to test against
* @param player Used to read active potion / status effects
* @return 0..? double value
*/
public double getStateChance(String biome, Player player) {
String namedState = biomeLimits.containsKey(biome) ? biomeLimits.get(biome).state : limits.state;
PlayerStateConfig sConfig = Config.getState(namedState);
return (sConfig != null) ? sConfig.statusRate(player) : 1.0;
}
public double getXPChance(String biome) {
XPConfig xp = getBiomeXP(biome);
return xp != null ? xp.chance : 0.0d;
}
public double getXPMinAmount(String biome) {
XPConfig xp = getBiomeXP(biome);
return xp != null ? xp.minAmount : 0.0d;
}
public double getXPMaxAmount(String biome) {
XPConfig xp = getBiomeXP(biome);
return xp != null ? xp.maxAmount : 0.0d;
}
/**
* Gives even chance of any amount.
*
* @param modify modification to drop based on tool
* @param biome the biome at play
* @return Items that can be rendered.
*/
public List<ItemStack> renderDrop(String biome, ToolConfig modify) {
/* multipliers */
double min = getMinAmount(biome) + (modify != null ? modify.getMinAmountModifier() : 0.0);
double max = getMaxAmount(biome) + (modify != null ? modify.getMaxAmountModifier() : 0.0);
double amount = (min == max) ? min : (double) ((max - min) * Math.random() + min);
if (Config.isDebug)
HiddenOre.getPlugin().getLogger().log(Level.INFO, "Trigger drop {0} [{1}, {2}] = {3}",
new Object[] {dropName, min, max, amount});
List<ItemStack> toDrop = new ArrayList<ItemStack>(drops.size());
for (DropItemConfig item : drops) {
if (!this.transformIfAble || !item.canTransform())
toDrop.add(item.render(amount));
}
return toDrop;
}
/**
* Gives even chance of any amount.
*
* @param modify modification to drop based on tool
* @param biome the biome at play
* @return Items that can be transformed.
*/
public List<ItemStack> renderTransform(String biome, ToolConfig modify) {
/** multipliers **/
double min = getMinAmount(biome) + (modify != null ? modify.getMinAmountModifier() : 0.0);
double max = getMaxAmount(biome) + (modify != null ? modify.getMaxAmountModifier() : 0.0);
double amount = (min == max) ? min : (double) ((max - min) * Math.random() + min);
if (Config.isDebug)
HiddenOre.getPlugin().getLogger().log(Level.INFO, "Trigger transform {0} [{1}, {2}] = {3}",
new Object[] {dropName, min, max, amount});
List<ItemStack> toTransform = new ArrayList<ItemStack>(drops.size());
for (DropItemConfig item : drops) {
if (this.transformIfAble && item.canTransform())
toTransform.add(item.render(amount));
}
return toTransform;
}
/**
* Gives chance to drop XP
*
* @param biome the biome at play
* @param modify the tool config to modify XP
* @return XP to drop
*/
public int renderXP(String biome, ToolConfig modify) {
/** multipliers **/
double min = getXPMinAmount(biome) + (modify != null ? modify.getMinAmountModifier() : 0.0);
double max = getXPMaxAmount(biome) + (modify != null ? modify.getMaxAmountModifier() : 0.0);
double amount = (min == max) ? min : (double) ((max - min) * Math.random() + min);
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger().log(Level.INFO, "Trigger xp {0} [{1}, {2}] = {3}",
new Object[] {dropName, min, max, amount});
}
return (int) Math.round(amount);
}
public boolean shouldTransformIfAble() {
return this.transformIfAble;
}
public boolean shouldDropIfTransformFails() {
return this.dropIfTransformFails;
}
public int maxDropsIfTransformFails() {
return this.maxDropsIfTransformFails;
}
}

View File

@@ -0,0 +1,41 @@
package com.github.devotedmc.hiddenore;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.inventory.ItemStack;
public class DropItemConfig {
private ItemStack template;
private boolean canTransform;
public DropItemConfig(ItemStack template) {
this.template = template;
this.canTransform = template.getType().isBlock();
}
public boolean canTransform() {
return canTransform;
}
public ItemStack render(double multiplier) {
ItemStack is = template.clone();
is.setAmount((int) Math.round((double) is.getAmount() * multiplier));
if (is.getAmount() > is.getMaxStackSize()) {
is.setAmount(is.getMaxStackSize());
}
return is;
}
public static List<DropItemConfig> transform(List<ItemStack> items) {
ArrayList<DropItemConfig> drops = new ArrayList<DropItemConfig>(items.size());
for (ItemStack item : items) {
if (item != null) {
drops.add(new DropItemConfig(item));
}
}
return drops;
}
}

View File

@@ -0,0 +1,34 @@
package com.github.devotedmc.hiddenore;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class DropLimitsConfig {
public List<String> tools;
public int minY;
public int maxY;
public double chance;
public double minAmount;
public double maxAmount;
public XPConfig xp;
public String state;
public DropLimitsConfig() {
tools = new LinkedList<String>();
minY = 1;
maxY = 255;
chance = 0.0;
minAmount = 0.0;
maxAmount = 0.0;
xp = null;
state = null;
}
public void setTools(Collection<String> tools) {
this.tools.clear();
if (tools != null) {
this.tools.addAll(tools);
}
}
}

View File

@@ -0,0 +1,94 @@
package com.github.devotedmc.hiddenore;
import com.github.devotedmc.hiddenore.commands.CommandHandler;
import com.github.devotedmc.hiddenore.listeners.BlockBreakListener;
import com.github.devotedmc.hiddenore.listeners.ExploitListener;
import com.github.devotedmc.hiddenore.listeners.WorldGenerationListener;
import com.github.devotedmc.hiddenore.tracking.BreakTracking;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
public class HiddenOre extends JavaPlugin {
private static HiddenOre plugin;
private static CommandHandler commandHandler;
private static BreakTracking tracking;
private BukkitTask trackingSave;
private BukkitTask trackingMapSave;
private static BlockBreakListener breakHandler;
private static ExploitListener exploitHandler;
private static List<WorldGenerationListener> worldGen;
@Override
public void onEnable() {
plugin = this;
saveDefaultConfig();
reloadConfig();
Config.loadConfig();
tracking = new BreakTracking();
tracking.load();
trackingSave = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
public void run() {
tracking.save();
}
}, Config.trackSave, Config.trackSave);
trackingMapSave = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
public void run() {
tracking.saveMap();
}
}, Config.mapSave, Config.mapSave);
exploitHandler = new ExploitListener(plugin);
this.getServer().getPluginManager().registerEvents(exploitHandler, this);
breakHandler = new BlockBreakListener(plugin);
this.getServer().getPluginManager().registerEvents(breakHandler, this);
commandHandler = new CommandHandler(this);
this.getCommand("hiddenore").setExecutor(commandHandler);
worldGen = new ArrayList<>();
ConfigurationSection worldGenConfig = Config.instance.getWorldGenerations();
if (worldGenConfig != null) {
for (String key : worldGenConfig.getKeys(false)) {
this.getLogger().log(Level.INFO, "Registered Ore Generation Suppression Listener for World {0}", key);
WorldGenerationListener list = new WorldGenerationListener(worldGenConfig.getConfigurationSection(key));
this.getServer().getPluginManager().registerEvents(list, this);
worldGen.add(list);
}
}
}
@Override
public void onDisable() {
trackingSave.cancel();
trackingMapSave.cancel();
tracking.save();
tracking.saveMap();
}
public static HiddenOre getPlugin() {
return plugin;
}
public BreakTracking getTracking() {
return tracking;
}
public BlockBreakListener getBreakListener() {
return breakHandler;
}
}

View File

@@ -0,0 +1,20 @@
package com.github.devotedmc.hiddenore;
/**
* TODO: 1.13 -- evaluate if there are other dimensions of consideration here.
*
* @author programmerdan
*
*/
public class NameConfig {
public String prettyName;
public NameConfig(String prettyName) {
this.prettyName = prettyName;
}
public String getPrettyName() {
return prettyName;
}
}

View File

@@ -0,0 +1,77 @@
package com.github.devotedmc.hiddenore;
import java.util.List;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
/**
* Allows altering the chance rate based on the player's state/ effects they are under
*
* format --
* <code>
* &lt;name&gt;:
* haste: [0.8, 0.5]
* fatigue: [1.0, 1.0, 0.8]
* nausea: ...
* luck: ...
* blindness: ...
* badluck: ...
* </code>
* each list of numbers is applied to each "level" so first number is for haste I, second for haste II; in this way custom effect levels
* can be accomodated. At present only effects that already "logically" impact mining as present, you can add others I suppose.
*
* See doLoad function for where these are initialized. Usable configs are set as named sections under top-level section "states".
* Then, individual DropLimits as defined at any level can apply a state using a "state" attribute decorator.
*
* @author ProgrammerDan
*
*/
public class PlayerStateConfig {
public List<Double> hasteRates;
public List<Double> fatigueRates;
public List<Double> nauseaRates;
public List<Double> luckRates;
public List<Double> blindnessRates;
public List<Double> badluckRates;
/**
* All states are applied successively
*
* @param player the player to apply adjustments to
* @return value 0+ indicating adjustment to chance
*/
public double statusRate(Player player) {
double presentRate = 1.0d;
for (PotionEffect effect : player.getActivePotionEffects()) {
int idx = effect.getAmplifier();
if (effect.getType().equals(PotionEffectType.FAST_DIGGING)) { // haste
if (hasteRates != null && idx < hasteRates.size()) {
presentRate *= hasteRates.get(idx);
}
} else if (effect.getType().equals(PotionEffectType.SLOW_DIGGING)) { // fatigue
if (fatigueRates != null && idx < fatigueRates.size()) {
presentRate *= fatigueRates.get(idx);
}
} else if (effect.getType().equals(PotionEffectType.CONFUSION)) { // nausea
if (nauseaRates != null && idx < nauseaRates.size()) {
presentRate *= nauseaRates.get(idx);
}
} else if (effect.getType().equals(PotionEffectType.LUCK)) { // luck
if (luckRates != null && idx < luckRates.size()) {
presentRate *= luckRates.get(idx);
}
} else if (effect.getType().equals(PotionEffectType.BLINDNESS)) { // blindness
if (blindnessRates != null && idx < blindnessRates.size()) {
presentRate *= blindnessRates.get(idx);
}
} else if (effect.getType().equals(PotionEffectType.UNLUCK)) { // unluck
if (badluckRates != null && idx < badluckRates.size()) {
presentRate *= badluckRates.get(idx);
}
}
}
return presentRate;
}
}

View File

@@ -0,0 +1,282 @@
package com.github.devotedmc.hiddenore;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
/**
*
wood_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
type: WOOD_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.0
minAmount: 0
maxAmount: 0
*
* special case for anything: (catchall)
* "ignore" section is ignored but "modifiers" are observed
* value after "all" is ignored, just something needs to be there.
*
name:
ignore:
all: true
modifiers:
dropChance: 1.0
minAmount: 0
maxAmount: 0
*
* @author ProgrammerDan
*
*/
public class ToolConfig {
private ItemStack template;
private boolean ignoreAmount;
private boolean ignoreDurability;
private boolean ignoreEnchants;
private boolean ignoreOtherEnchants;
private boolean ignoreEnchantsLvl;
private boolean ignoreLore;
private boolean ignoreName;
private boolean ignoreMeta;
private double minAmountModifier;
private double maxAmountModifier;
private double dropChanceModifier;
private static Map<String, ToolConfig> tools = new HashMap<>();
private static List<String> toolList = new LinkedList<>();
protected ToolConfig(ItemStack template, boolean ignoreAmount, boolean ignoreDurability,
boolean ignoreEnchants, boolean ignoreOtherEnchants, boolean ignoreEnchantsLvl,
boolean ignoreLore, boolean ignoreName,
Double dropChanceModifier, Double minAmountModifier, Double maxAmountModifier) {
this.template = template;
this.ignoreAmount = ignoreAmount;
this.ignoreDurability = ignoreDurability;
this.ignoreEnchants = ignoreEnchants;
this.ignoreOtherEnchants = ignoreOtherEnchants;
this.ignoreEnchantsLvl = ignoreEnchantsLvl;
this.ignoreLore = ignoreLore;
this.ignoreName = ignoreName;
this.ignoreMeta = ignoreEnchants && ignoreLore && ignoreName;
this.dropChanceModifier = (dropChanceModifier == null ? 1.0 : dropChanceModifier);
this.minAmountModifier = (minAmountModifier == null ? 0.0 : minAmountModifier);
this.maxAmountModifier = (maxAmountModifier == null ? 0.0 : maxAmountModifier);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(template);
sb.append(",ignore:");
if (this.ignoreAmount) sb.append("amount");
if (this.ignoreMeta) {
sb.append("meta");
} else {
if (this.ignoreEnchants) sb.append("enchants");
if (this.ignoreOtherEnchants) sb.append("otherench");
if (this.ignoreEnchantsLvl) sb.append("enchantslvl");
if (this.ignoreLore) sb.append("lore");
if (this.ignoreName) sb.append("name");
}
sb.append(",mods:").append("c:").append(this.dropChanceModifier);
sb.append("n:").append(this.minAmountModifier);
sb.append("m:").append(this.maxAmountModifier);
return sb.toString();
}
public ItemStack getTemplate() {
return template;
}
public boolean ignoreAmount() {
return ignoreAmount;
}
public boolean ignoreDurability() {
return ignoreDurability;
}
public boolean ignoreEnchants() {
return ignoreEnchants;
}
public boolean ignoreOtherEnchants() {
return ignoreOtherEnchants;
}
public boolean ignoreEnchantsLvl() {
return ignoreEnchantsLvl;
}
public boolean ignoreLore() {
return ignoreLore;
}
public boolean ignoreName() {
return ignoreName;
}
public boolean ignoreMeta() {
return ignoreMeta;
}
public double getDropChanceModifier() {
return dropChanceModifier;
}
public double getMinAmountModifier() {
return minAmountModifier;
}
public double getMaxAmountModifier() {
return maxAmountModifier;
}
public static void clear() {
tools = new HashMap<>();
toolList = new LinkedList<>();
}
public static void initTool(ConfigurationSection tool) {
if (tools == null) clear();
if (!tool.contains("template")) {
if (!tool.contains("ignore.all")) {
return;
} else {
HiddenOre.getPlugin().getLogger().info("Catchall tool found: " + tool.getName());
}
}
if (tools.containsKey(tool.getName())){
HiddenOre.getPlugin().getLogger().info("Duplicate definition for tool: " + tool.getName());
} else {
toolList.add(tool.getName());
}
ItemStack temp = (tool.contains("ignore.all") ? null : (ItemStack) tool.get("template"));
tools.put(tool.getName(),
new ToolConfig(temp,
tool.getBoolean("ignore.amount", true),
tool.getBoolean("ignore.durability", true),
tool.getBoolean("ignore.enchants", true),
tool.getBoolean("ignore.otherEnchants", true),
tool.getBoolean("ignore.enchantsLvl", true),
tool.getBoolean("ignore.lore", true),
tool.getBoolean("ignore.name", true),
tool.getDouble("modifiers.dropChance", 1.0),
tool.getDouble("modifiers.minAmount", 0.0),
tool.getDouble("modifiers.maxAmount", 0.0)
)
);
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger().log(Level.INFO, "Tool {0} defined as: {1}",
new Object[] {tool.getName(), tools.get(tool.getName())});
}
}
public static ToolConfig getConfig(String t) {
return tools.get(t);
}
public static boolean dropsWithTool(List<String> t, ItemStack tool) {
ToolConfig ret = getTool(t, tool);
/*DIAGNOSTICS* HiddenOre.getPlugin().getLogger().log(Level.INFO, "Out of {0} picked {1} for {2}",
new Object[] {t, ret, tool});*/
return ret != null;
}
public static ToolConfig getAnyTool(ItemStack tool) {
return getTool(toolList, tool);
}
public static ToolConfig getTool(List<String> t, ItemStack tool) {
ToolConfig catchall = null;
for (String test : t) {
ToolConfig comp = tools.get(test);
/*DIAGNOSTICS*HiddenOre.getPlugin().getLogger().log(Level.INFO, " -- Comparing {0} to {1}:{2}", new Object[] {
tool, test, comp == null ? null : comp.toString()
});*/
if (comp != null) {
ItemStack compare = comp.getTemplate();
if (compare == null) {
catchall = comp;
continue;
}
if (compare.getType() != tool.getType()) continue;
if (!comp.ignoreAmount() && compare.getAmount() != tool.getAmount()) continue;
// Short circuit of metachecks.
if (comp.ignoreMeta()) return comp;
// Metachecks.
ItemMeta compmeta = compare.getItemMeta();
ItemMeta toolmeta = tool.getItemMeta();
if (toolmeta == null && toolmeta == compmeta) return comp; // equal but no further compare
if (compmeta == null) continue; // toolmeta != null but compmeta == null
// both non-null.
if (!comp.ignoreDurability && (!(comp instanceof Damageable) || !(toolmeta instanceof Damageable) ||
((Damageable) comp).getDamage() != ((Damageable) toolmeta).getDamage())) {
continue;
}
if (!comp.ignoreName() && !(toolmeta.hasDisplayName() ?
toolmeta.getDisplayName().equals(compmeta.getDisplayName()) : !compmeta.hasDisplayName() ) ) continue;
if (!comp.ignoreLore() &&
!(toolmeta.hasLore() ? toolmeta.getLore().equals(compmeta.getLore()) : !compmeta.hasLore())) continue;
// Expensive enchantment checks.
if (!comp.ignoreEnchants()) {
Map<Enchantment, Integer> compench = compmeta.getEnchants();
Map<Enchantment, Integer> toolench = toolmeta.getEnchants();
// check that set of enchants is same (both null or both not null and same) else bail
if (!comp.ignoreOtherEnchants() && !((compench == null && toolench == null) ||
(compench != null && toolench != null && compench.keySet().equals(toolench.keySet()) ) ) ) continue;
// check that tool has at least the enchantments specified; ignore the rest.
if (comp.ignoreOtherEnchants() && !(compench == null ||
(toolench != null && toolench.keySet().containsAll(compench.keySet()) ) ) ) continue;
// also check _level_ of enchants
if (!comp.ignoreEnchantsLvl() && compench != null) {
boolean fail = false;
for(Enchantment ech : compench.keySet()) {
if (!compench.get(ech).equals(toolench.get(ech))) {
fail = true;
break;
}
}
if (fail) continue;
}
}
return comp;
}
}
/*DIAGNOSTICS*HiddenOre.getPlugin().getLogger().log(Level.INFO, " -- Using Catchall with {0} as {1}", new Object[] {
tool, catchall != null ? catchall.toString() : null
});*/
return catchall;
}
}

View File

@@ -0,0 +1,65 @@
package com.github.devotedmc.hiddenore;
import org.bukkit.Location;
import org.bukkit.util.noise.NoiseGenerator;
import org.bukkit.util.noise.SimplexNoiseGenerator;
/**
* Borrowed wholesale, for now, from CivClassic vein's approach branch.
* Might change over time.
*
* @author TealNerd, ProgrammerDan
*
*/
public class VeinConfig {
private double density;
private double maxSpan;
private double densityBonus;
private double areaHeight;
private double areaSpan;
private double heightLength;
private double densityLength;
private boolean forceVisibleTransform;
private NoiseGenerator heightNoiseGen;
private NoiseGenerator densityNoiseGen;
public VeinConfig(long densitySeed, long heightSeed, double density, double maxSpan, double densityBonus, double areaHeight,
double areaSpan, double heightLength, double densityLength, boolean forceVisibleTransform) {
this.density = density;
this.maxSpan = maxSpan;
this.densityBonus = densityBonus;
this.areaHeight = areaHeight;
this.areaSpan = areaSpan;
this.heightLength = heightLength;
this.densityLength = densityLength;
this.heightNoiseGen = new SimplexNoiseGenerator(heightSeed);
this.densityNoiseGen = new SimplexNoiseGenerator(densitySeed);
this.forceVisibleTransform = forceVisibleTransform;
}
public double getOreChance(Location loc) {
return getOreChance(loc.getX(), loc.getBlockY(), loc.getZ());
}
public double getOreChance(double x, int y, double z) {
double chance = Math.abs(y-getVeinHeight(x, z));
if(chance > maxSpan) return 0;
return ((Math.cos(chance * Math.PI / maxSpan) + 1) / 2) * getVeinDensity(x, z);
}
private double getVeinHeight(double x, double z) {
return heightNoiseGen.noise(x / heightLength, z / heightLength) * areaSpan + areaHeight;
}
private double getVeinDensity(double x, double z) {
return (densityNoiseGen.noise(x / densityLength, z / densityLength) + densityBonus) * density;
}
public boolean getForceVisibleTransform() {
return this.forceVisibleTransform;
}
}

View File

@@ -0,0 +1,24 @@
package com.github.devotedmc.hiddenore;
public class XPConfig {
public double chance;
public double minAmount;
public double maxAmount;
public XPConfig() {
chance = 0.0;
minAmount = 0.0;
maxAmount = 0.0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("chance: ").append(chance)
.append(" min: ").append(minAmount)
.append(" max: ").append(maxAmount);
return sb.toString();
}
}

View File

@@ -0,0 +1,106 @@
package com.github.devotedmc.hiddenore.commands;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import com.github.devotedmc.hiddenore.BlockConfig;
import com.github.devotedmc.hiddenore.Config;
import com.github.devotedmc.hiddenore.DropConfig;
import com.github.devotedmc.hiddenore.DropItemConfig;
import com.github.devotedmc.hiddenore.HiddenOre;
/**
* Management and maintenance commands
*
* @author programmerdan
*/
public class CommandHandler implements CommandExecutor {
final HiddenOre plugin;
public CommandHandler(HiddenOre instance) {
plugin = instance;
}
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (cmd.getName().equalsIgnoreCase("hiddenore")) {
if (sender.hasPermission(plugin.getCommand("hiddenore").getPermission())) {
if (args.length >= 2) {
if ("debug".equals(args[0])) {
Config.isDebug = Boolean.parseBoolean(args[1]);
sender.sendMessage("HiddenOre debug mode now " + (Config.isDebug ? "on" : "off"));
return true;
}
}
if (args.length >= 1) {
if ("save".equals(args[0])) {
plugin.getTracking().liveSave();
sender.sendMessage("HiddenOre tracking forced live save scheduled");
return true;
} else if ("generate".equals(args[0])) {
if (!(sender instanceof Player)) {
sender.sendMessage("Cannot be run as console");
}
Player player = (Player) sender;
double mult = 1;
try {
mult = Integer.parseInt(args[1]);
} catch (Exception e) {mult = 1;}
final double vmult = mult;
final UUID world = player.getWorld().getUID();
sender.sendMessage("Generating all drops, this could cause lag");
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
long delay = 0l;
Map<NamespacedKey, List<BlockConfig>> worldBlockConfigs = Config.instance.blockConfigs.getOrDefault(world, Config.instance.blockConfigs.get(null));
if (worldBlockConfigs == null) {
sender.sendMessage("No drops configured for blocks in this world.");
return;
}
for (NamespacedKey blockConf : worldBlockConfigs.keySet()) {
for (BlockConfig block : worldBlockConfigs.get(blockConf)) {
for (String dropConf : block.getDrops()) {
DropConfig drop = block.getDropConfig(dropConf);
for (DropItemConfig item : drop.drops) {
Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
@Override
public void run() {
sender.sendMessage(String.format("Block: %s, drop: %s", blockConf.toString(), dropConf));
Item dropped = player.getWorld().dropItem(player.getLocation().add(0, 1.0, 0), item.render(vmult));
dropped.setPickupDelay(20);
}
}, delay++);
}
}
}
}
}
});
return true;
}
} else {
Bukkit.getPluginManager().disablePlugin(plugin);
Bukkit.getPluginManager().enablePlugin(plugin);
sender.sendMessage("HiddenOre reloaded");
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,65 @@
package com.github.devotedmc.hiddenore.events;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
public class HiddenOreEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final Player player;
private final Location dropLocation;
private List<ItemStack> drops;
public HiddenOreEvent(final Player player, final Location dropLocation, List<ItemStack> drops) {
super(false);
this.player = player;
this.dropLocation = dropLocation;
this.drops = new ArrayList<>(drops);
}
private boolean cancel = false;
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancel = cancelled;
}
@Override
public HandlerList getHandlers() {
return HiddenOreEvent.handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public Player getPlayer() {
return player;
}
public Location getDropLocation() {
return dropLocation;
}
public List<ItemStack> getDrops() {
return drops;
}
public void setDrops(List<ItemStack> drops) {
if (drops == this.drops) return;
this.drops.clear();
this.drops.addAll(drops);
}
}

View File

@@ -0,0 +1,62 @@
package com.github.devotedmc.hiddenore.events;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class HiddenOreGenerateEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final Player player;
private final Block transformBlock;
private Material transform;
public HiddenOreGenerateEvent(final Player player, final Block transformBlock, Material transform) {
super(false);
this.player = player;
this.transformBlock = transformBlock;
this.transform = transform;
}
private boolean cancel = false;
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancel = cancelled;
}
@Override
public HandlerList getHandlers() {
return HiddenOreGenerateEvent.handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public Player getPlayer() {
return player;
}
public Block getBlock() {
return transformBlock;
}
public Material getTransform() {
return transform;
}
public void setTransform(Material transform) {
if (transform == this.transform) return;
this.transform = transform;
}
}

View File

@@ -0,0 +1,459 @@
package com.github.devotedmc.hiddenore.listeners;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.ChatColor;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
import com.github.devotedmc.hiddenore.BlockConfig;
import com.github.devotedmc.hiddenore.DropConfig;
import com.github.devotedmc.hiddenore.HiddenOre;
import com.github.devotedmc.hiddenore.Config;
import com.github.devotedmc.hiddenore.ToolConfig;
import com.github.devotedmc.hiddenore.VeinConfig;
import com.github.devotedmc.hiddenore.events.HiddenOreEvent;
import com.github.devotedmc.hiddenore.events.HiddenOreGenerateEvent;
import com.github.devotedmc.hiddenore.util.FakePlayer;
/**
* Heart of ore generation, handles breaks.
*
* @author soerxpso, programmerdan
*/
public class BlockBreakListener implements Listener {
private final HiddenOre plugin;
public BlockBreakListener(HiddenOre plugin) {
this.plugin = plugin;
}
/**
* Core method of interest, captures block breaks and checks if we care; if we do, continue
*
* @param event The break event to check
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent event) {
try {
doBlockBreak(event);
} catch (NullPointerException npe) {
plugin.getLogger().log(Level.WARNING, "Failure in Block Break handling", npe);
}
}
public static void spoofBlockBreak(Location playerLoc, Block block, ItemStack inHand) {
HiddenOre.getPlugin().getBreakListener().doBlockBreak(
new BlockBreakEvent(block, new FakePlayer(playerLoc, inHand))
);
}
/**
* The heart of the plugin; handles block breaks and what to drop from them.
* Every attempt has been made to keep this pretty lightweight but it has
* grown as the featureset has grown.
*
* @param event
*/
private void doBlockBreak(BlockBreakEvent event) {
Block b = event.getBlock();
BlockData bd = b.getBlockData();
String blockName = b.getType().name();
UUID world = b.getWorld().getUID();
BlockConfig bc = Config.isDropBlock(world, bd);
Player p = event.getPlayer();
// Check if suppression is on (preventing all drops). Fires off a HiddenOreGenerateEvent in case
// someone listening might object to our manipulation here.
if (bc != null && bc.suppressDrops) {
debug("Attempting to suppress break of tracked type {0}", blockName);
HiddenOreGenerateEvent hoges = new HiddenOreGenerateEvent(p, b, Material.AIR);
Bukkit.getPluginManager().callEvent(hoges);
if (!hoges.isCancelled()) {
b.setType(Material.AIR);
event.setCancelled(true);
}
bc = null;
}
// Check with out tracker to see if any more drops are available in this little slice of the world.
if (!plugin.getTracking().trackBreak(event.getBlock().getLocation())) {
debug("Drop skipped at {0} - layer break max met", event.getBlock().getLocation());
return;
}
// We have no block config.
if (bc == null) return;
// There is no player responsible.
if (p == null) return;
debug("Break of tracked type {0} by {1}", blockName, p.getDisplayName());
ItemStack inMainHand = p.getInventory().getItemInMainHand();
// Check SilkTouch failfast, if configured.
if (!Config.instance.ignoreSilktouch && inMainHand != null && inMainHand.hasItemMeta() &&
inMainHand.getEnchantments() != null && inMainHand.getEnchantments().containsKey(Enchantment.SILK_TOUCH)) {
plugin.getTracking().postTrackBreak(event.getBlock().getLocation(), true); // before return, let's posttrack.
return;
}
boolean hasDrop = false;
StringBuilder alertUser = new StringBuilder().append(Config.instance.defaultPrefix);
String biomeName = b.getBiome().name();
if (bc.dropMultiple) {
for (String drop : bc.getDrops()) {
DropConfig dc = bc.getDropConfig(drop);
if (!dc.dropsWithTool(biomeName, inMainHand)) {
debug("Cannot drop {0} - wrong tool", drop);
continue;
}
if (b.getLocation().getBlockY() > dc.getMaxY(biomeName)
|| b.getLocation().getBlockY() < dc.getMinY(biomeName)) {
debug("Cannot drop {0} - wrong Y", drop);
continue;
}
ToolConfig dropModifier = dc.dropsWithToolConfig(biomeName, inMainHand);
double dropChance = dc.getChance(biomeName)
* (dropModifier == null ? 1.0 : dropModifier.getDropChanceModifier())
* dc.getStateChance(biomeName, p);
VeinConfig vc = dc.getVeinNature(); // handle vein externally
if (vc != null) {
dropChance *= vc.getOreChance(b.getLocation());
}
// Random check to decide whether or not the special drop should be dropped
if (dropChance > Math.random()) {
hasDrop = doDrops(hasDrop, b, event, p, biomeName, dropModifier,
drop, dc, blockName, bc, alertUser);
if (!hasDrop) {
// Core of event cancelled!
plugin.getTracking().postTrackBreak(event.getBlock().getLocation(), true);
return;
} else {
doXP(dc, biomeName, dropModifier, b.getLocation(), p);
}
}
}
} else {
String drop = bc.getDropConfig(Math.random(), biomeName, inMainHand,
p, b.getLocation()); // handles vein internally
if (drop != null) {
DropConfig dc = bc.getDropConfig(drop);
ToolConfig tc = dc.dropsWithToolConfig(biomeName, inMainHand);
hasDrop = doDrops(hasDrop, b, event, p, biomeName, tc,
drop, dc, blockName, bc, alertUser);
if (!hasDrop) {
// Core of event cancelled!
plugin.getTracking().postTrackBreak(event.getBlock().getLocation(), true);
return;
} else {
doXP(dc, biomeName, tc, b.getLocation(), p);
}
}
}
if (Config.isAlertUser() && hasDrop) {
if (Config.isListDrops()) {
alertUser.deleteCharAt(alertUser.length() - 1);
}
String alert = alertUser.toString();
if (alert.length() > 0) {
event.getPlayer().sendMessage(ChatColor.GOLD + alert);
}
}
plugin.getTracking().postTrackBreak(event.getBlock().getLocation(), true);
}
private void doXP(DropConfig dc, String biomeName, ToolConfig dropModifier, Location loc, Player p) {
double xpChance = dc.getXPChance(biomeName)
* (dropModifier == null ? 1.0 : dropModifier.getDropChanceModifier())
* dc.getStateChance(biomeName, p);
VeinConfig vc = dc.getVeinNature();
if (vc != null) {
xpChance *= vc.getOreChance(loc);
}
if (xpChance > Math.random()) {
int toXP = dc.renderXP(biomeName, dropModifier);
if (toXP > 0) {
Entity xp = loc.getWorld().spawnEntity(loc, EntityType.EXPERIENCE_ORB);
if (xp instanceof ExperienceOrb) {
ExperienceOrb eo = (ExperienceOrb) xp;
eo.setExperience(toXP);
}
}
}
}
/**
* Reuse! Handles the actual rendering and dropping of drops.
*
* @param clearBlock Should we clear the block which was broken? Also fires a HiddenOreGenerateEvent for this block incase it shouldn't be HiddenOre'd.
* @param sourceBlock The Block that was broken
* @param event the BlockBreakEvent that started this whole chain of events
* @param player the Player going around breaking stuff.
* @param biomeName the Biome Name in which things were broken.
* @param dropTool the ToolConfig that best matches for this block config, drop config, and biome.
* @param dropName The configured name of this drop
* @param dropConfig The actual drop config being invoked
* @param blockName The configured name of the block config
* @param blockConfig The actual block config being invoked.
* @param alertBuffer a StringBuffer used to report to the user on what was found, if configured.
* @return true if everything went well, false if the generate was cancelled or other error.
*/
private Boolean doDrops(boolean clearBlock, Block sourceBlock, BlockBreakEvent event, Player player, String biomeName, ToolConfig dropTool,
String dropName, DropConfig dropConfig, String blockName, BlockConfig blockConfig, StringBuilder alertBuffer) {
// Remove block, drop special drop and cancel the event
if (!clearBlock) {
HiddenOreGenerateEvent hoge = new HiddenOreGenerateEvent(player, sourceBlock, Material.AIR);
Bukkit.getPluginManager().callEvent(hoge);
if (!hoge.isCancelled()) {
sourceBlock.setType(Material.AIR);
event.setCancelled(true);
} else {
log("For {0} at {1}, HiddenOre for {2} cancelled.", player.getDisplayName(), player.getLocation(), sourceBlock);
debug("Generate cancelled, cancelling HiddenOre drop.");
return false;
}
}
final List<ItemStack> items = dropConfig.renderDrop(biomeName, dropTool);
final Location sourceLocation = sourceBlock.getLocation();
if (!items.isEmpty()) {
doActualDrops(items, sourceLocation, player, dropName, blockName, blockConfig, alertBuffer);
}
if (dropConfig.transformIfAble) {
final List<ItemStack> transform = dropConfig.renderTransform(biomeName, dropTool);
if (!transform.isEmpty()) {
doActualGenerate(transform, sourceLocation, player, dropName, blockName, blockConfig,
alertBuffer, dropConfig);
}
}
runCommand(player, dropConfig.command);
// xp handled upstream
return true;
}
private void doActualDrops(final List<ItemStack> items, final Location sourceLocation, final Player player,
String dropName, String blockName, BlockConfig blockConfig, StringBuilder alertBuffer) {
final HiddenOreEvent hoe = new HiddenOreEvent(player, sourceLocation, items);
Bukkit.getPluginManager().callEvent(hoe);
if (!hoe.isCancelled()) {
// Schedule drop.
new BukkitRunnable() {
@Override
public void run() {
for (ItemStack item: hoe.getDrops()) {
sourceLocation.getWorld().dropItem(sourceLocation.add(0.5, 0.5, 0.5), item).setVelocity(new Vector(0, 0.05, 0));
}
}
}.runTaskLater(plugin, 1l);
// Correct stats output.
for (ItemStack item: hoe.getDrops()) {
String name = item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().name();
log("STAT: Player {0} at {1} broke {2} - dropping {3} {4}",
player.getDisplayName(), player.getLocation(), blockName,
item.getAmount(), name);
}
if (Config.isAlertUser()) {
if (blockConfig.hasCustomPrefix(dropName)) {
// Custom prefix items are immediately reported to a player
StringBuilder customAlerts = new StringBuilder(blockConfig.getPrefix(dropName));
for (ItemStack item : hoe.getDrops()) {
buildAlert(customAlerts, item, null, item.getAmount(), null);
}
player.sendMessage(ChatColor.GOLD + customAlerts.toString());
} else {
// otherwise, if list drops are enabled we aggregate and report items when done everything
if (Config.isListDrops()) {
for (ItemStack item : hoe.getDrops()) {
buildAlert(alertBuffer, item, null, item.getAmount(), ",");
}
}
}
}
} else {
log("For {0} at {1}, HiddenOre {2} cancelled.", player.getDisplayName(), player.getLocation(), dropName);
}
}
private void doActualGenerate(final List<ItemStack> items, final Location sourceLocation, final Player player,
String dropName, String blockName, BlockConfig blockConfig, StringBuilder alertBuffer, DropConfig dropConfig) {
int maxWalk = 0;
int cPlace = 0;
double cAttempt = 0;
boolean tryFacing = false; // pick a facing block of attacked block
int forceFacing = -1;
VeinConfig vc = dropConfig.getVeinNature();
Block origin = sourceLocation.getBlock();
for (ItemStack xform : items) {
Material sample = xform.getType();
Material expressed = sample;
forceFacing = (vc == null ? -1 : (vc.getForceVisibleTransform() ? 0 : -1 )); // do index traverse on visible faces
// to ensure overall fairness but density in discovery, we add walk attempts to cover forced facing reveal
// to make sure we test them all before moving on.
maxWalk += xform.getAmount() * Config.getTransformAttemptMultiplier() + (forceFacing == 0 ? visibleFaces.length : 0);
cPlace = xform.getAmount();
while (cPlace > 0 && maxWalk > 0) {
Block walk = null;
if (forceFacing > -1 && forceFacing < visibleFaces.length) {
walk = this.getVisibleFacing(origin);//origin.getRelative(this.visibleFaces[forceFacing++]);
forceFacing++;
tryFacing = true;
} else if (!tryFacing) {
// Try to ensure something of the generation is visible.
walk = this.getVisibleFacing(origin);
} else {
// Use a kind of radial bloom to try to place the discovered blocks.
// expose u0, uA (multiplier on cube root of attempts) in config
double z = Math.random() * 2.0 - 1.0;
double zsq = Math.sqrt(1-Math.pow(z, 2));
double u = 0.5 + Math.floor(Math.cbrt(cAttempt++));
double theta = Math.random() * 2.0 * Math.PI;
walk = origin.getRelative(
(int) Math.round(u * zsq * Math.cos(theta)),
(int) Math.round(u * zsq * Math.sin(theta)),
(int) Math.round(u * z));
}
if (plugin.getTracking().testGen(walk.getLocation()) && blockConfig.checkGenerateBlock(walk)) {
HiddenOreGenerateEvent hoge = new HiddenOreGenerateEvent(player, walk, sample);
Bukkit.getPluginManager().callEvent(hoge);
if (!hoge.isCancelled()) {
walk.setType(hoge.getTransform());
expressed = hoge.getTransform();
cPlace --;
tryFacing = true;
plugin.getTracking().trackGen(walk.getLocation());
}
}
maxWalk --;
}
int placed = xform.getAmount() - cPlace;
if (placed < 1 && dropConfig.dropIfTransformFails) { // total failure.
ItemStack toDrop = xform.clone();
toDrop.setAmount(Math.min(xform.getAmount(), dropConfig.maxDropsIfTransformFails));
final List<ItemStack> newDrops = new ArrayList<ItemStack>();
newDrops.add(toDrop);
doActualDrops(newDrops, sourceLocation, player, dropName, blockName, blockConfig, alertBuffer);
} else {
String name = xform.hasItemMeta() && xform.getItemMeta().hasDisplayName() ?
xform.getItemMeta().getDisplayName() : Config.getPrettyName(xform.getType().name());
log("STAT: Player {0} at {1} broke {2} - replacing with {3} {4} as {6}",
player.getDisplayName(), player.getLocation(), blockName,
placed, name, expressed);
// Anything to tell anyone about?
if (placed > 0 && Config.isAlertUser()) {
if (blockConfig.hasCustomPrefix(dropName)) {
// if this block has a custom prefix we alert immediately
StringBuilder customAlerts = new StringBuilder(blockConfig.getPrefix(dropName));
buildAlert(customAlerts, null, name, placed, " nearby");
player.sendMessage(ChatColor.GOLD + customAlerts.toString());
} else {
// otherwise, we aggregate our notices and send them after all drop / gen is done.
if (Config.isListDrops()) {
buildAlert(alertBuffer, null, name, placed, " nearby,");
}
}
}
}
}
}
private void buildAlert(StringBuilder alertBuilder, ItemStack item, String nameOverride, int amount, String postfix) {
String name = nameOverride;
if (name == null && item != null) {
name = item.hasItemMeta() && item.getItemMeta().hasDisplayName() ?
item.getItemMeta().getDisplayName() : Config.getPrettyName(item.getType().name());
}
alertBuilder.append(" ").append(amount).append(" ").append(name);
if (postfix != null) {
alertBuilder.append(postfix);
}
}
private static BlockFace[] visibleFaces = new BlockFace[] {
BlockFace.DOWN, BlockFace.UP, BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH
};
private static Random facerandom = new Random();
private Block getVisibleFacing(Block origin) {
Block face = origin.getRelative(visibleFaces[facerandom.nextInt(visibleFaces.length)]);
return face;
}
private void log(String message, Object...replace) {
plugin.getLogger().log(Level.INFO, message, replace);
}
private void debug(String message, Object...replace) {
if (Config.isDebug) {
plugin.getLogger().log(Level.INFO, message, replace);
}
}
private void runCommand(Player player, String command) {
if (player != null && command != null) {
try {
String fCommand = command.replaceAll("%player%", player.getName()).replaceAll("%uuid%", player.getUniqueId().toString());
if (Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), fCommand)) {
log("Fired off {0} for {1}.", fCommand, player.getName());
} else {
log("Failed to fire off {0} for {1}.", fCommand, player.getName());
}
} catch (Exception e) {
log("Failure during command processing: {0}", e);
}
}
}
}

View File

@@ -0,0 +1,70 @@
package com.github.devotedmc.hiddenore.listeners;
import com.github.devotedmc.hiddenore.Config;
import com.github.devotedmc.hiddenore.HiddenOre;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.event.world.WorldLoadEvent;
/**
* For world ore clearing to work, the plugin has to launch on startup.
* This means however that worlds aren't loaded yet, which breaks the tight-binding
* goal for world-specific configs.
*
* This infrastructure allows soft-binding during first load and eventual resolution of
* binding once the worlds are loaded.
*
* @author ProgrammerDan
*/
public class ConfigDeferralListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void handleWorldInitEvent(WorldInitEvent init) {
checkPreLoad(init.getWorld());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void handleWorldLoadEvent(WorldLoadEvent init) {
checkPreLoad(init.getWorld());
}
private void checkPreLoad(World world) {
try {
String found = null;
if (Config.instance.preloadBlockConfigs.size() > 0) {
for (String key : Config.instance.preloadBlockConfigs.keySet()) {
if (world.getName().equals(key)) {
HiddenOre.getPlugin().getLogger().log( Level.INFO, "Found a match during loading for {0}, bound config to world", key);
Config.instance.blockConfigs.put(world.getUID(), Config.instance.preloadBlockConfigs.get(key));
found = key;
break;
}
try {
UUID worldkey = UUID.fromString(key);
if (worldkey != null && world.getUID().equals(worldkey)) {
HiddenOre.getPlugin().getLogger().log( Level.INFO, "Found a match during loading for {0}, bound config to world", key);
Config.instance.blockConfigs.put(world.getUID(), Config.instance.preloadBlockConfigs.get(key));
found = key;
break;
}
} catch (IllegalArgumentException e) {
// no match.
}
}
}
if (found != null) {
Config.instance.preloadBlockConfigs.remove(found);
}
} catch (Exception e) {
HiddenOre.getPlugin().getLogger().log(Level.WARNING, "Tried to check for dangling preloaded configs, failed", e);
}
}
}

View File

@@ -0,0 +1,193 @@
package com.github.devotedmc.hiddenore.listeners;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import com.github.devotedmc.hiddenore.Config;
import com.github.devotedmc.hiddenore.HiddenOre;
/**
* Moving exploit tracking here for easier expansion / searching.
*
* @author ProgrammerDan
*/
public class ExploitListener implements Listener {
private final HiddenOre plugin;
public ExploitListener(HiddenOre plugin) {
this.plugin = plugin;
}
/**
* On reflection I realized you could just push smoothstone into an unedited chunk layer and increase overall drops
* for a world. So to counter-balance, on each extension we track the location of the blocks and increment their Y
* counters.
*
* @param event the piston event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPistonExtend(BlockPistonExtendEvent event) {
Block source = event.getBlock();
debug("Piston event from {0}", source.getLocation());
Block extension = event.getBlock().getRelative(event.getDirection());
for (Block b : event.getBlocks()) {
Block next = b.getRelative(event.getDirection());
if (next.equals(source) || next.equals(extension)) {
continue;
}
plugin.getTracking().trackBreak(next.getLocation());
}
plugin.getTracking().trackBreak(source.getLocation());
if (!source.equals(extension)) {
plugin.getTracking().trackBreak(extension.getLocation());
}
}
/**
* Should cover even more clever implementations involving sticky pistons to game things.
*
* @param event the piston retract event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPistonRetract(BlockPistonRetractEvent event) {
if (!event.isSticky()) return; // only care about stick business.
Block source = event.getBlock();
debug("Sticky Piston event from {0}", source.getLocation());
Block extension = event.getBlock().getRelative(event.getDirection());
for (Block b : event.getBlocks()) {
Block next = b.getRelative(event.getDirection());
if (next.equals(source) || next.equals(extension)) {
continue;
}
plugin.getTracking().trackBreak(next.getLocation());
}
plugin.getTracking().trackBreak(source.getLocation());
if (!source.equals(extension)) {
plugin.getTracking().trackBreak(extension.getLocation());
}
}
/**
* Catch block placement directly.
*
* @param event block place event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlacingThings(BlockPlaceEvent event) {
Location placed = event.getBlockPlaced().getLocation();
if (event.getPlayer() != null) {
debug("Block place event at {0} by {1}", placed, event.getPlayer().getDisplayName());
} else {
debug("Block place event at {0}", placed);
}
plugin.getTracking().trackBreak(placed);
}
/**
* Catch explosions
*
* TODO Issue#20: Support mining via TNT?
*
* @param event explode event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onExplodingThings(BlockExplodeEvent event) {
plugin.getTracking().trackBreak(event.getBlock().getLocation());
debug("Explosion event at {0}", event.getBlock().getLocation());
for (Block b : event.blockList()) {
if (b != null) {
plugin.getTracking().trackBreak(b.getLocation());
}
}
}
/**
* Prevent gaming by dropping sand/gravel/gravity blocks
*
* @param event Change Block event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onFallingThings(EntityChangeBlockEvent event) {
Material from = event.getBlock().getType();
if (!event.getBlock().isEmpty() && !event.getBlock().isLiquid()) {
if (!from.hasGravity()) return;
if (!from.isBlock()) return;
// At this point we've confirmed that the FROM used to be a block, that is now falling.
debug("Block about to fall from {0}, was a {1}", event.getBlock().getLocation(), from);
} else {
// if (event.getBlock().isEmpty() || event.getBlock().isLiquid()) {
// At this point we've confirmed that the FROM is air or liquid e.g. this is a block
// that is done falling and wants to be a block again.
if (EntityType.FALLING_BLOCK != event.getEntityType()) return;
debug("Block has fallen to {0}, was a {1}", event.getBlock().getLocation(), from);
}
// track a break at FROM and TO, so you can't game sand/gravel by dropping it into a chunk.
plugin.getTracking().trackBreak(event.getBlock().getLocation());
}
/**
* Prevent gaming using water/lava generators.
*
* Add other generators as you become aware.
*
* Credit for basic generator detector to fireblast709 (https://bukkit.org/threads/blocking-cobblestone-generators.120924/)
*
* @param event block from to event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onGeneratingThings(BlockFromToEvent event) {
Block root = event.getBlock();
Block block = event.getToBlock();
Material type = root.getType();
if (!Material.WATER.equals(type) && !Material.LAVA.equals(type)) {
return; // not cobblegen.
}
Material mirror = Material.WATER.equals(type) ? Material.LAVA : Material.WATER;
// Looks like 1.13 simplified this a lot, since flowing and stationary are now just "water", with state of flow represented in
// a "Levelled" cast from BlockData of that block. Good job, Spigot/Bukkit -- much better imho.
for (BlockFace face : faces) {
Block check = block.getRelative(face, 1);
Block check2 = root.getRelative(face, 1);
if (mirror.equals(check.getType()) || mirror.equals(check2.getType())) {
plugin.getTracking().trackBreak(block.getLocation());
plugin.getTracking().trackBreak(check.getLocation());
plugin.getTracking().trackBreak(check2.getLocation());
debug("Generating something at union of {0} and {1}/{2}", block, check, check2);
return;
}
}
}
private final BlockFace[] faces = new BlockFace[]
{
BlockFace.SELF,
BlockFace.UP,
BlockFace.DOWN,
BlockFace.NORTH,
BlockFace.EAST,
BlockFace.SOUTH,
BlockFace.WEST,
};
private void debug(String message, Object...replace) {
if (Config.isDebug) {
plugin.getLogger().log(Level.INFO, message, replace);
}
}
}

View File

@@ -0,0 +1,183 @@
package com.github.devotedmc.hiddenore.listeners;
import com.github.devotedmc.hiddenore.BlockConfig;
import com.github.devotedmc.hiddenore.Config;
import com.github.devotedmc.hiddenore.HiddenOre;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkPopulateEvent;
import org.bukkit.inventory.ItemStack;
/**
* Populator to strip out blocks selectively from a world during generation.
*
* @author ProgrammerDan
*/
public class WorldGenerationListener implements Listener {
Map<Material, Material> replacements = null;
String worldName = null;
UUID worldUUID = null;
/**
* When creating, pass in a config with three sub-elements. Now supports UUID reference of world.
* <br>
* <code>
* world: world_name (or UUID)
* replace:
* IRON_ORE: STONE
* REDSTONE_ORE: STONE
* </code>
* <br>
* This should be specified per world.
*
* @param config The world-specific config.
*
*/
public WorldGenerationListener(ConfigurationSection config) {
if (config.contains("world")) {
worldName = config.getString("world");
}
try {
if (worldName != null) {
World world = HiddenOre.getPlugin().getServer().getWorld(worldName);
if (world != null) {
worldUUID = world.getUID();
} else {
worldUUID = UUID.fromString(worldName);
}
}
} catch (IllegalArgumentException iae) {
worldUUID = null;
}
if (config.contains("replace")) {
replacements = new HashMap<>();
for (String replace : config.getConfigurationSection("replace").getKeys(false)) {
Material rMat = Material.matchMaterial(replace.toUpperCase());
Material wMat = Material.matchMaterial(config.getConfigurationSection("replace").getString(replace));
if (rMat != null && wMat != null) {
replacements.put(rMat, wMat);
}
}
}
}
/**
* Reviews the chunk line by line and replaces all instances of toReplace with replaceWith.
* This is configured world to world.
*
* Note that by contract, this is called for a single chunk but the generation can occur
* for surrounding chunks, if they are not yet populated.
*
* @param event ChunkPopulateEvent covering the chunk
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void postGenerationOreClear(ChunkPopulateEvent event) {
if (replacements == null || (worldName == null && worldUUID == null) ) {
return;
}
Chunk chunk = event.getChunk();
World world = chunk.getWorld();
if (!world.getName().equalsIgnoreCase(worldName) && !world.getUID().equals(worldUUID)) {
return;
}
clear(chunk);
int x = chunk.getX();
int z = chunk.getZ();
// check adjacent chunks, which by contract
// might have been updated.
if (world.isChunkLoaded(x - 1, z) ) {
chunk = world.getChunkAt(x - 1, z);
clear(chunk);
}
if (world.isChunkLoaded(x + 1, z) ) {
chunk = world.getChunkAt(x + 1, z);
clear(chunk);
}
if (world.isChunkLoaded(x, z - 1) ) {
chunk = world.getChunkAt(x, z - 1);
clear(chunk);
}
if (world.isChunkLoaded(x, z + 1) ) {
chunk = world.getChunkAt(x, z + 1);
clear(chunk);
}
if(Config.caveOres) {
generateCaveOres(chunk);
}
}
private void clear(Chunk chunk) {
int rep = 0;
try {
int maxY = chunk.getWorld().getMaxHeight();
// now scan the chunk for ores and remove them.
for (int y = chunk.getWorld().getMinHeight() + 1; y < maxY; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
Block block = chunk.getBlock(x, y, z);
Material mat = block.getType();
if (replacements.containsKey(mat)) {
rep++;
block.setType(replacements.get(mat), false);
}
}
}
}
if (maxY < 32) {
HiddenOre.getPlugin().getLogger().log(Level.WARNING, "Chunk height abnormally low: {0} at {1}, {2}",
new Object[]{maxY, chunk.getX(), chunk.getZ()});
}
} catch (Exception e) {
HiddenOre.getPlugin().getLogger().log(Level.SEVERE, "Failed to clear ores from chunk at {0}, {1} with error {2}",
new Object[]{chunk.getX(), chunk.getZ(), e.getMessage()});
}
if (rep > 0 && Config.isDebug) {
HiddenOre.getPlugin().getLogger().log(Level.INFO, "Replaced {0} blocks at {1}, {2}", new Object[]{rep, chunk.getX(), chunk.getZ()});
}
}
static BlockFace[] faces = new BlockFace[] {BlockFace.UP,BlockFace.DOWN,BlockFace.NORTH,BlockFace.SOUTH,BlockFace.EAST,BlockFace.WEST};
private void generateCaveOres(Chunk chunk) {
UUID world = chunk.getWorld().getUID();
int xzmax = chunk.getWorld().getMaxHeight();
ItemStack breakItem = new ItemStack(Material.DIAMOND_PICKAXE);
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
for(int y = chunk.getWorld().getMinHeight(); y < xzmax; y++) {
Block block = chunk.getBlock(x, y, z);
BlockConfig bc = Config.isDropBlock(world, block.getBlockData());
if(bc == null) continue;
for(BlockFace face : faces) {
if(block.getRelative(face).getType().isAir()) {
BlockBreakListener.spoofBlockBreak(block.getLocation(), block, breakItem);
break;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,644 @@
package com.github.devotedmc.hiddenore.tracking;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import com.github.devotedmc.hiddenore.Config;
import com.github.devotedmc.hiddenore.HiddenOre;
/**
* A critical component of HiddenOre is preventing gaming of the ore generation system.
* This is done by circumspect tracking and suppressing of "repeat placements",
* generators, piston use, etc. It's quite effective; all known avenues of attack
* result in significantly reduced drop/genrates. It does <i>not</i> pay to cheat.
*
* @author soerxpso, programmerdan
*/
public class BreakTracking {
private static final int POS_LAYERS = 320;
private static final int NEG_LAYERS = 64;
private static final int TOTAL_LAYERS = POS_LAYERS + NEG_LAYERS;
private static final int GEN = 1;
private static final int MAP = 0;
Map<UUID, Map<Long, short[]>> track;
Map<UUID, Map<Long, long[][][]>> map;
// Now for one small data structures that will let us keep track of most-recent-breaks to directly
// prevent gaming.
private static final int RECENT_MAX = 512;
private int recentPtr;
private Location[] recent;
public BreakTracking() {
track = new HashMap<>();
map = new HashMap<>();
recent = new Location[RECENT_MAX];
recentPtr = 0;
}
public void load() {
long s = System.currentTimeMillis();
HiddenOre.getPlugin().getLogger().info("Starting Break Tracking load");
File tf = Config.getTrackFile();
if (!tf.exists()) {
HiddenOre.getPlugin().getLogger().info("No save exists to load");
} else {
try {
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(tf)));
String uuid = null;
boolean active = true;
while (active) {
try {
uuid = dis.readUTF();
HiddenOre.getPlugin().getLogger().info("Loading tracking data for world " + uuid);
} catch (EOFException done) {
active = false;
break;
}
UUID uid = UUID.fromString(uuid);
Map<Long, short[]> world = new HashMap<>();
track.put(uid, world);
long ccnt = 0l;
while (dis.readBoolean()) {
Long chunk = dis.readLong();
short[] layers = new short[TOTAL_LAYERS];
for (int i = 0; i < layers.length; i++) {
layers[i] = dis.readShort();
}
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger().info("Loaded layers for chunk " + chunk);
}
ccnt++;
world.put(chunk, layers);
}
HiddenOre.getPlugin().getLogger().info("Loaded " + ccnt + " chunks");
}
dis.close();
} catch (IOException ioe) {
HiddenOre.getPlugin().getLogger().log(Level.SEVERE, "Failed to load break tracking.", ioe);
}
}
s = System.currentTimeMillis() - s;
HiddenOre.getPlugin().getLogger().info("Took " + s + "ms to load Break Tracking");
if (!Config.isMapActive()) {
HiddenOre.getPlugin().getLogger().info("Skipped Break Map init, disabled in config.");
return;
}
s = System.currentTimeMillis();
HiddenOre.getPlugin().getLogger().info("Starting Break Map load");
tf = Config.getMapFile();
if (!tf.exists()) {
HiddenOre.getPlugin().getLogger().info("No map save exists to load");
} else {
try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(tf)))){
String uuid = null;
boolean active = true;
while (active) {
try {
uuid = dis.readUTF();
HiddenOre.getPlugin().getLogger().info("Loading mapping data for world " + uuid);
} catch (EOFException done) {
active = false;
break;
}
UUID uid = UUID.fromString(uuid);
Map<Long, long[][][]> world = new HashMap<Long, long[][][]>();
map.put(uid, world);
long ccnt = 0l;
while (dis.readBoolean()) {
Long chunk = dis.readLong();
long[][][] layers = new long[2][TOTAL_LAYERS][4];
for (int i = 0; i < layers[MAP].length; i++) {
for (int j = 0; j < 4; j++) {
layers[MAP][i][j] = dis.readLong(); // "map"
}
}
for (int i = 0; i < layers[GEN].length; i++) {
for (int j = 0; j < 4; j++) {
layers[GEN][i][j] = dis.readLong(); // "gen"
}
}
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger().info("Loaded layers for chunk " + chunk);
}
ccnt++;
world.put(chunk, layers);
}
HiddenOre.getPlugin().getLogger().info("Loaded " + ccnt + " chunks");
}
dis.close();
} catch (IOException ioe) {
HiddenOre.getPlugin().getLogger().log(Level.SEVERE, "Failed to load break map.", ioe);
}
}
s = System.currentTimeMillis() - s;
HiddenOre.getPlugin().getLogger().info("Took " + s + "ms to load Break Map");
}
public void liveSave() {
Bukkit.getScheduler().runTaskLaterAsynchronously(HiddenOre.getPlugin(), new Runnable() {
public void run() {
save();
}
}, 0);
Bukkit.getScheduler().runTaskLaterAsynchronously(HiddenOre.getPlugin(), new Runnable() {
public void run() {
saveMap();
}
}, 0);
}
public void save() {
long s = System.currentTimeMillis();
HiddenOre.getPlugin().getLogger().info("Starting Break Tracking save");
File tf = Config.getTrackFile();
try {
if (tf.exists()) {
File tfb = new File(tf.getAbsoluteFile() + ".backup");
if (tfb.exists()) {
if (!tfb.delete()) {
HiddenOre.getPlugin().getLogger().info("Couldn't remove old backup file - " + tfb);
} else {
tf.renameTo(tfb);
}
} else {
tf.renameTo(tfb);
}
}
} catch (SecurityException se) {
HiddenOre.getPlugin().getLogger().log(Level.SEVERE, "Failed to manage old backup of break tracking.", se);
}
try {
if (tf.createNewFile()) {
DataOutputStream oos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(tf)));
for (Map.Entry<UUID, Map<Long, short[]>> world : track.entrySet()) {
HiddenOre.getPlugin().getLogger().info("Saving world - " + world.getKey());
oos.writeUTF(world.getKey().toString());
long ccnt = 0l;
for (Map.Entry<Long, short[]> chunk : world.getValue().entrySet()) {
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger().info(" Saving chunk " + chunk.getKey());
}
oos.writeBoolean(true);
oos.writeLong(chunk.getKey());
short[] layers = chunk.getValue();
for (short layer : layers) {
oos.writeShort(layer);
}
ccnt++;
}
HiddenOre.getPlugin().getLogger().info(" Saved " + ccnt + " chunks");
oos.writeBoolean(false);
}
oos.flush();
oos.close();
} else {
HiddenOre.getPlugin().getLogger().log(Level.WARNING, "Failed to create break tracking save file.");
}
} catch (IOException ioe) {
HiddenOre.getPlugin().getLogger().log(Level.SEVERE, "Failed to save break tracking.", ioe);
}
s = System.currentTimeMillis() - s;
HiddenOre.getPlugin().getLogger().info("Took " + s + "ms to save Break Tracking");
}
public void saveMap() {
if (!Config.isMapActive()) {
HiddenOre.getPlugin().getLogger().info("Skipped Break Map save, disabled in config.");
}
long s = System.currentTimeMillis();
HiddenOre.getPlugin().getLogger().info("Starting Break Map save");
File tf = Config.getMapFile();
try {
if (tf.exists()) {
File tfb = new File(tf.getAbsoluteFile() + ".backup");
if (tfb.exists()) {
if (!tfb.delete()) {
HiddenOre.getPlugin().getLogger().info("Couldn't remove old map backup file - " + tfb);
} else {
tf.renameTo(tfb);
}
} else {
tf.renameTo(tfb);
}
}
} catch (SecurityException se) {
HiddenOre.getPlugin().getLogger().log(Level.SEVERE, "Failed to manage old break map backup.", se);
}
try {
if (tf.createNewFile()) {
DataOutputStream oos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(tf)));
for (Map.Entry<UUID, Map<Long, long[][][]>> world : map.entrySet()) {
HiddenOre.getPlugin().getLogger().info("Saving world - " + world.getKey());
oos.writeUTF(world.getKey().toString());
long ccnt = 0l;
for (Map.Entry<Long, long[][][]> chunk : world.getValue().entrySet()) {
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger().info(" Saving chunk " + chunk.getKey());
}
oos.writeBoolean(true);
oos.writeLong(chunk.getKey());
long[][][] layers = chunk.getValue();
for (long[] layer : layers[MAP]) {
for (long quad : layer) {
oos.writeLong(quad);
}
}
for (long[] layer : layers[GEN]) {
for (long quad : layer) {
oos.writeLong(quad);
}
}
ccnt++;
}
HiddenOre.getPlugin().getLogger().info(" Saved " + ccnt + " chunks");
oos.writeBoolean(false);
}
oos.flush();
oos.close();
} else {
HiddenOre.getPlugin().getLogger().log(Level.WARNING, "Failed to create break map save file.");
}
} catch (IOException ioe) {
HiddenOre.getPlugin().getLogger().log(Level.SEVERE, "Failed to save break map.", ioe);
}
s = System.currentTimeMillis() - s;
HiddenOre.getPlugin().getLogger().info("Took " + s + "ms to save Break Map");
}
/**
* This has a specific purpose of tracking a generation of an ore. For breaks or manipulations, use trackBreak.
*
* @param loc the location to track gen
* @return true if new gen, false otherwise
*/
public boolean trackGen(Location loc) {
if (!Config.isMapActive()) return true;
long s = System.currentTimeMillis();
int Y = loc.getBlockY();
int X = (loc.getBlockX() % 16 + 16) % 16;
int Z = (loc.getBlockZ() % 16 + 16) % 16;
UUID world = loc.getWorld().getUID();
Chunk chunk = loc.getChunk();
long chunk_id = ((long) chunk.getX() << 32L) + (long) chunk.getZ();
int block_id = (( X << 4) + Z);
int quad_id = ( block_id / 64);
long mask_id = (1l << (block_id % 64));
Map<Long, long[][][]> mapChunks = map.get(world);
if (mapChunks == null) { // init map chunk
mapChunks = new HashMap<Long, long[][][]>();
map.put(world, mapChunks);
}
long[][][] mapLayers = mapChunks.get(chunk_id);
if (mapLayers == null) { // init layers
mapLayers = new long[2][256][4];
mapChunks.put(chunk_id, mapLayers);
for (int y = -NEG_LAYERS; y < POS_LAYERS; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
Block b = chunk.getBlock(x, y, z);
if (b.isEmpty() || b.isLiquid()) {
int bloc = (( x << 4) + z);
int quad = (block_id / 64);
long mask = (1l << (bloc % 64));
mapLayers[MAP][y+NEG_LAYERS][quad] |= mask; // if unset, set. Ignore complements basis.
mapLayers[GEN][y+NEG_LAYERS][quad] |= mask; // tracks for "breaks" and "gens".
}
}
}
}
}
boolean ret = false;
if ((mapLayers[GEN][Y+NEG_LAYERS][quad_id] & mask_id) == mask_id) {
ret = false; // already broken!
} else {
ret = true; // new break according to this tracking.
mapLayers[MAP][Y+NEG_LAYERS][quad_id] |= mask_id;
mapLayers[GEN][Y+NEG_LAYERS][quad_id] |= mask_id;
}
s = System.currentTimeMillis() - s;
if (s > 10l) {
HiddenOre.getPlugin().getLogger().info("Took a long time (" + s + "ms) recording generation at " + loc);
}
return ret;
}
/**
* This has a specific purpose of testing a generation of an ore.
*
* As of 1.5.3, checks the recent list for a match, just in case.
*
* @param loc the location to check
* @return true if ok to gen, otherwise false
*/
public boolean testGen(Location loc) {
int j = recentPtr;
for (int i = 0; i < RECENT_MAX; i++) {
j = (recentPtr + i) % RECENT_MAX;
if (recent[j] == null) break; // anything null means we're done here
if (loc.equals(recent[j])) {
return false;
}
}
if (!Config.isMapActive()) return true;
int Y = loc.getBlockY();
int X = (loc.getBlockX() % 16 + 16) % 16;
int Z = (loc.getBlockZ() % 16 + 16) % 16;
UUID world = loc.getWorld().getUID();
Chunk chunk = loc.getChunk();
long chunk_id = ((long) chunk.getX() << 32L) + (long) chunk.getZ();
int block_id = (( X << 4) + Z);
int quad_id = ( block_id / 64);
long mask_id = (1l << (block_id % 64));
Map<Long, long[][][]> mapChunks = map.get(world);
if (mapChunks == null) { // no tracking, so OK
return true;
}
long[][][] mapLayers = mapChunks.get(chunk_id);
if (mapLayers == null) { // no layer, so OK
return true;
}
if ((mapLayers[GEN][Y+NEG_LAYERS][quad_id] & mask_id) == mask_id) {
return false;
} else {
return true;
}
}
/**
* This is only for map breaks -- "expands" impact of break node, to prevent
* any additional breaks of exposed blocks from generating.
*
* Somewhat gracefully handles promoting breaks into adjacent chunks if needed.
* TODO eval above
*
* ATM this works by suppressing _generation_ in blocks around the one that was broken.
* It does NOT suppress the ability of one of those blocks to generate more nodes.
*
* Basically, once a block is exposed it doesn't transform.
*
* @param loc the location to check after a break
* @param exp expand to nearby layers/chunks?
*/
public void postTrackBreak(Location loc, boolean exp) {
if (!Config.isMapActive()) return;
long s = System.currentTimeMillis();
int Y = loc.getBlockY();
int X = (loc.getBlockX() % 16 + 16) % 16;
int Z = (loc.getBlockZ() % 16 + 16) % 16;
UUID world = loc.getWorld().getUID();
Chunk chunk = loc.getChunk();
long chunk_id = ((long) chunk.getX() << 32L) + (long) chunk.getZ();
int block_id = (( X << 4) + Z);
int quad_id = ( block_id / 64);
long mask_id = (1l << (block_id % 64));
Map<Long, long[][][]> genChunks = map.get(world);
if (genChunks == null) {
return; // should be init'd or this is being improperly called.
}
long[][][] mapLayers = genChunks.get(chunk_id);
if (mapLayers == null) {
return; // should be init'd or this is being improperly called.
}
mapLayers[GEN][Y+NEG_LAYERS][quad_id] |= mask_id;
if (exp) {
if (Y > -NEG_LAYERS) mapLayers[GEN][Y-1][quad_id] |= mask_id;
if (Y < POS_LAYERS) mapLayers[GEN][Y+1][quad_id] |= mask_id;
if (X > 0) {
int nblock_id = (((X-1) << 4) + Z);
int nquad_id = (nblock_id / 64);
long nmask_id = (1l << (nblock_id % 64));
mapLayers[GEN][Y+NEG_LAYERS][nquad_id] |= nmask_id;
} else postTrackBreak(loc.clone().add(-1, 0, 0), false);
if (X < 15) {
int nblock_id = (((X+1) << 4) + Z);
int nquad_id = (nblock_id / 64);
long nmask_id = (1l << (nblock_id % 64));
mapLayers[GEN][Y+NEG_LAYERS][nquad_id] |= nmask_id;
} else postTrackBreak(loc.clone().add(1, 0, 0), false);
if (Z > 0) {
int nblock_id = (((X) << 4) + (Z-1));
int nquad_id = (nblock_id / 64);
long nmask_id = (1l << (nblock_id % 64));
mapLayers[GEN][Y+NEG_LAYERS][nquad_id] |= nmask_id;
} else postTrackBreak(loc.clone().add(0, 0, -1), false);
if (Z < 15) {
int nblock_id = (((X) << 4) + (Z+1));
int nquad_id = (nblock_id / 64);
long nmask_id = (1l << (nblock_id % 64));
mapLayers[GEN][Y][nquad_id] |= nmask_id;
} else postTrackBreak(loc.clone().add(0, 0, 1), false);
}
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger()
.info("now world " + world + " chunk " + chunk_id + " gent " + mapLayers[GEN][Y+NEG_LAYERS][quad_id]);
}
s = System.currentTimeMillis() - s;
if (s > 10l) {
HiddenOre.getPlugin().getLogger().info("Took a long time (" + s + "ms) recording genmap post break at " + loc);
}
}
/**
* Tracks a first order break or manip.
* Locks that location from generating new drop opportunities or from transforming.
*
* @param loc the location to check
* @return true if OK to break, false otherwise
*/
public boolean trackBreak(Location loc) {
long initChunk = 0l;
long initLayers = 0l;
long initMapChunk = 0l;
long initMapLayers = 0l;
long scanLayer = 0l;
long recentCheck = 0l;
long s = System.currentTimeMillis();
int Y = loc.getBlockY();
int X = (loc.getBlockX() % 16 + 16) % 16;
int Z = (loc.getBlockZ() % 16 + 16) % 16;
UUID world = loc.getWorld().getUID();
Chunk chunk = loc.getChunk();
long chunk_id = ((long) chunk.getX() << 32L) + (long) chunk.getZ();
int block_id = ((X << 4) + Z);
int quad_id = (block_id / 64);
long mask_id = (1l << (block_id % 64));
Map<Long, short[]> chunks = track.get(world);
if (chunks == null) { // init chunk
initChunk = System.nanoTime();
chunks = new HashMap<Long, short[]>();
track.put(world, chunks);
initChunk = System.nanoTime() - initChunk;
}
short[] layers = chunks.get(chunk_id);
if (layers == null) { // init layers
initLayers = System.nanoTime();
layers = new short[TOTAL_LAYERS];
chunks.put(chunk_id, layers);
initLayers = System.nanoTime() - initLayers;
}
boolean ret = true;
long spc = 0l;
if (Config.isMapActive()) {
Map<Long, long[][][]> mapChunks = map.get(world);
if (mapChunks == null) { // init map chunk
initMapChunk = System.nanoTime();
mapChunks = new HashMap<Long, long[][][]>();
map.put(world, mapChunks);
initMapChunk = System.nanoTime() - initMapChunk;
}
long[][][] mapLayers = mapChunks.get(chunk_id);
if (mapLayers == null) { // init layers
initMapLayers = System.nanoTime();
mapLayers = new long[2][TOTAL_LAYERS][4];
mapChunks.put(chunk_id, mapLayers);
ChunkSnapshot chunkS = chunk.getChunkSnapshot();
for (int y = -NEG_LAYERS; y < POS_LAYERS; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
//Block b = chunk.getBlock(x, y, z);
//if (b.isEmpty() || b.isLiquid()) {
Material m = chunkS.getBlockType(x, y, z);
if (Material.AIR.equals(m) || Material.WATER.equals(m) || Material.LAVA.equals(m)) {
int bloc = ((x << 4) + z);
int quad = (block_id / 64);
long mask = (1l << (bloc % 64));
mapLayers[MAP][y+NEG_LAYERS][quad] |= mask; // if unset, set. Ignore complements basis.
mapLayers[GEN][y+NEG_LAYERS][quad] |= mask; // tracks for "breaks" and "gens".
}
}
}
}
initMapLayers = System.nanoTime() - initMapLayers;
}
if ((mapLayers[MAP][Y+NEG_LAYERS][quad_id] & mask_id) == mask_id) {
ret = false; // already broken!
} else {
ret = true; // new break according to this tracking.
mapLayers[MAP][Y+NEG_LAYERS][quad_id] |= mask_id;
mapLayers[GEN][Y+NEG_LAYERS][quad_id] |= mask_id;
}
spc = mapLayers[MAP][Y+NEG_LAYERS][quad_id];
}
if (layers[Y+NEG_LAYERS] == 0) {
scanLayer = System.nanoTime();
// quick layer scan for air and water
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
Block b = chunk.getBlock(x, Y, z);
if (b.isEmpty() || b.isLiquid()) {
layers[Y+NEG_LAYERS]++;
}
}
}
scanLayer = System.nanoTime() - scanLayer;
}
if (layers[Y+NEG_LAYERS] >= TOTAL_LAYERS) { // done
ret = false;
} else if (ret) {
layers[Y+NEG_LAYERS]++; // represent new break in layer.
ret = true;
}
if (ret) {
recentCheck = System.nanoTime();
boolean shallow = false;
int j = recentPtr;
for (int i = 0; i < RECENT_MAX; i++) {
j = (recentPtr + i) % RECENT_MAX;
if (recent[j] == null) break; // anything null means we're done here
if (loc.equals(recent[j])) {
ret = false;
shallow = true;
break;
}
}
if (!shallow) { // we add to the "end" of a circular list.
if (--recentPtr < 0) {
recentPtr += RECENT_MAX;
}
recent[recentPtr] = loc;
}
recentCheck = System.nanoTime() - recentCheck;
}
if (Config.isDebug) {
HiddenOre.getPlugin().getLogger()
.info("now world " + world + " chunk " + chunk_id + " layersum " + layers[Y+NEG_LAYERS] + " map" + quad_id + ":" + mask_id + "t " + spc);
}
s = System.currentTimeMillis() - s;
if (s > 10l) {
HiddenOre.getPlugin().getLogger().info("Took a long time (" + s + "ms) recording break at " + loc);
HiddenOre.getPlugin().getLogger().log(Level.INFO, "Breakdown: chunk{0}ns layer{1}ns mchunk{2}ns mlayer{3}ns scan{4}ns recent{5}ns",
new Object[] {initChunk, initLayers, initMapChunk, initMapLayers, scanLayer, recentCheck});
}
return ret;
}
}

View File

@@ -0,0 +1,119 @@
#This config is meant to illustrate more advanced uses of HiddenOre features, including the
# full depth of the tool config and what it means, and how it works in combination with other
# drops and configs.
#
#This is not a valid configuration file and will cause errors if used directly.
# It is broken into sections, small chunks of valid configurations that individually
# would be usable as a config.
#
#If you have any questions don't hesitate to open an issue on the official github:
# http://www.github.com/DevotedMC/HiddenOre
# SAMPLE ONE: Advanced Tool Specifications
prefix: You found a hidden ore!
alert_user: true
list_drops: true
debug: false
ignore_silktouch: true
tools:
fancy_dpick:
template:
# This specifies a named diamond pickaxe -- a colored name even -- with lore, and two enchantments
==: org.bukkit.inventory.ItemStack
v: 1
type: DIAMOND_PICKAXE
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: <EFBFBD>dResin
lore:
- Marked Item
enchants:
DURABILITY: 3
DIG_SPEED: 4
ignore:
# These ignore settings say that a match is achieved if and only if the name, lore, and enchantments
# and levels of those enchantments are the same as the template. However, we ignore
# _other_ enchantments meaning if the tool also has silk touch, that's ignored and a match
# would still be found. Damage/Durability is ignored
amount: true
durability: true
enchants: false
otherEnchants: true
enchantsLvl: false
lore: false
name: false
modifiers:
# These settings mean that whatever drop is associated with this tool, on a match, will have twice
# the chance of dropping vs. base configuration.
dropChance: 2.0
# As well, the size of the drop will be increased by at least 2 and the maximum size of the drop
# will be expanded by 4.
minAmount: 2.0
maxAmount: 4.0
blocks:
stone:
material: STONE
dropMultiple: false
drops:
washed_out_shulker:
package:
# The drop config can get very complex; here is a Named Monster Egg for Shulkers.
- ==: org.bukkit.inventory.ItemStack
v: 1
type: SHULKER_SPAWN_EGG
amount: 1
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: Pet Shulker
# Even cooler, you can specify the "block" type of water, and
# if transformIsAble is true, the very rock around you will instantly transform
# into water.
- ==: org.bukkit.inventory.ItemStack
v: 1
type: WATER
amount: 1
tools:
- fancy_dpick
minY: 1
maxY: 131
chance: 0.001
minAmount: 4
maxAmount: 6
transformIfAble: true
# Just an example of the worlds settings. Generic `blocks` section above is default for all other worlds.
# Instead of world name, you can also use the internal UUID of the world, if you know it.
worlds:
world_the_end:
blocks:
endstone:
material: END_STONE
dropMultiple: false
drops:
washed_out_shulker:
package:
# The drop config can get very complex; here is a Named Monster Egg for Shulkers.
- ==: org.bukkit.inventory.ItemStack
v: 1
type: SHULKER_SPAWN_EGG
amount: 1
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: Pet Shulker
# Even cooler, you can specify the "block" type of stationary water, and
# if transformIsAble is true, the very rock around you will instantly transform
# into water.
- ==: org.bukkit.inventory.ItemStack
v: 1
type: WATER
amount: 1
tools:
- fancy_dpick
minY: 1
maxY: 255
chance: 0.001
minAmount: 4
maxAmount: 6
transformIfAble: true

View File

@@ -0,0 +1,226 @@
prefix: You found a hidden ore!
alert_user: true
list_drops: true
debug: true
ignore_silktouch: true
# For each potential transformation in a generate event, how many attempts at most will be made?
# Keep this value small, or performance _will_ suffer.
transform_attempt_multiplier: 4
tools:
anything:
ignore:
all: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 0.0
wood_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: WOODEN_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 2.0
gold_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: GOLDEN_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.1
minAmount: 1.0
maxAmount: 3.0
iron_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: IRON_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.1
minAmount: 2.0
maxAmount: 4.0
stone_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: STONE_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.05
minAmount: 0.0
maxAmount: 0.0
diamond_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: DIAMOND_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.2
minAmount: 5.0
maxAmount: 10.0
blocks:
stone:
material: STONE
validTransforms:
cobblestone:
material: COBBLESTONE
dirt:
material: DIRT
dropMultiple: false
drops:
coal_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: COAL_ORE
amount: 1
tools:
- wood_pickaxe
- stone_pickaxe
- iron_pickaxe
- gold_pickaxe
- diamond_pickaxe
minY: 1
maxY: 131
chance: 0.1
minAmount: 2
maxAmount: 8
transformIfAble: true
transformDropIfFails: true
transformMaxDropsIfFails: 4
iron_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: IRON_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 67
chance: 0.2
minAmount: 1
maxAmount: 4
transformIfAble: true
transformDropIfFails: false
transformMaxDropsIfFails: 4
gold_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: GOLD_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.3
minAmount: 1
maxAmount: 4
transformIfAble: false
transformDropIfFails: false
transformMaxDropsIfFails: 4
diamond_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: DIAMOND_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.2
minAmount: 1
maxAmount: 3
redstone_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: REDSTONE_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.1
minAmount: 1
maxAmount: 4
transformIfAble: true
transformDropIfFails: true
transformMaxDropsIfFails: 1
lapis_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: LAPIS_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.1
minAmount: 1
maxAmount: 3
pretty_names:
DIAMOND_ORE: Diamond Ore
IRON_ORE: Iron Ore
GOLD_ORE: Gold Ore
NETHER_QUARTZ_ORE: Quartz Ore
EMERALD_ORE: Emerald Ore
REDSTONE_ORE: Redstone Ore
LAPIS_ORE: Lapis Lazuli Ore
COAL_ORE: Coal Ore

View File

@@ -0,0 +1,365 @@
# This file shows an example of how to do veins for HiddenOre backed by noise generators.
# Portions of this are deeply inspired by the work by TealNerd in his CivClassic fork of
# HiddenOre.
prefix: You found a hidden vein ore!
alert_user: true
list_drops: true
debug: false
# As of 1.4.0, a new tracking was added that keeps a per-block record of "activity" and discards revealed blocks as
# sources for transformation. This tracking requires a LOT of space and memory, compared to ordinary exploit prevention.
# It's mostly a quality of life addition. Set this to false to turn it off. Default (if unset) is to turn it on.
# Example for 1.5.2 is to set to false, as it will incur some additional runtime impact.
map_save_active: false
ignore_silktouch: true
caveOres: false
# For each potential transformation in a generate event, how many attempts at most will be made?
# Keep this value small, or performance _will_ suffer.
transform_attempt_multiplier: 3
clear_ores:
main_world:
world: world
replace:
IRON_ORE: STONE
REDSTONE_ORE: STONE
GOLD_ORE: STONE
COAL_ORE: STONE
DIAMOND_ORE: STONE
EMERALD_ORE: STONE
LAPIS_ORE: STONE
end_world:
world: world_nether
replace:
NETHER_QUARTZ_ORE: NETHERRACK
tools:
# Giving a tool ignore:all:true will make it the default catchall, if specified in a config.
# So if you have a "default" behavior you want for any non-specified tool, use that.
# Great for things that are often broken by hand or with random crap held, like dirt, sand, gravel.
# "ignore" section is ignored but "modifiers" section will be applied.
anything:
ignore:
all: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 0.0
states:
# These allow modification of drop chance based on the player's state
# Give each "set" of modifications a name
# For each specific type of player state (haste / fatigue / nausea, etc) provide an array
# The chance is picked from this array by matching Level of effect to the named effect.
# So for [0.8, 0.5] -- haste I has a 0.8 reduction, haste II has a 0.5 reduction.
generalRebalance:
haste: [0.9, 0.7]
fatigue: [1.1, 1.3]
nausea: [0.5, 0.3, 0.1]
luck: [2.0, 3.0, 4.0]
badluck: [0.1, 0.01, 0.002]
blindness: [0.0, 0.0, 0.0]
drops:
# As of 1.3.2, you can now specify a command to run instead of (or in addition to) drops.
fancy_cmd:
minY: 1
maxY: 255
chance: 0.0001
command: '/give %player% iron_ingot 1'
# You can specify tool and biome, and state modifiers like any other drop.
tools:
- wood_pickaxe
# Here we define all the potential drops for this material/subtype. Give each "drop config" a friendly name.
coal_ore:
# Package is the list of ConfigurationSerializeable ItemStack that should constitutes this drop.
# You can specify one or _many_ items, to emulate the true "drop chest" experience.
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: COAL_ORE
# Amount is _not_ ignored. Use this as the "base" multipler. This amount will be multiplied by the randomly chosen "amount" configured
# later and modified per tool and biome.
amount: 1
# A String list here of all the tool friendly-names that you defined in the root "tools" section which can trigger this drop.
tools:
- wood_pickaxe
- stone_pickaxe
- iron_pickaxe
- gold_pickaxe
- diamond_pickaxe
# lowest Y in the map that this drop can be found.
# acts as "areaHeight" if areaHeight isn't set on veinNature.
minY: 1
# highest Y in the map that this drop can be found.
maxY: 131
# Base chance for this drop. This is percent chance / 100 -- so 1 is 100%, 0.1 is 10%, and 0.01 is 1%.
chance: 0.01
# multipliers of package's amount. Specify these separately or "amount" for a fixed value. If min and max are used,
# and this drop is selected, then a random value between min and max is chosen, after min and max are adjusted
# by biome or tool.
minAmount: 2
maxAmount: 8
# Instead of dropping the package, attempts to transform nearby blocks of the same type as triggered this
# drop into the package. If portions of the drop can't be transformed, they are still just dropped.
# Default is false.
transformIfAble: true
# If for some reason transformation can't find a place to do its magic, should we use normal drop behavior?
# Default is false.
transformDropIfFails: false
# If we do use normal drop behavior, what's the maximum drop size (after being constrained by maxAmount above)
# Default is 1.
transformMaxDropsIfFails: 1
# Describes which state by name to use from the `states` section above.
state: generalRebalance
# drop configurations can also have a vein nature
veinNature:
# Controls the "intensity" of the noise function, or, the overall likelihood at peak noise
density: .5
# This controls in the tailoff distribution across the vein span region, such that
# it helps to form a cosine distribution centered around the "center" of the
# vein as bounded by areaHeight, areaSpan, and the height seed.
maxSpan: 10
# After raw density [-1,1] is computed, add this value to the computation, then multiply by density.
densityBonus: 0.1
# Offset Y for vein. This is the midpoint for vein centers.
areaHeight: 30
# Preferred radius/spread on Y for vein. Vein will be
# again subject to drop's minY maxY. Full vein center distribution vertically is
# controlled by [areaHeight - areaSpan, areaHeight + areaSpan]
#
# Full spread is [areaHeight - areaSpan - maxSpan, areaHeight + areaSpan + maxSpan]
# where maxSpan is max distance from the chosen midpoint that has a
# chance of being in the vein, subject to a cosine function decaying out
# from the center, with point of highest chance at the chosen midpoint, and
# lowest chance at maxSpan distance away from that midpoint.
#
# All of these spans are constrained by [minY, maxY]
areaSpan: 20
# This is a divider against the "height noise map". Basically, smaller values results in more severe vein height
# transitions. heigher values, more gradual.
heightLength: 0.5
# This is a divider against the "density noise map". smaller values result in smaller overall veins in terms of
# density. Conversely, large values; larger regions of density. Some trial and error is necessary to determine the
# relative "feel" of the resulting vein structures.
densityLength: 2
# A number indicating the height seed to use in the noise function.
heightSeed: 129382
# A number indicating the density seed to use
densitySeed: 384837
# Basically, if a vein block condition is met, check all immediate neighboring, and now visible blocks
# if they should be revealed, subject to the drop & block configurations.
# revealed as well
forceVisibleTransform: true
iron_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: IRON_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
# Chance for XP to drop when this gen/drop occurs
# you can add this section to "biomes" as well for unique biome-level drops
# chance and amount are modified by tool.
xp:
# standard amount config; amount for "always" value, minAmount/maxAmount for randomized size
amount: 1
# Chance of XP to generate; this is a separate chance and computed separately for XP
chance: 1.0
minY: 1
maxY: 67
chance: 0.007
minAmount: 1
maxAmount: 4
state: generalRebalance
blocks:
# This defines for which types of things drops are possible. Give each "block config" a friendly name.
stone:
# Name the 'root type' of this block config using its Bukkit material name.
material: STONE
# If any of the drops for this block have Transform mode enabled, you can specify a list of blocks that,
# in addition to the block defined above, can be transformed.
validTransforms:
# Use the same style of definition as above. Throwaway label, then 'material' and either 'allTypes' true, or define a "types" list.
cobblestone:
material: COBBLESTONE
dirt:
material: DIRT
andesite:
material: ANDESITE
diorite:
material: DIORITE
granite:
material: GRANITE
# Drop mode. Multiple drops means that every possible drop is evaluated separately and all RNG-winners are grouped together in one big drop.
# false is more of a "classic" mode, where every possible drop's chance is accumulated, then a single Random number is generated and
# evaluated, resulting in either one or no drop.
dropMultiple: false
dropList:
# As of 1.5.1, you can now specify a list of drops, configured in a master drops section, to apply.
- fancy_cmd
- coal_ore
- gold_ore
- diamond_ore
- lapis_ore
- iron_ore
- redstone_ore
- emerald_ore
# As of 1.5.1, you can skip this if you use the dropList above.
#drops:
# As of 1.5.1 you can indicate a list of materials that all share this config,
# and it will be replicated for each one.
aggregates:
# Note, use "materials" instead of "material" (1.5.1) for it to work (pick just one -- no mixnmatch here)
materials:
andesite:
# we preserve this structure in anticipation of future features for 1.13 and beyond, where we can specify block
# natures and otherwise limit or enhance our recognition of specific blocks and block types.
material: ANDESITE
diorite:
material: DIORITE
granite:
material: GRANITE
validTransforms:
cobblestone:
material: COBBLESTONE
dirt:
material: DIRT
stone:
material: STONE
diorite:
material: DIORITE
granite:
material: GRANITE
andesite:
material: ANDESITE
dropMultiple: false
# As of 1.5.1 you can mix and match new dropList and prior drops
dropList:
- fancy_cmd
drops:
coal_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: COAL_ORE
amount: 1
tools:
- wood_pickaxe
- stone_pickaxe
- iron_pickaxe
- gold_pickaxe
- diamond_pickaxe
minY: 1
maxY: 131
chance: 0.01
minAmount: 2
maxAmount: 8
transformIfAble: true
transformDropIfFails: false
transformMaxDropsIfFails: 1
state: generalRebalance
iron_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: IRON_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
xp:
amount: 1
chance: 1.0
minY: 1
maxY: 67
chance: 0.007
minAmount: 1
maxAmount: 4
state: hasteDebuff
gold_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: GOLD_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.001437
minAmount: 1
maxAmount: 4
state: generalRebalance
diamond_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: DIAMOND_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.0005427
minAmount: 1
maxAmount: 3
state: generalRebalance
redstone_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: REDSTONE_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.01025
minAmount: 1
maxAmount: 4
state: generalRebalance
lapis_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: LAPIS_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.000597
minAmount: 1
maxAmount: 3
state: fatigueBuff
emerald_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: EMERALD_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 32
chance: 0.0
minAmount: 1
maxAmount: 2
state: generalRebalance
biomes:
EXTREME_HILLS:
chance: 0.001437
state: fatigueBuff
MUTATED_EXTREME_HILLS:
chance: 0.001437
state: hasteDebuff
EXTREME_HILLS_WITH_TREES:
chance: 0.001437
MUTATED_EXTREME_HILLS_WITH_TREES:
chance: 0.001437

View File

@@ -0,0 +1,607 @@
prefix: You found a hidden ore!
alert_user: true
list_drops: true
debug: false
# As of 1.4.0, a new tracking was added that keeps a per-block record of "activity" and discards revealed blocks as
# sources for transformation. This tracking requires a LOT of space and memory, compared to ordinary exploit prevention.
# It's mostly a quality of life addition. Set this to false to turn it off. Default (if unset) is to turn it on.
# Example for 1.5.2 is to set to false, as it will incur some additional runtime impact.
map_save_active: false
ignore_silktouch: true
caveOres: false
# For each potential transformation in a generate event, how many attempts at most will be made?
# Keep this value small, or performance _will_ suffer.
transform_attempt_multiplier: 3
clear_ores:
main_world:
world: world
replace:
IRON_ORE: STONE
REDSTONE_ORE: STONE
GOLD_ORE: STONE
COAL_ORE: STONE
DIAMOND_ORE: STONE
EMERALD_ORE: STONE
LAPIS_ORE: STONE
end_world:
world: world_nether
replace:
NETHER_QUARTZ_ORE: NETHERRACK
tools:
# Giving a tool ignore:all:true will make it the default catchall, if specified in a config.
# So if you have a "default" behavior you want for any non-specified tool, use that.
# Great for things that are often broken by hand or with random crap held, like dirt, sand, gravel.
# "ignore" section is ignored but "modifiers" section will be applied.
anything:
ignore:
all: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 0.0
# Give the item a "friendly name" that you'll use in the "tools" section of each drop config.
wood_pickaxe:
# Open an issue if the config-advanced.yml data doesn't give you enough to go on to
# design the template of your desire.
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: WOODEN_PICKAXE
amount: 1
ignore:
# Ignore the amount held.
amount: true
# Ignore the durability of the item held.
durability: true
# Ignore all enchantments.
enchants: true
# Ignore any enchantments not specified in the template
otherEnchants: true
# Ignore the specific lvl of the enchantments considered
enchantsLvl: true
# Ignore any differences in lore
lore: true
# Ignore any differences in display name.
name: true
modifiers:
# Multiplier to drop chance; applied _after_ biome computation
dropChance: 1.0
# Adder on minimum drop size; so, drop config min + this min.
minAmount: 0.0
# Adder on maximum drop size; so, drop config max + this max. Both can be negative.
maxAmount: 0.0
gold_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: GOLDEN_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 0.0
iron_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: IRON_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 0.0
stone_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: STONE_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 0.0
diamond_pickaxe:
template:
==: org.bukkit.inventory.ItemStack
v: 1
type: DIAMOND_PICKAXE
amount: 1
ignore:
amount: true
durability: true
enchants: true
otherEnchants: true
enchantsLvl: true
lore: true
name: true
modifiers:
dropChance: 1.0
minAmount: 0.0
maxAmount: 0.0
states:
# These allow modification of drop chance based on the player's state
hasteDebuff:
# Give each "set" of modifications a name
haste: [0.8, 0.5]
# For each specific type of player state (haste / fatigue / nausea, etc) provide an array
# The chance is picked from this array by matching Level of effect to the named effect.
# So for [0.8, 0.5] -- haste I has a 0.8 reduction, haste II has a 0.5 reduction.
fatigueBuff:
fatigue: [1.2, 1.5]
generalRebalance:
haste: [0.9, 0.7]
fatigue: [1.1, 1.3]
nausea: [0.5, 0.3, 0.1]
luck: [2.0, 3.0, 4.0]
badluck: [0.1, 0.01, 0.002]
blindness: [0.0, 0.0, 0.0]
drops:
# As of 1.3.2, you can now specify a command to run instead of (or in addition to) drops.
fancy_cmd:
minY: 1
maxY: 255
chance: 0.0001
command: '/give %player% iron_ingot 1'
# You can specify tool and biome, and state modifiers like any other drop.
tools:
- wood_pickaxe
# Here we define all the potential drops for this material/subtype. Give each "drop config" a friendly name.
coal_ore:
# Package is the list of ConfigurationSerializeable ItemStack that should constitutes this drop.
# You can specify one or _many_ items, to emulate the true "drop chest" experience.
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: COAL_ORE
# Amount is _not_ ignored. Use this as the "base" multipler. This amount will be multiplied by the randomly chosen "amount" configured
# later and modified per tool and biome.
amount: 1
# A String list here of all the tool friendly-names that you defined in the root "tools" section which can trigger this drop.
tools:
- wood_pickaxe
- stone_pickaxe
- iron_pickaxe
- gold_pickaxe
- diamond_pickaxe
# lowest Y in the map that this drop can be found.
minY: 1
# highest Y in the map that this drop can be found.
maxY: 131
# Base chance for this drop. This is percent chance / 100 -- so 1 is 100%, 0.1 is 10%, and 0.01 is 1%.
chance: 0.01
# multipliers of package's amount. Specify these separately or "amount" for a fixed value. If min and max are used,
# and this drop is selected, then a random value between min and max is chosen, after min and max are adjusted
# by biome or tool.
minAmount: 2
maxAmount: 8
# Instead of dropping the package, attempts to transform nearby blocks of the same type as triggered this
# drop into the package. If portions of the drop can't be transformed, they are still just dropped.
# Default is false.
transformIfAble: true
# If for some reason transformation can't find a place to do its magic, should we use normal drop behavior?
# Default is false.
transformDropIfFails: false
# If we do use normal drop behavior, what's the maximum drop size (after being constrained by maxAmount above)
# Default is 1.
transformMaxDropsIfFails: 1
# Describes which state by name to use from the `states` section above.
state: generalRebalance
iron_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: IRON_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
# Chance for XP to drop when this gen/drop occurs
# you can add this section to "biomes" as well for unique biome-level drops
# chance and amount are modified by tool.
xp:
# standard amount config; amount for "always" value, minAmount/maxAmount for randomized size
amount: 1
# Chance of XP to generate; this is a separate chance and computed separately for XP
chance: 1.0
minY: 1
maxY: 67
chance: 0.007
minAmount: 1
maxAmount: 4
state: hasteDebuff
gold_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: GOLD_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.001437
minAmount: 1
maxAmount: 4
state: generalRebalance
diamond_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: DIAMOND_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.0005427
minAmount: 1
maxAmount: 3
state: generalRebalance
redstone_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: REDSTONE_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.01025
minAmount: 1
maxAmount: 4
state: generalRebalance
lapis_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: LAPIS_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.000597
minAmount: 1
maxAmount: 3
state: fatigueBuff
emerald_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: EMERALD_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 32
chance: 0.0
minAmount: 1
maxAmount: 2
state: generalRebalance
# Biomes can modify the chance rate. Anything specified in biome _replaces_ the base chance or min/max amounts,
# unlike tools which modify those values after biome is resolved.
# States can be defined per biome as well, and similarly replaces base state assignment when within that biome.
biomes:
EXTREME_HILLS:
chance: 0.001437
state: fatigueBuff
MUTATED_EXTREME_HILLS:
chance: 0.001437
state: hasteDebuff
EXTREME_HILLS_WITH_TREES:
chance: 0.001437
MUTATED_EXTREME_HILLS_WITH_TREES:
chance: 0.001437
blocks:
# This defines for which types of things drops are possible. Give each "block config" a friendly name.
stone:
# Name the 'root type' of this block config using its Bukkit material name.
material: STONE
# If any of the drops for this block have Transform mode enabled, you can specify a list of blocks that,
# in addition to the block defined above, can be transformed.
validTransforms:
# Use the same style of definition as above. Throwaway label, then 'material' and either 'allTypes' true, or define a "types" list.
cobblestone:
material: COBBLESTONE
dirt:
material: DIRT
andesite:
material: ANDESITE
diorite:
material: DIORITE
granite:
material: GRANITE
# Drop mode. Multiple drops means that every possible drop is evaluated separately and all RNG-winners are grouped together in one big drop.
# false is more of a "classic" mode, where every possible drop's chance is accumulated, then a single Random number is generated and
# evaluated, resulting in either one or no drop.
dropMultiple: false
dropList:
# As of 1.5.1, you can now specify a list of drops, configured in a master drops section, to apply.
- fancy_cmd
- coal_ore
- gold_ore
- diamond_ore
- lapis_ore
- iron_ore
- redstone_ore
- emerald_ore
# As of 1.5.1, you can skip this if you use the dropList above.
#drops:
# As of 1.5.1 you can indicate a list of materials that all share this config,
# and it will be replicated for each one.
aggregates:
# Note, use "materials" instead of "material" (1.5.1) for it to work (pick just one -- no mixnmatch here)
materials:
andesite:
material: ANDESITE
diorite:
material: DIORITE
granite:
material: GRANITE
validTransforms:
cobblestone:
material: COBBLESTONE
dirt:
material: DIRT
stone:
material: STONE
diorite:
material: DIORITE
granite:
material: GRANITE
andesite:
material: ANDESITE
dropMultiple: false
# As of 1.5.1 you can mix and match new dropList and prior drops
dropList:
- fancy_cmd
drops:
coal_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: COAL_ORE
amount: 1
tools:
- wood_pickaxe
- stone_pickaxe
- iron_pickaxe
- gold_pickaxe
- diamond_pickaxe
minY: 1
maxY: 131
chance: 0.01
minAmount: 2
maxAmount: 8
transformIfAble: true
transformDropIfFails: false
transformMaxDropsIfFails: 1
state: generalRebalance
iron_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: IRON_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
xp:
amount: 1
chance: 1.0
minY: 1
maxY: 67
chance: 0.007
minAmount: 1
maxAmount: 4
state: hasteDebuff
gold_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: GOLD_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.001437
minAmount: 1
maxAmount: 4
state: generalRebalance
diamond_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: DIAMOND_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.0005427
minAmount: 1
maxAmount: 3
state: generalRebalance
redstone_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: REDSTONE_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 15
chance: 0.01025
minAmount: 1
maxAmount: 4
state: generalRebalance
lapis_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: LAPIS_ORE
amount: 1
tools:
- stone_pickaxe
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 33
chance: 0.000597
minAmount: 1
maxAmount: 3
state: fatigueBuff
emerald_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: EMERALD_ORE
amount: 1
tools:
- iron_pickaxe
- diamond_pickaxe
minY: 1
maxY: 32
chance: 0.0
minAmount: 1
maxAmount: 2
state: generalRebalance
biomes:
EXTREME_HILLS:
chance: 0.001437
state: fatigueBuff
MUTATED_EXTREME_HILLS:
chance: 0.001437
state: hasteDebuff
EXTREME_HILLS_WITH_TREES:
chance: 0.001437
MUTATED_EXTREME_HILLS_WITH_TREES:
chance: 0.001437
# Example of new worlds config, here we explicitly specify only a single block drop for this world.
worlds:
world_nether:
blocks:
netherrack:
material: NETHERRACK
dropMultiple: false
drops:
quartz_ore:
package:
- ==: org.bukkit.inventory.ItemStack
v: 1
type: NETHER_QUARTZ_ORE
amount: 1
tools:
- wood_pickaxe
- stone_pickaxe
- iron_pickaxe
- gold_pickaxe
- diamond_pickaxe
minY: 1
maxY: 125
chance: 0.0
minAmount: 1
maxAmount: 2
biomes:
HELL:
# You can also modify the minimum Y values for a specific biome giving you complete control over where things can be found.
minY: 1
maxY: 125
chance: 0.007
# Example w/ custom prefixes.
# sand:
# material: SAND
# dropMultiple: true
# drops:
# ink_sack:
# package:
# - ==: org.bukkit.inventory.ItemStack
# type: INK_SAC
# durability: 4
# amount: 1
# prefix: Lucky find! You've found a dropchest inner.
# chance: 0.5
# minAmount: 32
# maxAmount: 64
# biome:
# PLAIN:
# minY: 1
# maxY: 128
# quartz:
# package:
# - ==: org.bukkit.inventory.ItemStack
# type: QUARTZ
# amount: 1
# prefix: Lucky find! You've found a dropchest.
# chance: 0.25
# amount: 5
# Example w/ drop suppression.
# glass:
# material: GLASS
# suppressDrops: true
# This will prevent glass from dropping, ever.
#
# Pretty names section; many tools use Vault or other dependencies. If there's a strong enough request I can add it later, but
# for now you can specify replacement names for your drops here. This values are only used in messaging to users about
# their drops and are not applied to the items dropped.
pretty_names:
DIAMOND: Diamond
IRON_INGOT: Iron ingot
GOLD_INGOT: Gold ingot
QUARTZ: Quartz
EMERALD: Emerald
PRISMARINE_SHARD: Prismarine Shard
REDSTONE: Redstone
DIAMOND_ORE: Diamond Ore
IRON_ORE: Iron Ore
GOLD_ORE: Gold Ore
QUARTZ_ORE: Quartz Ore
EMERALD_ORE: Emerald Ore
PRISMARINE: Prismarine
REDSTONE_ORE: Redstone Ore
LAPIS_ORE: Lapis Lazuli Ore
COAL_ORE: Coal Ore
COAL: Coal
NETHERRACK: Netherrack
# if the base type has subtypes, just specify those subtypes as a list. If you specify one, you'll need to specify all used subtypes.
LAPIS_LAZULI: Lapis Lazuli

View File

@@ -0,0 +1,26 @@
name: HiddenOre
version: ${version}
main: com.github.devotedmc.hiddenore.HiddenOre
author: Soerxpso
authors: [ProgrammerDan]
load: STARTUP
api-version: 1.18
website: https://www.github.com/DevotedMC/HiddenOre
description: HiddenOre allows the complete disguising of ores from players by not generating them in the first place, instead using a probability model to generate configurable ore layouts in response to player mining.
commands:
hiddenore:
description: HiddenOre allows the hiding of cool drops underground. This reloads the config.
usage: |
/hiddenore -- reloads
/hiddenore save -- forces save of tracking
/hiddenore generate [num] -- drops all drops near the player issuing the command, use sparingly.
permission: hiddenore.adv
permissions:
hiddenore.*:
description: Gives access to all HiddenOre commands
default: op
children:
hiddenore.adv: true
hiddenore.adv:
description: Allows access to HiddenOre commands.
default: op

View File

@@ -23,3 +23,4 @@ include(":plugins:realisticbiomes-paper")
include(":plugins:simpleadminhacks-paper")
include(":plugins:factorymod-paper")
include(":plugins:finale-paper")
include(":plugins:hiddenore-paper")