mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2024-11-24 14:16:45 +01:00
Merge branch 'master' of github.com:mcMMO-Dev/mcMMO into tridentsxbows
This commit is contained in:
commit
4e8262d818
@ -99,15 +99,50 @@ Version 2.2.000
|
||||
Parties got unnecessarily complex in my absence, I have removed many party features in order to simplify parties and bring them closer to my vision. I have also added new features which should improve parties where it matters.
|
||||
About the removed party features, all the features I removed I consider poor quality features and I don't think they belong in mcMMO. Feel free to yell at me in discord if you disagree.
|
||||
I don't know what genius decided to make parties public by default, when I found out that parties had been changed to such a system I could barely contain my disgust. Parties are back to being private, you get invited by a party leader or party officer. That is the only way to join a party.
|
||||
Version 2.1.165
|
||||
The mcMMO system which tracks player placed blocks has had some major rewrites (thanks t00thpick1)
|
||||
mcMMO will now be compatible with changes to world height (1.17 compatibility)
|
||||
Added missing cooldown locale message 'Commands.Database.Cooldown'
|
||||
Version 2.1.168
|
||||
Fixed an IndexOutOfBoundsException error when trying to access UserBlockTracker from an invalid range (thanks t00thpick1)
|
||||
(API) UserBlockTracker is now the interface by which our block-tracker will be known (thanks t00thpick1)
|
||||
|
||||
Version 2.1.167
|
||||
Fixed a serious dupe bug
|
||||
Add McMMOPlayerPreXpGainEvent event for plugins to modify given exp (thanks electronicboy)
|
||||
Add SkillActivationPerkEvent (thanks electronicboy)
|
||||
|
||||
NOTE:
|
||||
This bug was introduced in 2.1.165, update immediately if you are on 2.1.165 or 2.1.166
|
||||
|
||||
Version 2.1.166
|
||||
Fixed a small memory leak in the new COTW tracker
|
||||
Potentially fixed a ConcurrentModificationException involving the TransientEntityTracker (report this error if you encounter it)
|
||||
Music discs removed from the default fishing_treasures.yml
|
||||
Optimized how mcMMO saves player data (should improve timings on servers with bad disk speeds and or bad connectivity to their SQL server instance)
|
||||
|
||||
NOTES:
|
||||
No one likes fishing up music discs, if you want this change it is recommended you delete fishing_treasures.yml and let it regenerate
|
||||
(You won't have this file if you haven't updated in a while, if so you don't need to do anything)
|
||||
If any of you encounter a ConcurrentModificationException error that mentions TransientEntityTracker in its stack trace after this update let me know, I have another fix in mind for this if this update didn't fix it.
|
||||
|
||||
Version 2.1.165
|
||||
Fixed a bug where Enchanted Books dropped by mcMMO (in Fishing) did not function correctly
|
||||
The mcMMO system which tracks player placed blocks has had some major rewrites (thanks t00thpick1)
|
||||
Optimized our ChunkUnloadEvent, this should improve timings in this area
|
||||
How mcMMO tracks COTW entities has been rewritten
|
||||
When COTW summons are killed players are now informed (from anything other than the time expiring).
|
||||
mcMMO will now be compatible with changes to world height (1.17 compatibility)
|
||||
mcMMO will ignore EntityPickupItemEvents from "Fake-Player" NPCs if it recognizes them as such, this will prevent some compatibility issues with some plugins
|
||||
SuperBreaker will now always activate if the target blocks fastest tool is a Pickaxe (used to require the block giving XP or being considered an ore)
|
||||
Master Angler mentions that it works better with a boat in its hover tip
|
||||
Added missing cooldown locale message 'Commands.Database.Cooldown'
|
||||
Added new locale message 'Taming.Summon.COTW.Removed'
|
||||
Updated locale entry 'Fishing.SubSkill.MasterAngler.Description'
|
||||
|
||||
NOTES:
|
||||
Books dropped before this fix will not be usable and should just be chucked in lava, the broken books have blue names, the working books have yellow names.
|
||||
t00thpick1 has taken time to rewrite our block meta tracking system to be more efficient, easier to maintain, and support upcoming features such as world height changes
|
||||
This new system is compatible with the old one, it will convert old files to the new format as needed.
|
||||
This update shouldn't break anything as the API is the same
|
||||
This new system is compatible with the old one, it will convert old files to the new format as needed. You won't even know it is doing anything.
|
||||
This update shouldn't break anything as the API is the same.
|
||||
|
||||
Alright back to work on T&C unless some major bugs come out of this...
|
||||
|
||||
Version 2.1.164
|
||||
mcMMO will now let players use vanilla blocks that have interactions (such as the vanilla Anvil) which are assigned as either Repair or Salvage blocks if a player is sneaking (see notes)
|
||||
|
4
pom.xml
4
pom.xml
@ -124,6 +124,10 @@
|
||||
</artifactSet>
|
||||
<!-- <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>-->
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>net.kyori.examination</pattern>
|
||||
<shadedPattern>com.gmail.nossr50.kyori.examination</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.kyori.adventure</pattern>
|
||||
<shadedPattern>com.gmail.nossr50.mcmmo.kyori.adventure</shadedPattern>
|
||||
|
@ -0,0 +1,50 @@
|
||||
package com.gmail.nossr50.events.experience;
|
||||
|
||||
import com.gmail.nossr50.datatypes.experience.XPGainReason;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Called when a player gains XP in a skill
|
||||
*/
|
||||
public class McMMOPlayerPreXpGainEvent extends McMMOPlayerExperienceEvent {
|
||||
private float xpGained;
|
||||
|
||||
@Deprecated
|
||||
public McMMOPlayerPreXpGainEvent(Player player, PrimarySkillType skill, float xpGained) {
|
||||
super(player, skill, XPGainReason.UNKNOWN);
|
||||
this.xpGained = xpGained;
|
||||
}
|
||||
|
||||
public McMMOPlayerPreXpGainEvent(Player player, PrimarySkillType skill, float xpGained, XPGainReason xpGainReason) {
|
||||
super(player, skill, xpGainReason);
|
||||
this.xpGained = xpGained;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int amount of experience gained in this event
|
||||
*/
|
||||
public int getXpGained() {
|
||||
return (int) xpGained;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param xpGained int amount of experience gained in this event
|
||||
*/
|
||||
public void setXpGained(int xpGained) {
|
||||
this.xpGained = xpGained;
|
||||
}
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.gmail.nossr50.events.skills;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SkillActivationPerkEvent extends Event {
|
||||
|
||||
|
||||
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final Player player;
|
||||
private int ticks;
|
||||
private final int maxTicks;
|
||||
|
||||
public SkillActivationPerkEvent(Player player, int ticks, int maxTicks) {
|
||||
|
||||
this.player = player;
|
||||
this.ticks = ticks;
|
||||
this.maxTicks = maxTicks;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public int getTicks() {
|
||||
return ticks;
|
||||
}
|
||||
|
||||
public void setTicks(int ticks) {
|
||||
this.ticks = ticks;
|
||||
}
|
||||
|
||||
public int getMaxTicks() {
|
||||
return maxTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
@ -252,12 +252,18 @@ public class BlockListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockGrow(BlockGrowEvent event)
|
||||
{
|
||||
Block block = event.getBlock();
|
||||
World world = block.getWorld();
|
||||
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld()))
|
||||
if(WorldBlacklist.isWorldBlacklisted(world))
|
||||
return;
|
||||
|
||||
BlockState blockState = event.getBlock().getState();
|
||||
mcMMO.getPlaceStore().setFalse(blockState);
|
||||
// Minecraft is dumb, the events still throw when a plant "grows" higher than the max block height. Even though no new block is created
|
||||
if (block.getY() >= world.getMaxHeight())
|
||||
return;
|
||||
|
||||
mcMMO.getPlaceStore().setFalse(block);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,30 +1,20 @@
|
||||
package com.gmail.nossr50.listeners;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.compat.layers.persistentdata.MobMetaFlagType;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ChunkListener implements Listener {
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onChunkUnload(ChunkUnloadEvent event) {
|
||||
for(Entity entity : event.getChunk().getEntities()) {
|
||||
if(entity instanceof LivingEntity) {
|
||||
LivingEntity livingEntity = (LivingEntity) entity;
|
||||
if(mcMMO.getCompatibilityManager().getPersistentDataLayer().hasMobFlag(MobMetaFlagType.COTW_SUMMONED_MOB, livingEntity)) {
|
||||
|
||||
//Remove from existence
|
||||
if(livingEntity.isValid()) {
|
||||
mcMMO.getCompatibilityManager().getPersistentDataLayer().removeMobFlags(livingEntity);
|
||||
livingEntity.setHealth(0);
|
||||
livingEntity.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
List<LivingEntity> matchingEntities = mcMMO.getTransientEntityTracker().getAllTransientEntitiesInChunk(event.getChunk());
|
||||
for(LivingEntity livingEntity : matchingEntities) {
|
||||
mcMMO.getTransientEntityTracker().removeSummon(livingEntity, null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -689,12 +689,17 @@ public class EntityListener implements Listener {
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onEntityDeath(EntityDeathEvent event) {
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
|
||||
return;
|
||||
|
||||
LivingEntity entity = event.getEntity();
|
||||
|
||||
if(mcMMO.getTransientEntityTracker().isTransientSummon(entity)) {
|
||||
mcMMO.getTransientEntityTracker().removeSummon(entity, null, false);
|
||||
}
|
||||
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ExperienceConfig.getInstance().isNPCInteractionPrevented() && Misc.isNPCEntityExcludingVillagers(entity)) {
|
||||
return;
|
||||
}
|
||||
|
@ -448,6 +448,10 @@ public class PlayerListener implements Listener {
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
|
||||
return;
|
||||
|
||||
if(Misc.isNPCEntityExcludingVillagers(event.getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(event.getEntity() instanceof Player)
|
||||
{
|
||||
Player player = (Player) event.getEntity();
|
||||
@ -463,14 +467,13 @@ public class PlayerListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().queryPlayer(player);
|
||||
|
||||
//Profile not loaded
|
||||
if(mcMMO.getUserManager().queryPlayer(player) == null)
|
||||
{
|
||||
if(mmoPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().queryPlayer(player);
|
||||
|
||||
Item drop = event.getItem();
|
||||
ItemStack dropStack = drop.getItemStack();
|
||||
|
||||
@ -525,18 +528,32 @@ public class PlayerListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
|
||||
//Profile not loaded
|
||||
OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().queryPlayer(player);
|
||||
McMMOPlayer mmoPlayer = mcMMO.getUserManager().queryPlayer(player);
|
||||
|
||||
if(mmoPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: There's an issue with using Async saves on player quit
|
||||
//TODO: Basically there are conditions in which an async task does not execute fast enough to save the data if the server shutdown shortly after this task was scheduled
|
||||
mcMMO.getUserManager().saveUserWithDelay(mmoPlayer.getMMOPlayerData(), false, 20);
|
||||
|
||||
mcMMO.getUserManager().cleanupPlayer(mmoPlayer); //Handles cleaning up the player when their profile is no longer needed
|
||||
mcMMO.getUserManager().saveUserImmediately(mmoPlayer, mcMMO.isServerShutdownExecuted());
|
||||
//Use a sync save if the server is shutting down to avoid race conditions
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
//TODO: Make sure this cleans up
|
||||
mmoPlayer.logout(mcMMO.isServerShutdownExecuted());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,6 +91,8 @@ public class mcMMO extends JavaPlugin {
|
||||
private static ChatManager chatManager;
|
||||
private static CommandManager commandManager; //ACF
|
||||
private static SkillRegister skillRegister;
|
||||
private static TransientEntityTracker transientEntityTracker;
|
||||
private static boolean serverShutdownExecuted = false;
|
||||
|
||||
/* Adventure */
|
||||
private static BukkitAudiences audiences;
|
||||
@ -299,6 +301,9 @@ public class mcMMO extends JavaPlugin {
|
||||
chatManager = new ChatManager(this);
|
||||
|
||||
commandManager = new CommandManager(this);
|
||||
|
||||
transientEntityTracker = new TransientEntityTracker();
|
||||
setServerShutdown(false); //Reset flag, used to make decisions about async saves
|
||||
}
|
||||
|
||||
public static PlayerLevelUtils getPlayerLevelUtils() {
|
||||
@ -334,6 +339,10 @@ public class mcMMO extends JavaPlugin {
|
||||
*/
|
||||
@Override
|
||||
public void onDisable() {
|
||||
setServerShutdown(true);
|
||||
//TODO: Write code to catch unfinished async save tasks, for now we just hope they finish in time, which they should in most cases
|
||||
mcMMO.p.getLogger().info("Server shutdown has been executed, saving and cleaning up data...");
|
||||
|
||||
try {
|
||||
userManager.saveAllSync(); // Make sure to save player information if the server shuts down
|
||||
userManager.clearAll();
|
||||
@ -346,17 +355,11 @@ public class mcMMO extends JavaPlugin {
|
||||
|
||||
formulaManager.saveFormula();
|
||||
holidayManager.saveAnniversaryFiles();
|
||||
placeStore.cleanUp(); // Cleanup empty metadata stores
|
||||
placeStore.closeAll();
|
||||
}
|
||||
|
||||
catch (Exception e) { e.printStackTrace(); }
|
||||
|
||||
debug("Canceling all tasks...");
|
||||
getServer().getScheduler().cancelTasks(this); // This removes our tasks
|
||||
debug("Unregister all events...");
|
||||
HandlerList.unregisterAll(this); // Cancel event registrations
|
||||
|
||||
if (Config.getInstance().getBackupsEnabled()) {
|
||||
// Remove other tasks BEFORE starting the Backup, or we just cancel it straight away.
|
||||
try {
|
||||
@ -376,6 +379,11 @@ public class mcMMO extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
debug("Canceling all tasks...");
|
||||
getServer().getScheduler().cancelTasks(this); // This removes our tasks
|
||||
debug("Unregister all events...");
|
||||
HandlerList.unregisterAll(this); // Cancel event registrations
|
||||
|
||||
databaseManager.onDisable();
|
||||
debug("Was disabled."); // How informative!
|
||||
}
|
||||
@ -704,5 +712,18 @@ public class mcMMO extends JavaPlugin {
|
||||
return commandManager;
|
||||
}
|
||||
|
||||
public static TransientEntityTracker getTransientEntityTracker() {
|
||||
return transientEntityTracker;
|
||||
}
|
||||
|
||||
public static synchronized boolean isServerShutdownExecuted() {
|
||||
return serverShutdownExecuted;
|
||||
}
|
||||
|
||||
private static synchronized void setServerShutdown(boolean bool) {
|
||||
serverShutdownExecuted = bool;
|
||||
}
|
||||
|
||||
|
||||
public @NotNull SkillRegister getSkillRegister() { return skillRegister; }
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ import org.bukkit.entity.*;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
@ -396,7 +395,7 @@ public class FishingManager extends SkillManager {
|
||||
|
||||
if (treasure != null) {
|
||||
if(treasure instanceof FishingTreasureBook) {
|
||||
treasureDrop = createEnchantBook((FishingTreasureBook) treasure);
|
||||
treasureDrop = ItemUtils.createEnchantBook((FishingTreasureBook) treasure);
|
||||
} else {
|
||||
treasureDrop = treasure.getDrop().clone(); // Not cloning is bad, m'kay?
|
||||
|
||||
@ -450,37 +449,16 @@ public class FishingManager extends SkillManager {
|
||||
}
|
||||
|
||||
if(fishingSucceeds) {
|
||||
fishingCatch.setItemStack(treasureDrop);
|
||||
|
||||
if (Config.getInstance().getFishingExtraFish()) {
|
||||
Misc.spawnItem(player.getEyeLocation(), fishingCatch.getItemStack(), ItemSpawnReason.FISHING_EXTRA_FISH);
|
||||
}
|
||||
|
||||
fishingCatch.setItemStack(treasureDrop);
|
||||
}
|
||||
|
||||
applyXpGain(fishXp + treasureXp, XPGainReason.PVE);
|
||||
}
|
||||
|
||||
|
||||
private @NotNull ItemStack createEnchantBook(@NotNull FishingTreasureBook fishingTreasureBook) {
|
||||
ItemStack itemStack = fishingTreasureBook.getDrop().clone();
|
||||
EnchantmentWrapper enchantmentWrapper = getRandomEnchantment(fishingTreasureBook.getLegalEnchantments());
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
|
||||
if(itemMeta == null)
|
||||
return itemStack;
|
||||
|
||||
itemMeta.addEnchant(enchantmentWrapper.getEnchantment(), enchantmentWrapper.getEnchantmentLevel(), ExperienceConfig.getInstance().allowUnsafeEnchantments());
|
||||
itemStack.setItemMeta(itemMeta);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private @NotNull EnchantmentWrapper getRandomEnchantment(@NotNull List<EnchantmentWrapper> enchantmentWrappers) {
|
||||
Collections.shuffle(enchantmentWrappers, Misc.getRandom());
|
||||
|
||||
int randomIndex = Misc.getRandom().nextInt(enchantmentWrappers.size());
|
||||
return enchantmentWrappers.get(randomIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the vanilla XP boost for Fishing
|
||||
*
|
||||
|
@ -32,9 +32,7 @@ import org.bukkit.entity.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class TamingManager extends SkillManager {
|
||||
//TODO: Temporary static cache, will be changed in 2.2
|
||||
@ -42,8 +40,6 @@ public class TamingManager extends SkillManager {
|
||||
private static HashMap<CallOfTheWildType, TamingSummon> cotwSummonDataProperties;
|
||||
private long lastSummonTimeStamp;
|
||||
|
||||
private HashMap<CallOfTheWildType, List<TrackedTamingEntity>> playerSummonedEntities;
|
||||
|
||||
public TamingManager(@NotNull OnlineMMOPlayer mmoPlayer) {
|
||||
super(mmoPlayer, PrimarySkillType.TAMING);
|
||||
init();
|
||||
@ -55,20 +51,12 @@ public class TamingManager extends SkillManager {
|
||||
lastSummonTimeStamp = 0L;
|
||||
|
||||
//Init per-player tracking of summoned entities
|
||||
initPerPlayerSummonTracking();
|
||||
mcMMO.getTransientEntityTracker().initPlayer(mmoPlayer.getPlayer());
|
||||
|
||||
//Hacky stuff used as a band-aid
|
||||
initStaticCaches();
|
||||
}
|
||||
|
||||
private void initPerPlayerSummonTracking() {
|
||||
playerSummonedEntities = new HashMap<>();
|
||||
|
||||
for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
|
||||
playerSummonedEntities.put(callOfTheWildType, new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
private void initStaticCaches() {
|
||||
//TODO: Temporary static cache, will be changed in 2.2
|
||||
//This is shared between instances of TamingManager
|
||||
@ -503,58 +491,12 @@ public class TamingManager extends SkillManager {
|
||||
return summoningItems.containsKey(itemStack.getType());
|
||||
}
|
||||
|
||||
//TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
|
||||
private int getAmountCurrentlySummoned(CallOfTheWildType callOfTheWildType) {
|
||||
//The tracker is unreliable so validate its contents first
|
||||
recalibrateTracker();
|
||||
|
||||
return playerSummonedEntities.get(callOfTheWildType).size();
|
||||
private int getAmountCurrentlySummoned(@NotNull CallOfTheWildType callOfTheWildType) {
|
||||
return mcMMO.getTransientEntityTracker().getAmountCurrentlySummoned(getPlayer().getUniqueId(), callOfTheWildType);
|
||||
}
|
||||
|
||||
//TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
|
||||
private void addToTracker(@NotNull LivingEntity livingEntity, @NotNull CallOfTheWildType callOfTheWildType) {
|
||||
TrackedTamingEntity trackedEntity = new TrackedTamingEntity(livingEntity, callOfTheWildType, this);
|
||||
|
||||
playerSummonedEntities.get(callOfTheWildType).add(trackedEntity);
|
||||
}
|
||||
|
||||
//TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
|
||||
public List<TrackedTamingEntity> getTrackedEntities(CallOfTheWildType callOfTheWildType) {
|
||||
return playerSummonedEntities.get(callOfTheWildType);
|
||||
}
|
||||
|
||||
//TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
|
||||
public void removeFromTracker(@NotNull TrackedTamingEntity trackedEntity) {
|
||||
playerSummonedEntities.get(trackedEntity.getCallOfTheWildType()).remove(trackedEntity);
|
||||
|
||||
NotificationManager.sendPlayerInformationChatOnly(getPlayer(), "Taming.Summon.COTW.TimeExpired", StringUtils.getPrettyEntityTypeString(trackedEntity.getLivingEntity().getType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new tracked list by determining which tracked things are still valid
|
||||
*/
|
||||
//TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
|
||||
private void recalibrateTracker() {
|
||||
for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
|
||||
ArrayList<TrackedTamingEntity> validEntities = getValidTrackedEntities(callOfTheWildType);
|
||||
playerSummonedEntities.put(callOfTheWildType, validEntities); //Replace the old list with the new list
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
|
||||
private @NotNull ArrayList<TrackedTamingEntity> getValidTrackedEntities(@NotNull CallOfTheWildType callOfTheWildType) {
|
||||
ArrayList<TrackedTamingEntity> validTrackedEntities = new ArrayList<>();
|
||||
|
||||
for(TrackedTamingEntity trackedTamingEntity : getTrackedEntities(callOfTheWildType)) {
|
||||
LivingEntity livingEntity = trackedTamingEntity.getLivingEntity();
|
||||
|
||||
//Remove from existence
|
||||
if(livingEntity != null && livingEntity.isValid()) {
|
||||
validTrackedEntities.add(trackedTamingEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return validTrackedEntities;
|
||||
mcMMO.getTransientEntityTracker().registerEntity(getPlayer().getUniqueId(), new TrackedTamingEntity(livingEntity, callOfTheWildType, getPlayer()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -563,20 +505,6 @@ public class TamingManager extends SkillManager {
|
||||
*/
|
||||
//TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
|
||||
public void cleanupAllSummons() {
|
||||
for(List<TrackedTamingEntity> trackedTamingEntities : playerSummonedEntities.values()) {
|
||||
for(TrackedTamingEntity trackedTamingEntity : trackedTamingEntities) {
|
||||
LivingEntity livingEntity = trackedTamingEntity.getLivingEntity();
|
||||
|
||||
//Remove from existence
|
||||
if(livingEntity != null && livingEntity.isValid()) {
|
||||
mcMMO.getCompatibilityManager().getPersistentDataLayer().removeMobFlags(livingEntity);
|
||||
livingEntity.setHealth(0);
|
||||
livingEntity.remove();
|
||||
}
|
||||
}
|
||||
|
||||
//Clear the list
|
||||
trackedTamingEntities.clear();
|
||||
}
|
||||
mcMMO.getTransientEntityTracker().cleanupPlayer(getPlayer());
|
||||
}
|
||||
}
|
||||
|
@ -4,61 +4,40 @@ import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class TrackedTamingEntity extends BukkitRunnable {
|
||||
private final LivingEntity livingEntity;
|
||||
private final CallOfTheWildType callOfTheWildType;
|
||||
private final UUID id;
|
||||
private int length;
|
||||
private final TamingManager tamingManagerRef;
|
||||
private final @NotNull LivingEntity livingEntity;
|
||||
private final @NotNull CallOfTheWildType callOfTheWildType;
|
||||
private final @NotNull Player player;
|
||||
|
||||
protected TrackedTamingEntity(LivingEntity livingEntity, CallOfTheWildType callOfTheWildType, TamingManager tamingManagerRef) {
|
||||
this.tamingManagerRef = tamingManagerRef;
|
||||
protected TrackedTamingEntity(@NotNull LivingEntity livingEntity, @NotNull CallOfTheWildType callOfTheWildType, @NotNull Player player) {
|
||||
this.player = player;
|
||||
this.callOfTheWildType = callOfTheWildType;
|
||||
this.livingEntity = livingEntity;
|
||||
this.id = livingEntity.getUniqueId();
|
||||
|
||||
int tamingCOTWLength = Config.getInstance().getTamingCOTWLength(callOfTheWildType.getConfigEntityTypeEntry());
|
||||
|
||||
if (tamingCOTWLength > 0) {
|
||||
this.length = tamingCOTWLength * Misc.TICK_CONVERSION_FACTOR;
|
||||
int length = tamingCOTWLength * Misc.TICK_CONVERSION_FACTOR;
|
||||
this.runTaskLater(mcMMO.p, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (livingEntity.isValid()) {
|
||||
Location location = livingEntity.getLocation();
|
||||
location.getWorld().playSound(location, Sound.BLOCK_FIRE_EXTINGUISH, 0.8F, 0.8F);
|
||||
ParticleEffectUtils.playCallOfTheWildEffect(livingEntity);
|
||||
|
||||
if(tamingManagerRef != null)
|
||||
tamingManagerRef.removeFromTracker(this);
|
||||
|
||||
livingEntity.setHealth(0);
|
||||
livingEntity.remove();
|
||||
}
|
||||
|
||||
mcMMO.getTransientEntityTracker().removeSummon(this.getLivingEntity(), player, true);
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
public CallOfTheWildType getCallOfTheWildType() {
|
||||
public @NotNull CallOfTheWildType getCallOfTheWildType() {
|
||||
return callOfTheWildType;
|
||||
}
|
||||
|
||||
public LivingEntity getLivingEntity() {
|
||||
public @NotNull LivingEntity getLivingEntity() {
|
||||
return livingEntity;
|
||||
}
|
||||
|
||||
public UUID getID() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
@ -182,11 +182,14 @@ public final class BlockUtils {
|
||||
* @return true if the block should affected by Super Breaker, false
|
||||
* otherwise
|
||||
*/
|
||||
public static Boolean affectedBySuperBreaker(BlockState blockState) {
|
||||
public static boolean affectedBySuperBreaker(BlockState blockState) {
|
||||
if(mcMMO.getMaterialMapStore().isIntendedToolPickaxe(blockState.getType()))
|
||||
return true;
|
||||
|
||||
if (ExperienceConfig.getInstance().doesBlockGiveSkillXP(PrimarySkillType.MINING, blockState.getBlockData()))
|
||||
return true;
|
||||
|
||||
return isOre(blockState) || mcMMO.getModManager().isCustomMiningBlock(blockState);
|
||||
return mcMMO.getModManager().isCustomMiningBlock(blockState);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,10 @@ package com.gmail.nossr50.util;
|
||||
|
||||
import com.gmail.nossr50.config.AdvancedConfig;
|
||||
import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.config.party.ItemWeightConfig;
|
||||
import com.gmail.nossr50.datatypes.treasure.EnchantmentWrapper;
|
||||
import com.gmail.nossr50.datatypes.treasure.FishingTreasureBook;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -12,9 +15,11 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.FurnaceRecipe;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.Recipe;
|
||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.bukkit.Material.AIR;
|
||||
@ -581,4 +586,26 @@ public final class ItemUtils {
|
||||
public static boolean canBeSuperAbilityDigBoosted(@NotNull ItemStack itemStack) {
|
||||
return isShovel(itemStack) || isPickaxe(itemStack);
|
||||
}
|
||||
|
||||
public static @NotNull ItemStack createEnchantBook(@NotNull FishingTreasureBook fishingTreasureBook) {
|
||||
ItemStack itemStack = fishingTreasureBook.getDrop().clone();
|
||||
EnchantmentWrapper enchantmentWrapper = getRandomEnchantment(fishingTreasureBook.getLegalEnchantments());
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
|
||||
if(itemMeta == null) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) itemMeta;
|
||||
enchantmentStorageMeta.addStoredEnchant(enchantmentWrapper.getEnchantment(), enchantmentWrapper.getEnchantmentLevel(), ExperienceConfig.getInstance().allowUnsafeEnchantments());
|
||||
itemStack.setItemMeta(enchantmentStorageMeta);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
public static @NotNull EnchantmentWrapper getRandomEnchantment(@NotNull List<EnchantmentWrapper> enchantmentWrappers) {
|
||||
Collections.shuffle(enchantmentWrappers, Misc.getRandom());
|
||||
|
||||
int randomIndex = Misc.getRandom().nextInt(enchantmentWrappers.size());
|
||||
return enchantmentWrappers.get(randomIndex);
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ public class MaterialMapStore {
|
||||
private final @NotNull HashSet<String> enchantables;
|
||||
|
||||
private final @NotNull HashSet<String> ores;
|
||||
private final @NotNull HashSet<String> intendedToolPickAxe;
|
||||
private final @NotNull HashSet<String> intendedToolShovel;
|
||||
|
||||
private final @NotNull HashMap<String, Integer> tierValue;
|
||||
|
||||
@ -100,14 +102,16 @@ public class MaterialMapStore {
|
||||
enchantables = new HashSet<>();
|
||||
|
||||
ores = new HashSet<>();
|
||||
intendedToolPickAxe = new HashSet<>();
|
||||
intendedToolShovel = new HashSet<>();
|
||||
|
||||
tierValue = new HashMap<>();
|
||||
|
||||
fillVanillaMaterialRegisters();
|
||||
}
|
||||
|
||||
private void fillVanillaMaterialRegisters()
|
||||
{
|
||||
private void fillVanillaMaterialRegisters() {
|
||||
//The order matters
|
||||
fillAbilityBlackList();
|
||||
fillToolBlackList();
|
||||
fillMossyWhiteList();
|
||||
@ -122,6 +126,7 @@ public class MaterialMapStore {
|
||||
fillTools();
|
||||
fillEnchantables();
|
||||
fillOres();
|
||||
fillIntendedTools();
|
||||
|
||||
fillTierMap();
|
||||
}
|
||||
@ -206,6 +211,190 @@ public class MaterialMapStore {
|
||||
ores.add("gilded_blackstone");
|
||||
}
|
||||
|
||||
private void fillIntendedTools() {
|
||||
intendedToolPickAxe.addAll(ores);
|
||||
|
||||
intendedToolPickAxe.add("ice");
|
||||
intendedToolPickAxe.add("packed_ice");
|
||||
intendedToolPickAxe.add("blue_ice");
|
||||
intendedToolPickAxe.add("frosted_ice");
|
||||
intendedToolPickAxe.add("anvil");
|
||||
intendedToolPickAxe.add("bell");
|
||||
intendedToolPickAxe.add("block_of_redstone");
|
||||
intendedToolPickAxe.add("brewing_stand");
|
||||
intendedToolPickAxe.add("cauldron");
|
||||
intendedToolPickAxe.add("chain");
|
||||
intendedToolPickAxe.add("hopper");
|
||||
intendedToolPickAxe.add("iron_bars");
|
||||
intendedToolPickAxe.add("iron_door");
|
||||
intendedToolPickAxe.add("iron_trapdoor");
|
||||
intendedToolPickAxe.add("lantern");
|
||||
intendedToolPickAxe.add("weighted_pressure_plates");
|
||||
intendedToolPickAxe.add("block_of_iron");
|
||||
intendedToolPickAxe.add("copper_blocks");
|
||||
intendedToolPickAxe.add("cut_copper");
|
||||
intendedToolPickAxe.add("cut_copper_slab");
|
||||
intendedToolPickAxe.add("cut_copper_stairs");
|
||||
intendedToolPickAxe.add("lapis_lazuli_block");
|
||||
intendedToolPickAxe.add("lightning_rod");
|
||||
intendedToolPickAxe.add("block_of_diamond");
|
||||
intendedToolPickAxe.add("block_of_emerald");
|
||||
intendedToolPickAxe.add("block_of_gold");
|
||||
intendedToolPickAxe.add("block_of_netherite");
|
||||
intendedToolPickAxe.add("piston");
|
||||
intendedToolPickAxe.add("sticky_piston");
|
||||
intendedToolPickAxe.add("conduit");
|
||||
intendedToolPickAxe.add("shulker_box");
|
||||
intendedToolPickAxe.add("element_constructor"); //be & ee
|
||||
intendedToolPickAxe.add("compound_creator"); //be & ee
|
||||
intendedToolPickAxe.add("material_reducer"); //be & ee
|
||||
intendedToolPickAxe.add("activator_rail");
|
||||
intendedToolPickAxe.add("detector_rail");
|
||||
intendedToolPickAxe.add("powered_rail");
|
||||
intendedToolPickAxe.add("rail");
|
||||
intendedToolPickAxe.add("andesite");
|
||||
intendedToolPickAxe.add("basalt");
|
||||
intendedToolPickAxe.add("blackstone");
|
||||
intendedToolPickAxe.add("blast_furnace");
|
||||
intendedToolPickAxe.add("block_of_coal");
|
||||
intendedToolPickAxe.add("block_of_quartz");
|
||||
intendedToolPickAxe.add("bricks");
|
||||
intendedToolPickAxe.add("cobblestone");
|
||||
intendedToolPickAxe.add("cobblestone_wall");
|
||||
intendedToolPickAxe.add("concrete");
|
||||
intendedToolPickAxe.add("dark_prismarine");
|
||||
intendedToolPickAxe.add("diorite");
|
||||
intendedToolPickAxe.add("dispenser");
|
||||
intendedToolPickAxe.add("dripstone_block");
|
||||
intendedToolPickAxe.add("dropper");
|
||||
intendedToolPickAxe.add("enchantment_table");
|
||||
intendedToolPickAxe.add("end_stone");
|
||||
intendedToolPickAxe.add("ender_chest");
|
||||
intendedToolPickAxe.add("furnace");
|
||||
intendedToolPickAxe.add("glazed_terracotta");
|
||||
intendedToolPickAxe.add("granite");
|
||||
intendedToolPickAxe.add("grindstone");
|
||||
intendedToolPickAxe.add("heat_block"); //be & ee
|
||||
intendedToolPickAxe.add("lodestone");
|
||||
intendedToolPickAxe.add("mossy_cobblestone");
|
||||
intendedToolPickAxe.add("nether_bricks");
|
||||
intendedToolPickAxe.add("nether_brick_fence");
|
||||
intendedToolPickAxe.add("nether_gold_ore");
|
||||
intendedToolPickAxe.add("nether_quartz_ore");
|
||||
intendedToolPickAxe.add("netherrack");
|
||||
intendedToolPickAxe.add("observer");
|
||||
intendedToolPickAxe.add("prismarine");
|
||||
intendedToolPickAxe.add("prismarine_bricks");
|
||||
intendedToolPickAxe.add("pointed_dripstone");
|
||||
intendedToolPickAxe.add("polished_andesite");
|
||||
intendedToolPickAxe.add("polished_blackstone");
|
||||
intendedToolPickAxe.add("polished_blackstone_bricks");
|
||||
intendedToolPickAxe.add("polished_diorite");
|
||||
intendedToolPickAxe.add("polished_granite");
|
||||
intendedToolPickAxe.add("red_sandstone");
|
||||
intendedToolPickAxe.add("sandstone");
|
||||
intendedToolPickAxe.add("smoker");
|
||||
intendedToolPickAxe.add("spawner");
|
||||
intendedToolPickAxe.add("stonecutter");
|
||||
// intendedToolPickAxe.add("slabs");
|
||||
intendedToolPickAxe.add("colored_terracotta");
|
||||
// intendedToolPickAxe.add("stairs");
|
||||
intendedToolPickAxe.add("smooth_stone");
|
||||
intendedToolPickAxe.add("stone");
|
||||
intendedToolPickAxe.add("stone_bricks");
|
||||
intendedToolPickAxe.add("stone_button");
|
||||
intendedToolPickAxe.add("stone_pressure_plate");
|
||||
intendedToolPickAxe.add("terracotta");
|
||||
intendedToolPickAxe.add("amethyst_bud");
|
||||
intendedToolPickAxe.add("amethyst_cluster");
|
||||
intendedToolPickAxe.add("block_of_amethyst");
|
||||
intendedToolPickAxe.add("budding_amethyst");
|
||||
intendedToolPickAxe.add("ancient_debris");
|
||||
intendedToolPickAxe.add("crying_obsidian");
|
||||
intendedToolPickAxe.add("glowing_obsidian"); //be
|
||||
intendedToolPickAxe.add("obsidian");
|
||||
intendedToolPickAxe.add("respawn_anchor");
|
||||
|
||||
//slabs
|
||||
intendedToolPickAxe.add("petrified_oak_slab");
|
||||
intendedToolPickAxe.add("stone_slab");
|
||||
intendedToolPickAxe.add("smooth_stone_slab");
|
||||
intendedToolPickAxe.add("cobblestone_slab");
|
||||
intendedToolPickAxe.add("mossy_cobblestone_slab");
|
||||
intendedToolPickAxe.add("stone_brick_slab");
|
||||
intendedToolPickAxe.add("mossy_stone_brick_slab");
|
||||
intendedToolPickAxe.add("andesite_slab");
|
||||
intendedToolPickAxe.add("polished_andesite_slab");
|
||||
intendedToolPickAxe.add("diorite_slab");
|
||||
intendedToolPickAxe.add("polished_diorite_slab");
|
||||
intendedToolPickAxe.add("granite_slab");
|
||||
intendedToolPickAxe.add("polished_granite_slab");
|
||||
intendedToolPickAxe.add("sandstone_slab");
|
||||
intendedToolPickAxe.add("cut_sandstone_slab");
|
||||
intendedToolPickAxe.add("smooth_sandstone_slab");
|
||||
intendedToolPickAxe.add("red_sandstone_slab");
|
||||
intendedToolPickAxe.add("cut_red_sandstone_slab");
|
||||
intendedToolPickAxe.add("smooth_red_sandstone_slab");
|
||||
intendedToolPickAxe.add("brick_slab");
|
||||
intendedToolPickAxe.add("prismarine_brick_slab");
|
||||
intendedToolPickAxe.add("dark_prismarine_slab");
|
||||
intendedToolPickAxe.add("nether_brick_slab");
|
||||
intendedToolPickAxe.add("red_netherbrick_slab");
|
||||
intendedToolPickAxe.add("quartz_slab");
|
||||
intendedToolPickAxe.add("smooth_quartz_slab");
|
||||
intendedToolPickAxe.add("purpur_slab");
|
||||
intendedToolPickAxe.add("end_stone_brick_slab");
|
||||
intendedToolPickAxe.add("blackstone_slab");
|
||||
intendedToolPickAxe.add("polished_blackstone_slab");
|
||||
intendedToolPickAxe.add("polished_blackstone_brick_slab");
|
||||
intendedToolPickAxe.add("lightly_weathered_cut_copper_slab");
|
||||
intendedToolPickAxe.add("semi_weathered_cut_copper_slab");
|
||||
intendedToolPickAxe.add("waxed_semi_weathered_cut_copper_slab");
|
||||
intendedToolPickAxe.add("weathered_cut_copper_slab");
|
||||
intendedToolPickAxe.add("waxed_cut_copper_slab");
|
||||
intendedToolPickAxe.add("waxed_lightly_weathered_cut_copper_slab");
|
||||
|
||||
//stairs (not all of these exist, just copied the above list and replaced slab with stairs)
|
||||
intendedToolPickAxe.add("petrified_oak_stairs");
|
||||
intendedToolPickAxe.add("stone_stairs");
|
||||
intendedToolPickAxe.add("smooth_stone_stairs");
|
||||
intendedToolPickAxe.add("cobblestone_stairs");
|
||||
intendedToolPickAxe.add("mossy_cobblestone_stairs");
|
||||
intendedToolPickAxe.add("stone_brick_stairs");
|
||||
intendedToolPickAxe.add("mossy_stone_brick_stairs");
|
||||
intendedToolPickAxe.add("andesite_stairs");
|
||||
intendedToolPickAxe.add("polished_andesite_stairs");
|
||||
intendedToolPickAxe.add("diorite_stairs");
|
||||
intendedToolPickAxe.add("polished_diorite_stairs");
|
||||
intendedToolPickAxe.add("granite_stairs");
|
||||
intendedToolPickAxe.add("polished_granite_stairs");
|
||||
intendedToolPickAxe.add("sandstone_stairs");
|
||||
intendedToolPickAxe.add("cut_sandstone_stairs");
|
||||
intendedToolPickAxe.add("smooth_sandstone_stairs");
|
||||
intendedToolPickAxe.add("red_sandstone_stairs");
|
||||
intendedToolPickAxe.add("cut_red_sandstone_stairs");
|
||||
intendedToolPickAxe.add("smooth_red_sandstone_stairs");
|
||||
intendedToolPickAxe.add("brick_stairs");
|
||||
intendedToolPickAxe.add("prismarine_brick_stairs");
|
||||
intendedToolPickAxe.add("dark_prismarine_stairs");
|
||||
intendedToolPickAxe.add("nether_brick_stairs");
|
||||
intendedToolPickAxe.add("red_netherbrick_stairs");
|
||||
intendedToolPickAxe.add("quartz_stairs");
|
||||
intendedToolPickAxe.add("smooth_quartz_stairs");
|
||||
intendedToolPickAxe.add("purpur_stairs");
|
||||
intendedToolPickAxe.add("end_stone_brick_stairs");
|
||||
intendedToolPickAxe.add("blackstone_stairs");
|
||||
intendedToolPickAxe.add("polished_blackstone_stairs");
|
||||
intendedToolPickAxe.add("polished_blackstone_brick_stairs");
|
||||
intendedToolPickAxe.add("lightly_weathered_cut_copper_stairs");
|
||||
intendedToolPickAxe.add("semi_weathered_cut_copper_stairs");
|
||||
intendedToolPickAxe.add("waxed_semi_weathered_cut_copper_stairs");
|
||||
intendedToolPickAxe.add("weathered_cut_copper_stairs");
|
||||
intendedToolPickAxe.add("waxed_cut_copper_stairs");
|
||||
intendedToolPickAxe.add("waxed_lightly_weathered_cut_copper_stairs");
|
||||
|
||||
}
|
||||
|
||||
private void fillArmors() {
|
||||
fillLeatherArmorWhiteList();
|
||||
fillIronArmorWhiteList();
|
||||
@ -1078,6 +1267,14 @@ public class MaterialMapStore {
|
||||
toolBlackList.add("respawn_anchor");
|
||||
}
|
||||
|
||||
public boolean isIntendedToolPickaxe(Material material) {
|
||||
return intendedToolPickAxe.contains(material.getKey().getKey());
|
||||
}
|
||||
|
||||
public boolean isIntendedToolPickaxe(String string) {
|
||||
return intendedToolPickAxe.contains(string);
|
||||
}
|
||||
|
||||
public @NotNull HashSet<String> getNetheriteArmor() {
|
||||
return netheriteArmor;
|
||||
}
|
||||
|
316
src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java
Normal file
316
src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java
Normal file
@ -0,0 +1,316 @@
|
||||
package com.gmail.nossr50.util;
|
||||
|
||||
import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.skills.taming.TrackedTamingEntity;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
||||
import com.gmail.nossr50.util.text.StringUtils;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class TransientEntityTracker {
|
||||
//These two are updated in step with each other
|
||||
private final @NotNull HashMap<UUID, HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>>> perPlayerTransientEntityMap;
|
||||
private final @NotNull HashSet<LivingEntity> chunkLookupCache;
|
||||
|
||||
public TransientEntityTracker() {
|
||||
perPlayerTransientEntityMap = new HashMap<>();
|
||||
chunkLookupCache = new HashSet<>();
|
||||
}
|
||||
|
||||
public synchronized @NotNull HashSet<LivingEntity> getChunkLookupCache() {
|
||||
return chunkLookupCache;
|
||||
}
|
||||
|
||||
public synchronized @NotNull HashMap<UUID, HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>>> getPerPlayerTransientEntityMap() {
|
||||
return perPlayerTransientEntityMap;
|
||||
}
|
||||
|
||||
public synchronized void initPlayer(@NotNull Player player) {
|
||||
if (!isPlayerRegistered(player.getUniqueId())) {
|
||||
registerPlayer(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a player from the tracker
|
||||
*
|
||||
* @param playerUUID target player
|
||||
*/
|
||||
public synchronized void cleanupPlayer(@NotNull UUID playerUUID) {
|
||||
cleanPlayer(null, playerUUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a player from the tracker
|
||||
*
|
||||
* @param player target player
|
||||
*/
|
||||
public synchronized void cleanupPlayer(@NotNull Player player) {
|
||||
cleanPlayer(player, player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a player from the tracker
|
||||
*
|
||||
* @param player target player
|
||||
* @param playerUUID target player UUID
|
||||
*/
|
||||
private void cleanPlayer(@Nullable Player player, @NotNull UUID playerUUID) {
|
||||
cleanupAllSummons(player, player.getUniqueId());
|
||||
removePlayerFromMap(playerUUID);
|
||||
}
|
||||
|
||||
private void removePlayerFromMap(@NotNull UUID playerUUID) {
|
||||
getPerPlayerTransientEntityMap().remove(playerUUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player has already been registered
|
||||
* Being registered constitutes having necessary values initialized in our per-player map
|
||||
*
|
||||
* @param playerUUID target player
|
||||
* @return true if the player is registered
|
||||
*/
|
||||
private synchronized boolean isPlayerRegistered(@NotNull UUID playerUUID) {
|
||||
return getPerPlayerTransientEntityMap().get(playerUUID) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a player to our tracker, which initializes the necessary values in our per-player map
|
||||
*
|
||||
* @param playerUUID player to register
|
||||
*/
|
||||
private synchronized void registerPlayer(@NotNull UUID playerUUID) {
|
||||
getPerPlayerTransientEntityMap().put(playerUUID, new HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>>());
|
||||
|
||||
for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
|
||||
getPerPlayerTransientEntityMap().get(playerUUID).put(callOfTheWildType, new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tracked transient entities map for a specific player
|
||||
*
|
||||
* @param playerUUID the target uuid of the player
|
||||
* @return the tracked entities map for the player, null if the player isn't registered
|
||||
*/
|
||||
public synchronized @Nullable HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>> getPlayerTrackedEntityMap(@NotNull UUID playerUUID) {
|
||||
return getPerPlayerTransientEntityMap().get(playerUUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an entity to a player
|
||||
* This includes registration to our per-player map and our chunk lookup cache
|
||||
*
|
||||
* @param playerUUID target player's UUID
|
||||
* @param trackedTamingEntity target entity
|
||||
*/
|
||||
public synchronized void registerEntity(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) {
|
||||
//Add to map entry
|
||||
getTrackedEntities(playerUUID, trackedTamingEntity.getCallOfTheWildType()).add(trackedTamingEntity);
|
||||
|
||||
//Add to cache for chunk lookups
|
||||
addToChunkLookupCache(trackedTamingEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a living entity is a summon
|
||||
*
|
||||
* @param livingEntity target livinig entity
|
||||
* @return true if target living entity is a summon
|
||||
*/
|
||||
public synchronized boolean isTransientSummon(@NotNull LivingEntity livingEntity) {
|
||||
return getChunkLookupCache().contains(livingEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tracked taming entities for a player
|
||||
* If the player isn't registered this will return null
|
||||
*
|
||||
* @param playerUUID the target uuid of the player
|
||||
* @param callOfTheWildType target type
|
||||
* @return the set of tracked entities for the player, null if the player isn't registered, the set can be empty
|
||||
*/
|
||||
private synchronized @Nullable HashSet<TrackedTamingEntity> getTrackedEntities(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) {
|
||||
HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>> playerEntityMap = getPlayerTrackedEntityMap(playerUUID);
|
||||
|
||||
if(playerEntityMap == null)
|
||||
return null;
|
||||
|
||||
return playerEntityMap.get(callOfTheWildType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity to our chunk lookup cache
|
||||
*
|
||||
* @param trackedTamingEntity target tracked taming entity
|
||||
*/
|
||||
private synchronized void addToChunkLookupCache(@NotNull TrackedTamingEntity trackedTamingEntity) {
|
||||
getChunkLookupCache().add(trackedTamingEntity.getLivingEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entity from our tracker
|
||||
* This includes removal from our per-player map and our chunk lookup cache
|
||||
*
|
||||
* @param livingEntity target entity
|
||||
*/
|
||||
private void unregisterEntity(@NotNull LivingEntity livingEntity) {
|
||||
chunkLookupCacheCleanup(livingEntity);
|
||||
perPlayerTransientMapCleanup(livingEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entity from our chunk lookup cache
|
||||
*
|
||||
* @param livingEntity target entity
|
||||
*/
|
||||
private void chunkLookupCacheCleanup(@NotNull LivingEntity livingEntity) {
|
||||
getChunkLookupCache().remove(livingEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean a living entity from our tracker
|
||||
* Iterates over all players and their registered entities
|
||||
* Doesn't do any kind of failure checking, if it doesn't find any player with a registered entity nothing bad happens or is reported
|
||||
* However it should never happen like that, so maybe we could consider adding some failure to execute checking in the future
|
||||
*
|
||||
* @param livingEntity
|
||||
*/
|
||||
private void perPlayerTransientMapCleanup(@NotNull LivingEntity livingEntity) {
|
||||
for(UUID uuid : getPerPlayerTransientEntityMap().keySet()) {
|
||||
for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
|
||||
|
||||
HashSet<TrackedTamingEntity> trackedEntities = getTrackedEntities(uuid, callOfTheWildType);
|
||||
|
||||
if(trackedEntities == null)
|
||||
continue;
|
||||
|
||||
Iterator<TrackedTamingEntity> iterator = trackedEntities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if(iterator.next().getLivingEntity().equals(livingEntity)) {
|
||||
iterator.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transient entities that exist in a specific chunk
|
||||
*
|
||||
* @param chunk the chunk to match
|
||||
* @return a list of transient entities that are located in the provided chunk
|
||||
*/
|
||||
public synchronized @NotNull List<LivingEntity> getAllTransientEntitiesInChunk(@NotNull Chunk chunk) {
|
||||
ArrayList<LivingEntity> matchingEntities = new ArrayList<>();
|
||||
|
||||
for(LivingEntity livingEntity : getChunkLookupCache()) {
|
||||
if(livingEntity.getLocation().getChunk().equals(chunk)) {
|
||||
matchingEntities.add(livingEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return matchingEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of a summon currently active for a player
|
||||
*
|
||||
* @param playerUUID target player
|
||||
* @param callOfTheWildType summon type
|
||||
* @return the amount of summons currently active for player of target type
|
||||
*/
|
||||
public synchronized int getAmountCurrentlySummoned(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) {
|
||||
HashSet<TrackedTamingEntity> trackedEntities = getTrackedEntities(playerUUID, callOfTheWildType);
|
||||
|
||||
if(trackedEntities == null)
|
||||
return 0;
|
||||
|
||||
return trackedEntities.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills a summon and removes its metadata
|
||||
* Then it removes it from the tracker / chunk lookup cache
|
||||
*
|
||||
* @param livingEntity entity to remove
|
||||
* @param player associated player
|
||||
*/
|
||||
public synchronized void removeSummon(@NotNull LivingEntity livingEntity, @Nullable Player player, boolean timeExpired) {
|
||||
//Kill the summon & remove it
|
||||
if(livingEntity.isValid()) {
|
||||
livingEntity.setHealth(0); //Should trigger entity death events
|
||||
livingEntity.remove();
|
||||
|
||||
Location location = livingEntity.getLocation();
|
||||
|
||||
if (location.getWorld() != null) {
|
||||
location.getWorld().playSound(location, Sound.BLOCK_FIRE_EXTINGUISH, 0.8F, 0.8F);
|
||||
ParticleEffectUtils.playCallOfTheWildEffect(livingEntity);
|
||||
}
|
||||
|
||||
//Inform player of summon death
|
||||
if(player != null && player.isOnline()) {
|
||||
if(timeExpired) {
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.TimeExpired", StringUtils.getPrettyEntityTypeString(livingEntity.getType()));
|
||||
} else {
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.Removed", StringUtils.getPrettyEntityTypeString(livingEntity.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Remove our metadata
|
||||
mcMMO.getCompatibilityManager().getPersistentDataLayer().removeMobFlags(livingEntity);
|
||||
|
||||
//Clean from trackers
|
||||
unregisterEntity(livingEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all tracked entities from existence if they currently exist
|
||||
* Clear the tracked entity lists afterwards
|
||||
*
|
||||
* @deprecated use {@link #cleanupAllSummons(Player, UUID)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
private void cleanupAllSummons(@NotNull UUID playerUUID) {
|
||||
cleanupAllSummons(Bukkit.getPlayer(playerUUID), playerUUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills and cleans up all data related to all summoned entities for a player
|
||||
*
|
||||
* @param player used to send messages, can be null
|
||||
* @param playerUUID used to grab associated data, cannot be null
|
||||
*/
|
||||
private void cleanupAllSummons(@Nullable Player player, @NotNull UUID playerUUID) {
|
||||
for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
|
||||
HashSet<TrackedTamingEntity> trackedEntities = getTrackedEntities(playerUUID, callOfTheWildType);
|
||||
|
||||
if(trackedEntities == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ImmutableSet<TrackedTamingEntity> immutableSet = ImmutableSet.copyOf(trackedEntities);
|
||||
|
||||
for(TrackedTamingEntity trackedTamingEntity : immutableSet) {
|
||||
//Remove from existence
|
||||
removeSummon(trackedTamingEntity.getLivingEntity(), player, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -2,32 +2,37 @@ package com.gmail.nossr50.util.blockmeta;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.BitSet;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BitSetChunkStore implements ChunkStore, Serializable {
|
||||
private static final long serialVersionUID = -1L;
|
||||
transient private boolean dirty = false;
|
||||
// Bitset store conforms to a "bottom-up" bit ordering consisting of a stack of {worldHeight} Y planes, each Y plane consists of 16 Z rows of 16 X bits.
|
||||
private BitSet store;
|
||||
public class BitSetChunkStore implements ChunkStore {
|
||||
private static final int CURRENT_VERSION = 8;
|
||||
private static final int MAGIC_NUMBER = 0xEA5EDEBB;
|
||||
private int cx;
|
||||
private int cz;
|
||||
private int worldHeight;
|
||||
private UUID worldUid;
|
||||
|
||||
public BitSetChunkStore(World world, int cx, int cz) {
|
||||
this.cx = cx;
|
||||
this.cz = cz;
|
||||
this.worldUid = world.getUID();
|
||||
this.worldHeight = world.getMaxHeight();
|
||||
this.store = new BitSet(16 * 16 * worldHeight);
|
||||
private final int cx;
|
||||
private final int cz;
|
||||
private final int worldHeight;
|
||||
private final @NotNull UUID worldUid;
|
||||
// Bitset store conforms to a "bottom-up" bit ordering consisting of a stack of {worldHeight} Y planes, each Y plane consists of 16 Z rows of 16 X bits.
|
||||
private final @NotNull BitSet store;
|
||||
|
||||
private transient boolean dirty = false;
|
||||
|
||||
public BitSetChunkStore(@NotNull World world, int cx, int cz) {
|
||||
this(world.getUID(), world.getMaxHeight(), cx, cz);
|
||||
}
|
||||
|
||||
private BitSetChunkStore() {}
|
||||
private BitSetChunkStore(@NotNull UUID worldUid, int worldHeight, int cx, int cz) {
|
||||
this.cx = cx;
|
||||
this.cz = cz;
|
||||
this.worldUid = worldUid;
|
||||
this.worldHeight = worldHeight;
|
||||
this.store = new BitSet(16 * 16 * worldHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
@ -50,7 +55,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getWorldId() {
|
||||
public @NotNull UUID getWorldId() {
|
||||
return worldUid;
|
||||
}
|
||||
|
||||
@ -81,61 +86,27 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
|
||||
}
|
||||
|
||||
private int coordToIndex(int x, int y, int z) {
|
||||
return coordToIndex(x, y, z, worldHeight);
|
||||
}
|
||||
|
||||
private static int coordToIndex(int x, int y, int z, int worldHeight) {
|
||||
if (x < 0 || x >= 16 || y < 0 || y >= worldHeight || z < 0 || z >= 16)
|
||||
throw new IndexOutOfBoundsException();
|
||||
throw new IndexOutOfBoundsException(String.format("x: %d y: %d z: %d World Height: %d", x, y, z, worldHeight));
|
||||
return (z * 16 + x) + (256 * y);
|
||||
}
|
||||
|
||||
private void fixWorldHeight() {
|
||||
private static int getWorldHeight(@NotNull UUID worldUid, int storedWorldHeight)
|
||||
{
|
||||
World world = Bukkit.getWorld(worldUid);
|
||||
|
||||
// Not sure how this case could come up, but might as well handle it gracefully. Loading a chunkstore for an unloaded world?
|
||||
if (world == null)
|
||||
return;
|
||||
return storedWorldHeight;
|
||||
|
||||
// Lop off any extra data if the world height has shrunk
|
||||
int currentWorldHeight = world.getMaxHeight();
|
||||
if (currentWorldHeight < worldHeight)
|
||||
{
|
||||
store.clear(coordToIndex(16, currentWorldHeight, 16), store.length());
|
||||
worldHeight = currentWorldHeight;
|
||||
dirty = true;
|
||||
}
|
||||
// If the world height has grown, update the worldHeight variable, but don't bother marking it dirty as unless something else changes we don't need to force a file write;
|
||||
else if (currentWorldHeight > worldHeight)
|
||||
worldHeight = currentWorldHeight;
|
||||
return world.getMaxHeight();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
throw new UnsupportedOperationException("Serializable support should only be used for legacy deserialization");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.readInt(); // Magic number
|
||||
in.readInt(); // Format version
|
||||
long lsb = in.readLong();
|
||||
long msb = in.readLong();
|
||||
worldUid = new UUID(msb, lsb);
|
||||
cx = in.readInt();
|
||||
cz = in.readInt();
|
||||
|
||||
boolean[][][] oldStore = (boolean[][][]) in.readObject();
|
||||
worldHeight = oldStore[0][0].length;
|
||||
store = new BitSet(16 * 16 * worldHeight / 8);
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < worldHeight; y++) {
|
||||
store.set(coordToIndex(x, y, z), oldStore[x][z][y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
dirty = true;
|
||||
fixWorldHeight();
|
||||
}
|
||||
|
||||
private void serialize(DataOutputStream out) throws IOException {
|
||||
private void serialize(@NotNull DataOutputStream out) throws IOException {
|
||||
out.writeInt(MAGIC_NUMBER);
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
|
||||
@ -153,7 +124,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
private static BitSetChunkStore deserialize(DataInputStream in) throws IOException {
|
||||
private static @NotNull BitSetChunkStore deserialize(@NotNull DataInputStream in) throws IOException {
|
||||
int magic = in.readInt();
|
||||
// Can be used to determine the format of the file
|
||||
int fileVersionNumber = in.readInt();
|
||||
@ -161,28 +132,36 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
|
||||
if (magic != MAGIC_NUMBER || fileVersionNumber != CURRENT_VERSION)
|
||||
throw new IOException();
|
||||
|
||||
BitSetChunkStore chunkStore = new BitSetChunkStore();
|
||||
|
||||
long lsb = in.readLong();
|
||||
long msb = in.readLong();
|
||||
chunkStore.worldUid = new UUID(msb, lsb);
|
||||
chunkStore.cx = in.readInt();
|
||||
chunkStore.cz = in.readInt();
|
||||
UUID worldUid = new UUID(msb, lsb);
|
||||
int cx = in.readInt();
|
||||
int cz = in.readInt();
|
||||
|
||||
chunkStore.worldHeight = in.readInt();
|
||||
int worldHeight = in.readInt();
|
||||
byte[] temp = new byte[in.readInt()];
|
||||
in.readFully(temp);
|
||||
chunkStore.store = BitSet.valueOf(temp);
|
||||
BitSet stored = BitSet.valueOf(temp);
|
||||
|
||||
int currentWorldHeight = getWorldHeight(worldUid, worldHeight);
|
||||
|
||||
boolean worldHeightShrunk = currentWorldHeight < worldHeight;
|
||||
// Lop off extra data if world height has shrunk
|
||||
if (worldHeightShrunk)
|
||||
stored.clear(coordToIndex(16, currentWorldHeight, 16, worldHeight), stored.length());
|
||||
|
||||
BitSetChunkStore chunkStore = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz);
|
||||
chunkStore.store.or(stored);
|
||||
chunkStore.dirty = worldHeightShrunk; // In the expanded case there is no reason to re-write it unless the data changes
|
||||
|
||||
chunkStore.fixWorldHeight();
|
||||
return chunkStore;
|
||||
}
|
||||
|
||||
public static class Serialization {
|
||||
|
||||
public static final short STREAM_MAGIC = (short)0xACDC;
|
||||
public static final short STREAM_MAGIC = (short)0xACDC; // Rock on
|
||||
|
||||
public static ChunkStore readChunkStore(DataInputStream inputStream) throws IOException {
|
||||
public static @Nullable ChunkStore readChunkStore(@NotNull DataInputStream inputStream) throws IOException {
|
||||
if (inputStream.markSupported())
|
||||
inputStream.mark(2);
|
||||
short magicNumber = inputStream.readShort();
|
||||
@ -196,7 +175,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
|
||||
{
|
||||
// Creates a new stream with the two magic number bytes and then the rest of the original stream... Java is so dumb. I just wanted to look at two bytes.
|
||||
PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream, 2);
|
||||
pushbackInputStream.unread((magicNumber >>> 0) & 0xFF);
|
||||
pushbackInputStream.unread((magicNumber) & 0xFF);
|
||||
pushbackInputStream.unread((magicNumber >>> 8) & 0xFF);
|
||||
inputStream = new DataInputStream(pushbackInputStream);
|
||||
}
|
||||
@ -209,31 +188,85 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
|
||||
throw new IOException("Bad Data Format");
|
||||
}
|
||||
|
||||
public static void writeChunkStore(DataOutputStream outputStream, ChunkStore chunkStore) throws IOException {
|
||||
public static void writeChunkStore(@NotNull DataOutputStream outputStream, @NotNull ChunkStore chunkStore) throws IOException {
|
||||
if (!(chunkStore instanceof BitSetChunkStore))
|
||||
throw new InvalidClassException("ChunkStore must be instance of BitSetChunkStore");
|
||||
outputStream.writeShort(STREAM_MAGIC);
|
||||
((BitSetChunkStore)chunkStore).serialize(outputStream);
|
||||
}
|
||||
|
||||
// Handles loading the old serialized classes even though we have changed name/package
|
||||
// Handles loading the old serialized class
|
||||
private static class LegacyDeserializationInputStream extends ObjectInputStream {
|
||||
public LegacyDeserializationInputStream(InputStream in) throws IOException {
|
||||
private static class LegacyChunkStoreDeserializer implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = -1L;
|
||||
|
||||
private int cx;
|
||||
private int cz;
|
||||
private int worldHeight;
|
||||
private UUID worldUid;
|
||||
private boolean[][][] store;
|
||||
|
||||
private LegacyChunkStoreDeserializer() {}
|
||||
|
||||
@Deprecated
|
||||
private void writeObject(@NotNull ObjectOutputStream out) throws IOException {
|
||||
throw new UnsupportedOperationException("You goofed.");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void readObject(@NotNull ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.readInt(); // Magic number
|
||||
in.readInt(); // Format version
|
||||
long lsb = in.readLong();
|
||||
long msb = in.readLong();
|
||||
|
||||
worldUid = new UUID(msb, lsb);
|
||||
cx = in.readInt();
|
||||
cz = in.readInt();
|
||||
|
||||
store = (boolean[][][]) in.readObject();
|
||||
worldHeight = store[0][0].length;
|
||||
}
|
||||
|
||||
public @NotNull BitSetChunkStore convert()
|
||||
{
|
||||
int currentWorldHeight = getWorldHeight(worldUid, worldHeight);
|
||||
|
||||
BitSetChunkStore converted = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz);
|
||||
|
||||
// Read old data into new chunkstore
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < worldHeight && y < currentWorldHeight; y++) {
|
||||
converted.store.set(converted.coordToIndex(x, y, z), store[x][z][y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mark dirty so it will be re-written in new format on close
|
||||
converted.dirty = true;
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public LegacyDeserializationInputStream(@NotNull InputStream in) throws IOException {
|
||||
super(in);
|
||||
enableResolveObject(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
|
||||
protected @NotNull ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
|
||||
ObjectStreamClass read = super.readClassDescriptor();
|
||||
if (read.getName().contentEquals("com.gmail.nossr50.util.blockmeta.chunkmeta.PrimitiveChunkStore"))
|
||||
return ObjectStreamClass.lookup(BitSetChunkStore.class);
|
||||
return ObjectStreamClass.lookup(LegacyChunkStoreDeserializer.class);
|
||||
return read;
|
||||
}
|
||||
|
||||
public ChunkStore readLegacyChunkStore(){
|
||||
public @Nullable ChunkStore readLegacyChunkStore(){
|
||||
try {
|
||||
return (ChunkStore) readObject();
|
||||
LegacyChunkStoreDeserializer deserializer = (LegacyChunkStoreDeserializer)readObject();
|
||||
return deserializer.convert();
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1,126 +1,10 @@
|
||||
package com.gmail.nossr50.util.blockmeta;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ChunkManager {
|
||||
public interface ChunkManager extends UserBlockTracker {
|
||||
void closeAll();
|
||||
|
||||
/**
|
||||
* Saves a given Chunk's Chunklet data
|
||||
*
|
||||
* @param cx Chunk X coordinate that is to be saved
|
||||
* @param cz Chunk Z coordinate that is to be saved
|
||||
* @param world World that the Chunk is in
|
||||
*/
|
||||
void saveChunk(int cx, int cz, World world);
|
||||
|
||||
/**
|
||||
* Informs the ChunkletManager a chunk is unloaded
|
||||
*
|
||||
* @param cx Chunk X coordinate that is unloaded
|
||||
* @param cz Chunk Z coordinate that is unloaded
|
||||
* @param world World that the chunk was unloaded in
|
||||
*/
|
||||
void chunkUnloaded(int cx, int cz, World world);
|
||||
|
||||
/**
|
||||
* Save all ChunkletStores related to the given world
|
||||
*
|
||||
* @param world World to save
|
||||
*/
|
||||
void saveWorld(World world);
|
||||
|
||||
/**
|
||||
* Unload all ChunkletStores from memory related to the given world after saving them
|
||||
*
|
||||
* @param world World to unload
|
||||
*/
|
||||
void unloadWorld(World world);
|
||||
|
||||
/**
|
||||
* Save all ChunkletStores
|
||||
*/
|
||||
void saveAll();
|
||||
|
||||
/**
|
||||
* Check to see if a given location is set to true
|
||||
*
|
||||
* @param x X coordinate to check
|
||||
* @param y Y coordinate to check
|
||||
* @param z Z coordinate to check
|
||||
* @param world World to check in
|
||||
* @return true if the given location is set to true, false if otherwise
|
||||
*/
|
||||
boolean isTrue(int x, int y, int z, World world);
|
||||
|
||||
/**
|
||||
* Check to see if a given block location is set to true
|
||||
*
|
||||
* @param block Block location to check
|
||||
* @return true if the given block location is set to true, false if otherwise
|
||||
*/
|
||||
boolean isTrue(Block block);
|
||||
|
||||
/**
|
||||
* Check to see if a given BlockState location is set to true
|
||||
*
|
||||
* @param blockState BlockState to check
|
||||
* @return true if the given BlockState location is set to true, false if otherwise
|
||||
*/
|
||||
boolean isTrue(BlockState blockState);
|
||||
|
||||
/**
|
||||
* Set a given location to true, should create stores as necessary if the location does not exist
|
||||
*
|
||||
* @param x X coordinate to set
|
||||
* @param y Y coordinate to set
|
||||
* @param z Z coordinate to set
|
||||
* @param world World to set in
|
||||
*/
|
||||
void setTrue(int x, int y, int z, World world);
|
||||
|
||||
/**
|
||||
* Set a given block location to true, should create stores as necessary if the location does not exist
|
||||
*
|
||||
* @param block Block location to set
|
||||
*/
|
||||
void setTrue(Block block);
|
||||
|
||||
/**
|
||||
* Set a given BlockState location to true, should create stores as necessary if the location does not exist
|
||||
*
|
||||
* @param blockState BlockState location to set
|
||||
*/
|
||||
void setTrue(BlockState blockState);
|
||||
|
||||
/**
|
||||
* Set a given location to false, should not create stores if one does not exist for the given location
|
||||
*
|
||||
* @param x X coordinate to set
|
||||
* @param y Y coordinate to set
|
||||
* @param z Z coordinate to set
|
||||
* @param world World to set in
|
||||
*/
|
||||
void setFalse(int x, int y, int z, World world);
|
||||
|
||||
/**
|
||||
* Set a given block location to false, should not create stores if one does not exist for the given location
|
||||
*
|
||||
* @param block Block location to set
|
||||
*/
|
||||
void setFalse(Block block);
|
||||
|
||||
/**
|
||||
* Set a given BlockState location to false, should not create stores if one does not exist for the given location
|
||||
*
|
||||
* @param blockState BlockState location to set
|
||||
*/
|
||||
void setFalse(BlockState blockState);
|
||||
|
||||
/**
|
||||
* Delete any ChunkletStores that are empty
|
||||
*/
|
||||
void cleanUp();
|
||||
void chunkUnloaded(int cx, int cz, @NotNull World world);
|
||||
void unloadWorld(@NotNull World world);
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package com.gmail.nossr50.util.blockmeta;
|
||||
|
||||
import com.gmail.nossr50.config.HiddenConfig;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ChunkManagerFactory {
|
||||
public static ChunkManager getChunkManager() {
|
||||
public static @NotNull ChunkManager getChunkManager() {
|
||||
HiddenConfig hConfig = HiddenConfig.getInstance();
|
||||
|
||||
if (hConfig.getChunkletsEnabled()) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.gmail.nossr50.util.blockmeta;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@ -36,7 +36,7 @@ public interface ChunkStore {
|
||||
*/
|
||||
int getChunkZ();
|
||||
|
||||
UUID getWorldId();
|
||||
@NotNull UUID getWorldId();
|
||||
|
||||
/**
|
||||
* Checks the value at the given coordinates
|
||||
|
@ -1,12 +1,16 @@
|
||||
package com.gmail.nossr50.util.blockmeta;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class HashChunkManager implements ChunkManager {
|
||||
@ -21,7 +25,10 @@ public class HashChunkManager implements ChunkManager {
|
||||
{
|
||||
if (!chunkStore.isDirty())
|
||||
continue;
|
||||
writeChunkStore(Bukkit.getWorld(chunkStore.getWorldId()), chunkStore);
|
||||
World world = Bukkit.getWorld(chunkStore.getWorldId());
|
||||
if (world == null)
|
||||
continue; // Oh well
|
||||
writeChunkStore(world, chunkStore);
|
||||
}
|
||||
// Clear in memory chunks
|
||||
chunkMap.clear();
|
||||
@ -32,7 +39,7 @@ public class HashChunkManager implements ChunkManager {
|
||||
regionMap.clear();
|
||||
}
|
||||
|
||||
private synchronized ChunkStore readChunkStore(World world, int cx, int cz) throws IOException {
|
||||
private synchronized @Nullable ChunkStore readChunkStore(@NotNull World world, int cx, int cz) throws IOException {
|
||||
McMMOSimpleRegionFile rf = getSimpleRegionFile(world, cx, cz, false);
|
||||
if (rf == null)
|
||||
return null; // If there is no region file, there can't be a chunk
|
||||
@ -43,7 +50,7 @@ public class HashChunkManager implements ChunkManager {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void writeChunkStore(World world, ChunkStore data) {
|
||||
private synchronized void writeChunkStore(@NotNull World world, @NotNull ChunkStore data) {
|
||||
if (!data.isDirty())
|
||||
return; // Don't save unchanged data
|
||||
try {
|
||||
@ -58,7 +65,7 @@ public class HashChunkManager implements ChunkManager {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized McMMOSimpleRegionFile getSimpleRegionFile(World world, int cx, int cz, boolean createIfAbsent) {
|
||||
private synchronized @Nullable McMMOSimpleRegionFile getSimpleRegionFile(@NotNull World world, int cx, int cz, boolean createIfAbsent) {
|
||||
CoordinateKey regionKey = toRegionKey(world.getUID(), cx, cz);
|
||||
|
||||
return regionMap.computeIfAbsent(regionKey, k -> {
|
||||
@ -73,7 +80,7 @@ public class HashChunkManager implements ChunkManager {
|
||||
});
|
||||
}
|
||||
|
||||
private ChunkStore loadChunk(int cx, int cz, World world) {
|
||||
private @Nullable ChunkStore loadChunk(int cx, int cz, @NotNull World world) {
|
||||
try {
|
||||
return readChunkStore(world, cx, cz);
|
||||
}
|
||||
@ -82,7 +89,7 @@ public class HashChunkManager implements ChunkManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void unloadChunk(int cx, int cz, World world) {
|
||||
private void unloadChunk(int cx, int cz, @NotNull World world) {
|
||||
CoordinateKey chunkKey = toChunkKey(world.getUID(), cx, cz);
|
||||
ChunkStore chunkStore = chunkMap.remove(chunkKey); // Remove from chunk map
|
||||
if (chunkStore == null)
|
||||
@ -102,56 +109,12 @@ public class HashChunkManager implements ChunkManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveChunk(int cx, int cz, World world) {
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
CoordinateKey chunkKey = toChunkKey(world.getUID(), cx, cz);
|
||||
|
||||
ChunkStore out = chunkMap.get(chunkKey);
|
||||
|
||||
if (out == null)
|
||||
return;
|
||||
|
||||
if (!out.isDirty())
|
||||
return;
|
||||
|
||||
writeChunkStore(world, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void chunkUnloaded(int cx, int cz, World world) {
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
public synchronized void chunkUnloaded(int cx, int cz, @NotNull World world) {
|
||||
unloadChunk(cx, cz, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveWorld(World world) {
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
UUID wID = world.getUID();
|
||||
|
||||
// Save all teh chunks
|
||||
for (ChunkStore chunkStore : chunkMap.values()) {
|
||||
if (!chunkStore.isDirty())
|
||||
continue;
|
||||
if (!wID.equals(chunkStore.getWorldId()))
|
||||
continue;
|
||||
try {
|
||||
writeChunkStore(world, chunkStore);
|
||||
}
|
||||
catch (Exception ignore) { }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unloadWorld(World world) {
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
public synchronized void unloadWorld(@NotNull World world) {
|
||||
UUID wID = world.getUID();
|
||||
|
||||
// Save and remove all the chunks
|
||||
@ -177,18 +140,7 @@ public class HashChunkManager implements ChunkManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveAll() {
|
||||
for (World world : mcMMO.p.getServer().getWorlds()) {
|
||||
saveWorld(world);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isTrue(int x, int y, int z, World world) {
|
||||
if (world == null)
|
||||
return false;
|
||||
|
||||
private synchronized boolean isTrue(int x, int y, int z, @NotNull World world) {
|
||||
CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z);
|
||||
|
||||
// Get chunk, load from file if necessary
|
||||
@ -214,67 +166,36 @@ public class HashChunkManager implements ChunkManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isTrue(Block block) {
|
||||
if (block == null)
|
||||
return false;
|
||||
|
||||
public synchronized boolean isTrue(@NotNull Block block) {
|
||||
return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isTrue(BlockState blockState) {
|
||||
if (blockState == null)
|
||||
return false;
|
||||
|
||||
public synchronized boolean isTrue(@NotNull BlockState blockState) {
|
||||
return isTrue(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setTrue(int x, int y, int z, World world) {
|
||||
set(x, y, z, world, true);
|
||||
public synchronized void setTrue(@NotNull Block block) {
|
||||
set(block.getX(), block.getY(), block.getZ(), block.getWorld(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setTrue(Block block) {
|
||||
if (block == null)
|
||||
return;
|
||||
|
||||
setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
||||
public synchronized void setTrue(@NotNull BlockState blockState) {
|
||||
set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setTrue(BlockState blockState) {
|
||||
if (blockState == null)
|
||||
return;
|
||||
|
||||
setTrue(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld());
|
||||
public synchronized void setFalse(@NotNull Block block) {
|
||||
set(block.getX(), block.getY(), block.getZ(), block.getWorld(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setFalse(int x, int y, int z, World world) {
|
||||
set(x, y, z, world, false);
|
||||
public synchronized void setFalse(@NotNull BlockState blockState) {
|
||||
set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setFalse(Block block) {
|
||||
if (block == null)
|
||||
return;
|
||||
|
||||
setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setFalse(BlockState blockState) {
|
||||
if (blockState == null)
|
||||
return;
|
||||
|
||||
setFalse(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld());
|
||||
}
|
||||
|
||||
public synchronized void set(int x, int y, int z, World world, boolean value){
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
private synchronized void set(int x, int y, int z, @NotNull World world, boolean value){
|
||||
CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z);
|
||||
|
||||
// Get/Load/Create chunkstore
|
||||
@ -307,15 +228,15 @@ public class HashChunkManager implements ChunkManager {
|
||||
cStore.set(ix, y, iz, value);
|
||||
}
|
||||
|
||||
private CoordinateKey blockCoordinateToChunkKey(UUID worldUid, int x, int y, int z) {
|
||||
private CoordinateKey blockCoordinateToChunkKey(@NotNull UUID worldUid, int x, int y, int z) {
|
||||
return toChunkKey(worldUid, x >> 4, z >> 4);
|
||||
}
|
||||
|
||||
private CoordinateKey toChunkKey(UUID worldUid, int cx, int cz){
|
||||
private CoordinateKey toChunkKey(@NotNull UUID worldUid, int cx, int cz){
|
||||
return new CoordinateKey(worldUid, cx, cz);
|
||||
}
|
||||
|
||||
private CoordinateKey toRegionKey(UUID worldUid, int cx, int cz) {
|
||||
private CoordinateKey toRegionKey(@NotNull UUID worldUid, int cx, int cz) {
|
||||
// Compute region index (32x32 chunk regions)
|
||||
int rx = cx >> 5;
|
||||
int rz = cz >> 5;
|
||||
@ -323,11 +244,11 @@ public class HashChunkManager implements ChunkManager {
|
||||
}
|
||||
|
||||
private static final class CoordinateKey {
|
||||
public final UUID worldID;
|
||||
public final @NotNull UUID worldID;
|
||||
public final int x;
|
||||
public final int z;
|
||||
|
||||
private CoordinateKey(UUID worldID, int x, int z) {
|
||||
private CoordinateKey(@NotNull UUID worldID, int x, int z) {
|
||||
this.worldID = worldID;
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
@ -348,7 +269,4 @@ public class HashChunkManager implements ChunkManager {
|
||||
return Objects.hash(worldID, x, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void cleanUp() {}
|
||||
}
|
||||
|
@ -19,6 +19,9 @@
|
||||
*/
|
||||
package com.gmail.nossr50.util.blockmeta;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.BitSet;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
@ -54,7 +57,7 @@ public class McMMOSimpleRegionFile {
|
||||
private final int segmentMask;
|
||||
|
||||
// File location
|
||||
private final File parent;
|
||||
private final @NotNull File parent;
|
||||
// File access
|
||||
private final RandomAccessFile file;
|
||||
|
||||
@ -62,7 +65,7 @@ public class McMMOSimpleRegionFile {
|
||||
private final int rx;
|
||||
private final int rz;
|
||||
|
||||
public McMMOSimpleRegionFile(File f, int rx, int rz) {
|
||||
public McMMOSimpleRegionFile(@NotNull File f, int rx, int rz) {
|
||||
this.rx = rx;
|
||||
this.rz = rz;
|
||||
this.parent = f;
|
||||
@ -104,7 +107,7 @@ public class McMMOSimpleRegionFile {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized DataOutputStream getOutputStream(int x, int z) {
|
||||
public synchronized @NotNull DataOutputStream getOutputStream(int x, int z) {
|
||||
int index = getChunkIndex(x, z); // Get chunk index
|
||||
return new DataOutputStream(new DeflaterOutputStream(new McMMOSimpleChunkBuffer(this, index)));
|
||||
}
|
||||
@ -144,7 +147,7 @@ public class McMMOSimpleRegionFile {
|
||||
file.writeInt(chunkNumBytes[index]);
|
||||
}
|
||||
|
||||
public synchronized DataInputStream getInputStream(int x, int z) throws IOException {
|
||||
public synchronized @Nullable DataInputStream getInputStream(int x, int z) throws IOException {
|
||||
int index = getChunkIndex(x, z); // Get chunk index
|
||||
int byteLength = chunkNumBytes[index]; // Get byte length of data
|
||||
|
||||
|
@ -3,6 +3,7 @@ package com.gmail.nossr50.util.blockmeta;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullChunkManager implements ChunkManager {
|
||||
|
||||
@ -10,53 +11,30 @@ public class NullChunkManager implements ChunkManager {
|
||||
public void closeAll() {}
|
||||
|
||||
@Override
|
||||
public void saveChunk(int cx, int cz, World world) {}
|
||||
public void chunkUnloaded(int cx, int cz, @NotNull World world) {}
|
||||
|
||||
@Override
|
||||
public void chunkUnloaded(int cx, int cz, World world) {}
|
||||
public void unloadWorld(@NotNull World world) {}
|
||||
|
||||
@Override
|
||||
public void saveWorld(World world) {}
|
||||
|
||||
@Override
|
||||
public void unloadWorld(World world) {}
|
||||
|
||||
@Override
|
||||
public void saveAll() {}
|
||||
|
||||
@Override
|
||||
public boolean isTrue(int x, int y, int z, World world) {
|
||||
public boolean isTrue(@NotNull Block block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue(Block block) {
|
||||
public boolean isTrue(@NotNull BlockState blockState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue(BlockState blockState) {
|
||||
return false;
|
||||
}
|
||||
public void setTrue(@NotNull Block block) {}
|
||||
|
||||
@Override
|
||||
public void setTrue(int x, int y, int z, World world) {}
|
||||
public void setTrue(@NotNull BlockState blockState) {}
|
||||
|
||||
@Override
|
||||
public void setTrue(Block block) {}
|
||||
public void setFalse(@NotNull Block block) {}
|
||||
|
||||
@Override
|
||||
public void setTrue(BlockState blockState) {}
|
||||
|
||||
@Override
|
||||
public void setFalse(int x, int y, int z, World world) {}
|
||||
|
||||
@Override
|
||||
public void setFalse(Block block) {}
|
||||
|
||||
@Override
|
||||
public void setFalse(BlockState blockState) {}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {}
|
||||
public void setFalse(@NotNull BlockState blockState) {}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
package com.gmail.nossr50.util.blockmeta;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Contains blockstore methods that are safe for external plugins to access.
|
||||
* An instance can be retrieved via {@link mcMMO#getPlaceStore() mcMMO.getPlaceStore()}
|
||||
*/
|
||||
public interface UserBlockTracker {
|
||||
/**
|
||||
* Check to see if a given block location is set to true
|
||||
*
|
||||
* @param block Block location to check
|
||||
* @return true if the given block location is set to true, false if otherwise
|
||||
*/
|
||||
boolean isTrue(@NotNull Block block);
|
||||
|
||||
/**
|
||||
* Check to see if a given BlockState location is set to true
|
||||
*
|
||||
* @param blockState BlockState to check
|
||||
* @return true if the given BlockState location is set to true, false if otherwise
|
||||
*/
|
||||
boolean isTrue(@NotNull BlockState blockState);
|
||||
|
||||
/**
|
||||
* Set a given block location to true
|
||||
*
|
||||
* @param block Block location to set
|
||||
*/
|
||||
void setTrue(@NotNull Block block);
|
||||
|
||||
/**
|
||||
* Set a given BlockState location to true
|
||||
*
|
||||
* @param blockState BlockState location to set
|
||||
*/
|
||||
void setTrue(@NotNull BlockState blockState);
|
||||
|
||||
/**
|
||||
* Set a given block location to false
|
||||
*
|
||||
* @param block Block location to set
|
||||
*/
|
||||
void setFalse(@NotNull Block block);
|
||||
|
||||
/**
|
||||
* Set a given BlockState location to false
|
||||
*
|
||||
* @param blockState BlockState location to set
|
||||
*/
|
||||
void setFalse(@NotNull BlockState blockState);
|
||||
}
|
@ -2,8 +2,12 @@ package com.gmail.nossr50.util.skills;
|
||||
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.events.skills.SkillActivationPerkEvent;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -43,7 +47,9 @@ public final class PerksUtils {
|
||||
ticks += 4;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
final SkillActivationPerkEvent skillActivationPerkEvent = new SkillActivationPerkEvent(player, ticks, maxTicks);
|
||||
Bukkit.getPluginManager().callEvent(skillActivationPerkEvent);
|
||||
return skillActivationPerkEvent.getTicks();
|
||||
}
|
||||
|
||||
public static float handleXpPerks(Player player, float xp, PrimarySkillType skill) {
|
||||
|
@ -207,50 +207,6 @@ Fishing:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: LEGENDARY
|
||||
MUSIC_DISC_BLOCKS:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: UNCOMMON
|
||||
MUSIC_DISC_CHIRP:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: UNCOMMON
|
||||
MUSIC_DISC_FAR:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: RARE
|
||||
MUSIC_DISC_MALL:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: RARE
|
||||
MUSIC_DISC_MELLOHI:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: RARE
|
||||
MUSIC_DISC_STAL:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: EPIC
|
||||
MUSIC_DISC_STRAD:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: EPIC
|
||||
MUSIC_DISC_WARD:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: EPIC
|
||||
MUSIC_DISC_11:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: LEGENDARY
|
||||
MUSIC_DISC_WAIT:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: LEGENDARY
|
||||
MUSIC_DISC_13:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
Rarity: MYTHIC
|
||||
NETHERITE_SWORD:
|
||||
Amount: 1
|
||||
XP: 200
|
||||
|
@ -90,7 +90,6 @@ Fishing.SubSkill.Shake.Description=Vyklepni p\u0159edm\u011bty z p\u0159\u00ed\u
|
||||
Fishing.SubSkill.FishermansDiet.Name=Ryb\u00e1\u0159\u016fv apetit
|
||||
Fishing.SubSkill.FishermansDiet.Description=Zlep\u0161uje dopl\u0148ov\u00e1n\u00ed hladu z naryba\u0159en\u00fdch j\u00eddel
|
||||
Fishing.SubSkill.MasterAngler.Name=Mistr Ryb\u00e1\u0159
|
||||
Fishing.SubSkill.MasterAngler.Description=Zvy\u0161uje \u0161anci zah\u00e1knut\u00ed ryby p\u0159i ryba\u0159en\u00ed
|
||||
Fishing.SubSkill.IceFishing.Name=Ryba\u0159en\u00ed v ledu
|
||||
Fishing.SubSkill.IceFishing.Description=Umo\u017e\u0148uje v\u00e1m ryba\u0159it v ledov\u00fdch prost\u0159ed\u00edch
|
||||
Fishing.Chance.Raining=&9 De\u0161\u0165ov\u00fd bonus
|
||||
|
@ -372,7 +372,6 @@ Fishing.SubSkill.IceFishing.Stat = Eisangeln
|
||||
Fishing.SubSkill.MagicHunter.Description = Finde verzauberte Gegenst\u00E4nde
|
||||
Fishing.SubSkill.MagicHunter.Name = Zauber J\u00E4ger
|
||||
Fishing.SubSkill.MagicHunter.Stat = Zauber J\u00E4ger Chance
|
||||
Fishing.SubSkill.MasterAngler.Description = Erh\u00F6ht die Chance des Anbei\u00DFens beim Angeln
|
||||
Fishing.SubSkill.MasterAngler.Name = Superangel
|
||||
Fishing.SubSkill.Shake.Description = Rei\u00DFe Gegenst\u00E4nde weg von Lebewesen und Spielern mit deiner Angel
|
||||
Fishing.SubSkill.Shake.Name = Rei\u00DFen
|
||||
|
@ -258,7 +258,7 @@ Fishing.SubSkill.FishermansDiet.Name=Fisherman's Diet
|
||||
Fishing.SubSkill.FishermansDiet.Description=Improves hunger restored from fished foods
|
||||
Fishing.SubSkill.FishermansDiet.Stat=Fisherman's Diet:&a Rank {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=Master Angler
|
||||
Fishing.SubSkill.MasterAngler.Description=Fish are caught more frequently
|
||||
Fishing.SubSkill.MasterAngler.Description=Fish are caught more frequently, works better when fishing from a boat.
|
||||
Fishing.SubSkill.MasterAngler.Stat=Fishing min wait time reduction: &a-{0} seconds
|
||||
Fishing.SubSkill.MasterAngler.Stat.Extra=Fishing max wait time reduction: &a-{0} seconds
|
||||
Fishing.SubSkill.IceFishing.Name=Ice Fishing
|
||||
@ -493,6 +493,7 @@ Taming.Summon.COTW.Success.WithoutLifespan=&a(Call Of The Wild) &7You have summo
|
||||
Taming.Summon.COTW.Success.WithLifespan=&a(Call Of The Wild) &7You have summoned a &6{0}&7 and it has a duration of &6{1}&7 seconds.
|
||||
Taming.Summon.COTW.Limit=&a(Call Of The Wild) &7You can only have &c{0} &7summoned &7{1} pets at the same time.
|
||||
Taming.Summon.COTW.TimeExpired=&a(Call Of The Wild) &7Time is up, your &6{0}&7 departs.
|
||||
Taming.Summon.COTW.Removed=&a(Call Of The Wild) &7Your summoned &6{0}&7 has vanished from this world.
|
||||
Taming.Summon.COTW.BreedingDisallowed=&a(Call Of The Wild) &cYou cannot breed a summoned animal.
|
||||
Taming.Summon.COTW.NeedMoreItems=&a(Call Of The Wild) &7You need &e{0}&7 more &3{1}&7(s)
|
||||
Taming.Summon.Name.Format=&6(COTW) &f{0}'s {1}
|
||||
|
@ -91,7 +91,6 @@ Fishing.SubSkill.Shake.Description=Sacudir los items fuera de los monstruos con
|
||||
Fishing.SubSkill.FishermansDiet.Name=Dieta del pescador
|
||||
Fishing.SubSkill.FishermansDiet.Description=Mejora el hambre restaurada a partir de alimentos pescados
|
||||
Fishing.SubSkill.MasterAngler.Name=Maestro pescador
|
||||
Fishing.SubSkill.MasterAngler.Description=Aumenta la probabilidad de ser mordido mientras se pesca
|
||||
Fishing.SubSkill.IceFishing.Name=Pesca de hielo
|
||||
Fishing.SubSkill.IceFishing.Description=Te permite pescar en biomas de hielo
|
||||
Fishing.Chance.Raining=&9 Lluvia de Bonus
|
||||
|
@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=R\u00e9gime de fermier
|
||||
Fishing.SubSkill.FishermansDiet.Description=Am\u00e9liore la nutrition des produits de la ferme
|
||||
Fishing.SubSkill.FishermansDiet.Stat=R\u00e9gime de fermier:&a Grade {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=Ma\u00eetre P\u00eacheur
|
||||
Fishing.SubSkill.MasterAngler.Description=Augmente les chances que \u00e7a morde lors de la p\u00eache
|
||||
Fishing.SubSkill.IceFishing.Name=P\u00eache sur Glace
|
||||
Fishing.SubSkill.IceFishing.Description=Vous permet de p\u00eacher dans les biomes glac\u00e9s
|
||||
Fishing.SubSkill.IceFishing.Stat=P\u00eache sur Glace
|
||||
|
@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=Horg\u00E1szok Di\u00E9t\u00E1ja
|
||||
Fishing.SubSkill.FishermansDiet.Description=N\u00F6veli a kihal\u00E1szott \u00E9telek t\u00E1p\u00E9rt\u00E9k\u00E9t
|
||||
Fishing.SubSkill.FishermansDiet.Stat=Horg\u00E1szok Di\u00E9t\u00E1ja:&a Szint {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=Mester Horg\u00E1sz
|
||||
Fishing.SubSkill.MasterAngler.Description=N\u00F6veli a Kap\u00E1s es\u00E9ly\u00E9t horg\u00E1szat k\u00F6zben
|
||||
Fishing.SubSkill.IceFishing.Name=J\u00E9g Horg\u00E1szat
|
||||
Fishing.SubSkill.IceFishing.Description=Lehet\u0151v\u00E9 teszi sz\u00E1modra, hogy fagyos t\u00E1jakon is horg\u00E1szhass
|
||||
Fishing.SubSkill.IceFishing.Stat=J\u00E9g Horg\u00E1szat
|
||||
|
@ -258,7 +258,6 @@ Fishing.SubSkill.FishermansDiet.Name=Dieta del Pescatore
|
||||
Fishing.SubSkill.FishermansDiet.Description=Aumenta la fame recuperata tramite cibi pescati
|
||||
Fishing.SubSkill.FishermansDiet.Stat=Dieta del Pescatore:&a Grado {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=Pescatore Provetto
|
||||
Fishing.SubSkill.MasterAngler.Description=Migliora la possibilit\u00E0 di ottenere un morso durante la pesca
|
||||
Fishing.SubSkill.IceFishing.Name=Pesca sul Ghiaccio
|
||||
Fishing.SubSkill.IceFishing.Description=Ti permette di pescare in biomi ghiacciati
|
||||
Fishing.SubSkill.IceFishing.Stat=Pesca sul Ghiaccio
|
||||
|
@ -241,7 +241,6 @@ Fishing.SubSkill.FishermansDiet.Name=\u6f01\u5e2b\u306e\u98df\u4e8b
|
||||
Fishing.SubSkill.FishermansDiet.Description=\u9b5a\u4ecb\u985e\u304b\u3089\u56de\u5fa9\u3059\u308b\u6e80\u8179\u5ea6\u3092\u6539\u5584\u3059\u308b\u3002
|
||||
Fishing.SubSkill.FishermansDiet.Stat=\u6f01\u5e2b\u306e\u98df\u4e8b:&a \u30e9\u30f3\u30af {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=\u30de\u30b9\u30bf\u30fc\u30a2\u30f3\u30b0\u30e9\u30fc
|
||||
Fishing.SubSkill.MasterAngler.Description=\u91e3\u308c\u308b\u78ba\u7387\u304c\u4e0a\u304c\u308a\u307e\u3059\u3002
|
||||
Fishing.SubSkill.IceFishing.Name=\u7a74\u91e3\u308a
|
||||
Fishing.SubSkill.IceFishing.Description=\u5bd2\u3044\u30d0\u30a4\u30aa\u30fc\u30e0\u3067\u306e\u91e3\u308a\u304c\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308b\u3002
|
||||
Fishing.SubSkill.IceFishing.Stat=\u7a74\u91e3\u308a
|
||||
|
@ -132,7 +132,6 @@ Fishing.SubSkill.Shake.Description=\uC544\uC774\uD15C\uC744 \uBAB9\uC774\uB098 \
|
||||
Fishing.SubSkill.FishermansDiet.Name=\uC5B4\uBD80\uC758 \uB2E4\uC774\uC5B4\uD2B8
|
||||
Fishing.SubSkill.FishermansDiet.Description=\uC5B4\uB958 \uC74C\uC2DD \uD5C8\uAE30 \uD68C\uBCF5 \uC99D\uAC00
|
||||
Fishing.SubSkill.MasterAngler.Name=\uB09A\uC2DC\uAFBC \uC7A5\uC778
|
||||
Fishing.SubSkill.MasterAngler.Description=\uB09A\uC2DC\uC911 \uC785\uC9C8 \uD655\uB960 \uC99D\uAC00
|
||||
Fishing.SubSkill.IceFishing.Name=\uC5BC\uC74C \uB09A\uC2DC
|
||||
Fishing.SubSkill.IceFishing.Description=\uC5BC\uC74C\uC774 \uB36E\uD600\uC788\uB294 \uD658\uACBD\uC5D0\uC11C \uB09A\uC2DC \uAC00\uB2A5
|
||||
Fishing.Chance.Raining=&9 \uBE44 \uD2B9\uD61C
|
||||
|
@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=Fisherman's Diet
|
||||
Fishing.SubSkill.FishermansDiet.Description=Improves hunger restored from fished foods
|
||||
Fishing.SubSkill.FishermansDiet.Stat=Fisherman's Diet:&a Rank {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=Master Angler
|
||||
Fishing.SubSkill.MasterAngler.Description=Improves chance of getting a bite while fishing
|
||||
Fishing.SubSkill.IceFishing.Name=Ice Fishing
|
||||
Fishing.SubSkill.IceFishing.Description=Allows you to fish in icy biomes
|
||||
Fishing.SubSkill.IceFishing.Stat=Ice Fishing
|
||||
|
@ -71,7 +71,6 @@ Fishing.SubSkill.Shake.Description=Schud items af van mobs w/ hengel
|
||||
Fishing.SubSkill.FishermansDiet.Name=Visserman\'s dieet
|
||||
Fishing.SubSkill.FishermansDiet.Description=Verbetert de honger hersteld vanaf geviste voedingsmiddelen
|
||||
Fishing.SubSkill.MasterAngler.Name=Meester Hengelaar
|
||||
Fishing.SubSkill.MasterAngler.Description=Verbetert de kans op het bijten tijdens het vissen
|
||||
Fishing.SubSkill.IceFishing.Name=Ijs Vissen
|
||||
Fishing.SubSkill.IceFishing.Description=Stelt je in staat om te vissen in de ijzige biomen
|
||||
Fishing.Chance.Raining=&9 Regen Bonus
|
||||
|
@ -89,7 +89,6 @@ Fishing.SubSkill.Shake.Name=Potrz\u0105\u015bni\u0119cie (przeciwko jednostkom)
|
||||
Fishing.SubSkill.Shake.Description=Okradaj potwory z przedmiot\u00f3w u\u017cywaj\u0105c w\u0119dki.
|
||||
Fishing.SubSkill.FishermansDiet.Name=Dieta Rybaka
|
||||
Fishing.SubSkill.FishermansDiet.Description=Zwi\u0119ksza nasycenie posi\u0142k\u00f3w (ryby)
|
||||
Fishing.SubSkill.MasterAngler.Description=Zwieksza szanse na zlapanie ryby na haczyk
|
||||
Fishing.SubSkill.IceFishing.Name=Lodowe lowienie ryb
|
||||
Fishing.SubSkill.IceFishing.Description=Pozwala na lowienie ryb w zimowych biomach
|
||||
Fishing.Chance.Raining=&9 Bonus od Deszczu
|
||||
|
@ -199,7 +199,6 @@ Fishing.SubSkill.FishermansDiet.Name=\u0420\u044b\u0431\u0430\u0446\u043a\u0430\
|
||||
Fishing.SubSkill.FishermansDiet.Description=\u0423\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0443\u0442\u043e\u043b\u0435\u043d\u0438\u0435 \u0433\u043e\u043b\u043e\u0434\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u044b\u0431\u0430\u0446\u043a\u043e\u0439 \u0435\u0434\u044b
|
||||
Fishing.SubSkill.FishermansDiet.Stat=\u0420\u044b\u0431\u0430\u0446\u043a\u0430\u044f \u0414\u0438\u0435\u0442\u0430:&a \u0420\u0430\u043d\u0433 {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=\u041c\u0430\u0441\u0442\u0435\u0440 \u0420\u044b\u0431\u043e\u043b\u043e\u0432
|
||||
Fishing.SubSkill.MasterAngler.Description=\u0423\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0448\u0430\u043d\u0441 \u043f\u043e\u043a\u043b\u0435\u0432\u043a\u0438 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u044b\u0431\u0430\u043b\u043a\u0438
|
||||
Fishing.SubSkill.IceFishing.Name=\u041f\u043e\u0434\u043b\u0435\u0434\u043d\u0430\u044f \u0420\u044b\u0431\u0430\u043b\u043a\u0430
|
||||
Fishing.SubSkill.IceFishing.Description=\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u0430\u043c \u0440\u044b\u0431\u0430\u0447\u0438\u0442\u044c \u0432 \u0441\u043d\u0435\u0436\u043d\u044b\u0445 \u0431\u0438\u043e\u043c\u0430\u0445
|
||||
Fishing.SubSkill.IceFishing.Stat=\u041f\u043e\u0434\u043b\u0435\u0434\u043d\u0430\u044f \u0420\u044b\u0431\u0430\u043b\u043a\u0430
|
||||
|
@ -90,7 +90,6 @@ Fishing.SubSkill.Shake.Description=\u0e40\u0e02\u0e22\u0e48\u0e32\u0e40\u0e2d\u0
|
||||
Fishing.SubSkill.FishermansDiet.Name=Fisherman\'s Diet
|
||||
Fishing.SubSkill.FishermansDiet.Description=\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e04\u0e27\u0e32\u0e21\u0e2d\u0e34\u0e48\u0e21\u0e08\u0e32\u0e01\u0e01\u0e32\u0e23\u0e01\u0e34\u0e19\u0e1b\u0e25\u0e32
|
||||
Fishing.SubSkill.MasterAngler.Name=Master Angler
|
||||
Fishing.SubSkill.MasterAngler.Description=\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e42\u0e2d\u0e01\u0e32\u0e2a\u0e44\u0e14\u0e49\u0e23\u0e31\u0e1a\u0e1b\u0e25\u0e32\u0e21\u0e32\u0e01\u0e02\u0e36\u0e49\u0e19\u0e08\u0e32\u0e01\u0e01\u0e32\u0e23\u0e15\u0e01\u0e1b\u0e25\u0e32
|
||||
Fishing.SubSkill.IceFishing.Name=Ice Fishing
|
||||
Fishing.SubSkill.IceFishing.Description=\u0e2d\u0e19\u0e38\u0e0d\u0e32\u0e15\u0e34\u0e43\u0e2b\u0e49\u0e15\u0e01\u0e1b\u0e25\u0e32\u0e43\u0e19\u0e19\u0e49\u0e33\u0e41\u0e02\u0e47\u0e07
|
||||
Fishing.Chance.Raining=&9 Rain Bonus
|
||||
|
@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=\u6e14\u592b\u7684\u98df\u8c31
|
||||
Fishing.SubSkill.FishermansDiet.Description=\u63d0\u9ad8\u9c7c\u7c7b\u98df\u7269\u6062\u590d\u7684\u9971\u98df\u5ea6
|
||||
Fishing.SubSkill.FishermansDiet.Stat=\u6e14\u592b\u7684\u98df\u8c31:&a \u7b49\u7ea7 {0}
|
||||
Fishing.SubSkill.MasterAngler.Name=\u9493\u9c7c\u5927\u5e08
|
||||
Fishing.SubSkill.MasterAngler.Description=\u63d0\u9ad8\u9493\u9c7c\u54ac\u94a9\u51e0\u7387
|
||||
Fishing.SubSkill.IceFishing.Name=\u51b0\u9493
|
||||
Fishing.SubSkill.IceFishing.Description=\u5141\u8bb8\u4f60\u5728\u51b0\u51b7\u7684\u73af\u5883\u4e0b\u9493\u9c7c
|
||||
Fishing.SubSkill.IceFishing.Stat=\u51b0\u9493
|
||||
|
@ -93,7 +93,6 @@ Fishing.SubSkill.Shake.Description=\u7528\u91e3\u7aff\u628a\u602a\u7269\u7684\u7
|
||||
Fishing.SubSkill.FishermansDiet.Name=\u6f01\u4eba\u4fbf\u7576
|
||||
Fishing.SubSkill.FishermansDiet.Description=\u98df\u7528\u9b5a\u98df\u54c1\u6642\u984d\u5916\u6062\u5fa9\u98fd\u98df\u5ea6
|
||||
Fishing.SubSkill.MasterAngler.Name=\u5782\u91e3\u5927\u5e2b
|
||||
Fishing.SubSkill.MasterAngler.Description=\u589e\u52a0\u5728\u91e3\u9b5a\u6642\u4e0a\u9264\u7684\u6a5f\u7387
|
||||
Fishing.SubSkill.IceFishing.Name=\u51b0\u91e3
|
||||
Fishing.SubSkill.IceFishing.Description=\u5141\u8a31\u4f60\u5728\u51b0\u5929\u96ea\u5730\u88e1\u91e3\u9b5a
|
||||
Fishing.Chance.Raining=&9 \u5927\u91cf\u734e\u52f5
|
||||
|
@ -2,6 +2,9 @@ import com.gmail.nossr50.util.blockmeta.*;
|
||||
import com.google.common.io.Files;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
@ -140,16 +143,27 @@ public class ChunkStoreTest {
|
||||
@Test
|
||||
public void testRegressionChunkMirrorBug() {
|
||||
ChunkManager chunkManager = new HashChunkManager();
|
||||
chunkManager.setTrue(15,0,15, mockWorld);
|
||||
chunkManager.setFalse(-15, 0, -15, mockWorld);
|
||||
Assert.assertTrue(chunkManager.isTrue(15, 0, 15, mockWorld));
|
||||
Block mockBlockA = mock(Block.class);
|
||||
Mockito.when(mockBlockA.getX()).thenReturn(15);
|
||||
Mockito.when(mockBlockA.getZ()).thenReturn(15);
|
||||
Mockito.when(mockBlockA.getY()).thenReturn(0);
|
||||
Mockito.when(mockBlockA.getWorld()).thenReturn(mockWorld);
|
||||
Block mockBlockB = mock(Block.class);
|
||||
Mockito.when(mockBlockB.getX()).thenReturn(-15);
|
||||
Mockito.when(mockBlockB.getZ()).thenReturn(-15);
|
||||
Mockito.when(mockBlockB.getY()).thenReturn(0);
|
||||
Mockito.when(mockBlockB.getWorld()).thenReturn(mockWorld);
|
||||
|
||||
chunkManager.setTrue(mockBlockA);
|
||||
chunkManager.setFalse(mockBlockB);
|
||||
Assert.assertTrue(chunkManager.isTrue(mockBlockA));
|
||||
}
|
||||
|
||||
private interface Delegate {
|
||||
void run();
|
||||
}
|
||||
|
||||
private void assertThrows(Delegate delegate, Class<?> clazz) {
|
||||
private void assertThrows(@NotNull Delegate delegate, @NotNull Class<?> clazz) {
|
||||
try {
|
||||
delegate.run();
|
||||
Assert.fail(); // We didn't throw
|
||||
@ -170,7 +184,7 @@ public class ChunkStoreTest {
|
||||
Assert.assertTrue(expected.isTrue(x, y, z) == actual.isTrue(x, y, z));
|
||||
}
|
||||
|
||||
private static void recursiveDelete(File directoryToBeDeleted) {
|
||||
private static void recursiveDelete(@NotNull File directoryToBeDeleted) {
|
||||
if (directoryToBeDeleted.isDirectory()) {
|
||||
for (File file : directoryToBeDeleted.listFiles()) {
|
||||
recursiveDelete(file);
|
||||
@ -179,7 +193,7 @@ public class ChunkStoreTest {
|
||||
directoryToBeDeleted.delete();
|
||||
}
|
||||
|
||||
private static byte[] serializeChunkstore(ChunkStore chunkStore) throws IOException {
|
||||
private static byte[] serializeChunkstore(@NotNull ChunkStore chunkStore) throws IOException {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
if (chunkStore instanceof BitSetChunkStore)
|
||||
BitSetChunkStore.Serialization.writeChunkStore(new DataOutputStream(byteArrayOutputStream), chunkStore);
|
||||
@ -188,18 +202,17 @@ public class ChunkStoreTest {
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static class LegacyChunkStore implements ChunkStore, Serializable {
|
||||
private static final long serialVersionUID = -1L;
|
||||
transient private boolean dirty = false;
|
||||
public boolean[][][] store;
|
||||
private static final int CURRENT_VERSION = 7;
|
||||
private static final int MAGIC_NUMBER = 0xEA5EDEBB;
|
||||
private int cx;
|
||||
private int cz;
|
||||
private UUID worldUid;
|
||||
private final int cx;
|
||||
private final int cz;
|
||||
private final @NotNull UUID worldUid;
|
||||
|
||||
public LegacyChunkStore(World world, int cx, int cz) {
|
||||
public LegacyChunkStore(@NotNull World world, int cx, int cz) {
|
||||
this.cx = cx;
|
||||
this.cz = cz;
|
||||
this.worldUid = world.getUID();
|
||||
@ -227,7 +240,7 @@ public class ChunkStoreTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getWorldId() {
|
||||
public @NotNull UUID getWorldId() {
|
||||
return worldUid;
|
||||
}
|
||||
|
||||
@ -274,7 +287,7 @@ public class ChunkStoreTest {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
private void writeObject(@NotNull ObjectOutputStream out) throws IOException {
|
||||
out.writeInt(MAGIC_NUMBER);
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
|
||||
@ -287,18 +300,18 @@ public class ChunkStoreTest {
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
private void readObject(@NotNull ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UnitTestObjectOutputStream extends ObjectOutputStream {
|
||||
public UnitTestObjectOutputStream(OutputStream outputStream) throws IOException {
|
||||
public UnitTestObjectOutputStream(@NotNull OutputStream outputStream) throws IOException {
|
||||
super(outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUTF(String str) throws IOException {
|
||||
public void writeUTF(@NotNull String str) throws IOException {
|
||||
// Pretend to be the old class
|
||||
if (str.equals(LegacyChunkStore.class.getName()))
|
||||
str = "com.gmail.nossr50.util.blockmeta.chunkmeta.PrimitiveChunkStore";
|
||||
|
Loading…
Reference in New Issue
Block a user