mirror of
				https://github.com/mcMMO-Dev/mcMMO.git
				synced 2025-11-04 02:53:43 +01:00 
			
		
		
		
	Merge branch 'master' of github.com:mcMMO-Dev/mcMMO into tridentsxbows
This commit is contained in:
		
							
								
								
									
										51
									
								
								.github/workflows/maven.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								.github/workflows/maven.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
# This workflow automatically tests new commits and pull requests as they come in.
 | 
			
		||||
# Note that this does not upload any artifacts, you will need to compile mcMMO manually
 | 
			
		||||
# if you wish to create the actual jar.
 | 
			
		||||
name: Compile and test
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  # We run our tests whenever the pom or a source file was touched.
 | 
			
		||||
  # There is no need to run Maven when only the changelog was touched.
 | 
			
		||||
  # We may also want to re-run this workflow when the workflow file itself
 | 
			
		||||
  # was updated too.
 | 
			
		||||
  push:
 | 
			
		||||
    paths:
 | 
			
		||||
    - 'src/**'
 | 
			
		||||
    - 'pom.xml'
 | 
			
		||||
    - '.github/workflows/maven.yml'
 | 
			
		||||
 | 
			
		||||
  # Whenever someone submits a new pull request which modified the pom or a source file,
 | 
			
		||||
  # we want to ensure it compiles successfully and that all tests will pass.
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths:
 | 
			
		||||
    - 'src/**'
 | 
			
		||||
    - 'pom.xml'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  compile:
 | 
			
		||||
    name: Maven compiler
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
 | 
			
		||||
    # 1. Check out the current working tree
 | 
			
		||||
    - name: Checkout repository
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
    # 2. Setup Java 1.8 JDK
 | 
			
		||||
    - name: Java 1.8 setup
 | 
			
		||||
      uses: actions/setup-java@v1.4.3
 | 
			
		||||
      with:
 | 
			
		||||
        java-package: jdk
 | 
			
		||||
        java-version: 1.8
 | 
			
		||||
 | 
			
		||||
    # 3. Setup local Maven package cache to speed up building
 | 
			
		||||
    - name: Cache Maven packages
 | 
			
		||||
      uses: actions/cache@v2
 | 
			
		||||
      with:
 | 
			
		||||
        path: ~/.m2
 | 
			
		||||
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
 | 
			
		||||
        restore-keys: ${{ runner.os }}-m2
 | 
			
		||||
 | 
			
		||||
    # 4. Build via Maven 
 | 
			
		||||
    - name: Build via Maven
 | 
			
		||||
      run: mvn verify -B --file pom.xml
 | 
			
		||||
@@ -103,7 +103,15 @@ 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.176
 | 
			
		||||
    Added another measure to prevent item stacks from reaching 65 from double smelt
 | 
			
		||||
 | 
			
		||||
Version 2.1.175
 | 
			
		||||
    Fixed a bug where mcMMO would occasionally give a 65 item stack from a double smelt on a furnace
 | 
			
		||||
    Fixed a bug where arrows could be duped when fired from a crossbow with piercing enchantment
 | 
			
		||||
    Added setting to enable or disable Green Thumb automatically replanting crops per crop to config.yml under 'Green_Thumb_Replanting_Crops' section
 | 
			
		||||
    Updated Adventure (our text dependency) fixes some errors when using color codes in party/admin chat (thanks TheBusyBiscuit)
 | 
			
		||||
    Added some support for negative Y values in anticipation of 1.17 world height changes (thanks t00thpick1)
 | 
			
		||||
 | 
			
		||||
Version 2.1.174
 | 
			
		||||
    Some legacy color codes in our locale file were swapped to &-code equivalents (thanks ViaSnake)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								pom.xml
									
									
									
									
									
								
							@@ -101,6 +101,7 @@
 | 
			
		||||
                            <include>commons-logging:commons-logging</include>
 | 
			
		||||
                            <include>org.apache.tomcat:tomcat-jdbc</include>
 | 
			
		||||
                            <include>org.apache.tomcat:tomcat-juli</include>
 | 
			
		||||
                            <include>org.bstats:bstats-base</include>
 | 
			
		||||
                            <include>org.bstats:bstats-bukkit</include>
 | 
			
		||||
                            <include>net.kyori:adventure-api</include>
 | 
			
		||||
                            <include>net.kyori:adventure-text-serializer-gson</include>
 | 
			
		||||
@@ -235,27 +236,27 @@
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>net.kyori</groupId>
 | 
			
		||||
            <artifactId>adventure-text-serializer-gson</artifactId>
 | 
			
		||||
            <version>4.4.0</version>
 | 
			
		||||
            <version>4.5.1</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>net.kyori</groupId>
 | 
			
		||||
            <artifactId>adventure-api</artifactId>
 | 
			
		||||
            <version>4.4.0</version>
 | 
			
		||||
            <version>4.5.1</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>net.kyori</groupId>
 | 
			
		||||
            <artifactId>adventure-nbt</artifactId>
 | 
			
		||||
            <version>4.4.0</version>
 | 
			
		||||
            <version>4.5.1</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>net.kyori</groupId>
 | 
			
		||||
            <artifactId>adventure-key</artifactId>
 | 
			
		||||
            <version>4.4.0</version>
 | 
			
		||||
            <version>4.5.1</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>net.kyori</groupId>
 | 
			
		||||
            <artifactId>adventure-text-serializer-gson-legacy-impl</artifactId>
 | 
			
		||||
            <version>4.4.0</version>
 | 
			
		||||
            <version>4.5.1</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>net.kyori</groupId>
 | 
			
		||||
@@ -280,13 +281,13 @@
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.bstats</groupId>
 | 
			
		||||
            <artifactId>bstats-bukkit</artifactId>
 | 
			
		||||
            <version>1.8</version>
 | 
			
		||||
            <version>2.2.1</version>
 | 
			
		||||
            <scope>compile</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.spigotmc</groupId>
 | 
			
		||||
            <artifactId>spigot-api</artifactId>
 | 
			
		||||
            <version>1.16.4-R0.1-SNAPSHOT</version>
 | 
			
		||||
            <version>1.16.5-R0.1-SNAPSHOT</version>
 | 
			
		||||
            <scope>provided</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
@@ -334,7 +335,7 @@
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mockito</groupId>
 | 
			
		||||
            <artifactId>mockito-core</artifactId>
 | 
			
		||||
            <version>3.4.6</version>
 | 
			
		||||
            <version>3.8.0</version>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
 
 | 
			
		||||
@@ -612,4 +612,7 @@ public class Config extends AutoUpdateConfigLoader {
 | 
			
		||||
    public int getPowerLevelUpBroadcastRadius() { return config.getInt("General.Level_Up_Chat_Broadcasts.Broadcast_Powerlevels.Broadcast_Targets.Distance_Restrictions.Restricted_Radius", 100); }
 | 
			
		||||
    public int getPowerLevelUpBroadcastInterval() { return config.getInt("General.Level_Up_Chat_Broadcasts.Broadcast_Powerlevels.Milestone_Interval", 100); }
 | 
			
		||||
 | 
			
		||||
    public boolean isGreenThumbReplantableCrop(@NotNull Material material) {
 | 
			
		||||
        return config.getBoolean("Green_Thumb_Replanting_Crops." + StringUtils.getCapitalized(material.toString()), true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -147,10 +147,13 @@ public class BlockListener implements Listener {
 | 
			
		||||
        // Get opposite direction so we get correct block
 | 
			
		||||
        BlockFace direction = event.getDirection();
 | 
			
		||||
        Block movedBlock = event.getBlock().getRelative(direction);
 | 
			
		||||
        if (movedBlock.getY() >= Misc.getWorldMinCompat(movedBlock.getWorld())) // Very weird that the event is giving us these, they shouldn't exist
 | 
			
		||||
            mcMMO.getPlaceStore().setTrue(movedBlock);
 | 
			
		||||
 | 
			
		||||
        for (Block block : event.getBlocks()) {
 | 
			
		||||
            movedBlock = block.getRelative(direction);
 | 
			
		||||
            if (movedBlock.getY() < Misc.getWorldMinCompat(movedBlock.getWorld())) // Very weird that the event is giving us these, they shouldn't exist
 | 
			
		||||
                continue;
 | 
			
		||||
            mcMMO.getPlaceStore().setTrue(movedBlock);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -207,14 +207,10 @@ public class EntityListener implements Listener {
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                //Bow only
 | 
			
		||||
                if (!isCrossbow) {
 | 
			
		||||
                    for (Enchantment enchantment : player.getInventory().getItemInMainHand().getEnchantments().keySet()) {
 | 
			
		||||
                        if (enchantment.getKey().equals(piercingEnchantment)) {
 | 
			
		||||
                //Check both hands
 | 
			
		||||
                if(ItemUtils.doesPlayerHaveEnchantmentInHands(player, "piercing")) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.ARCHERY_ARROW_RETRIEVAL, player)) {
 | 
			
		||||
                        projectile.setMetadata(mcMMO.trackedArrow, mcMMO.metadataValue);
 | 
			
		||||
 
 | 
			
		||||
@@ -115,7 +115,8 @@ public class InventoryListener implements Listener {
 | 
			
		||||
 | 
			
		||||
                //Profile doesn't exist
 | 
			
		||||
                if(offlineProfile != null) {
 | 
			
		||||
                    event.setResult(offlineProfile.getSmeltingManager().smeltProcessing(smelting, event.getResult()));
 | 
			
		||||
                    //Process smelting
 | 
			
		||||
                    offlineProfile.getSmeltingManager().smeltProcessing(event, furnace);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,7 @@ import com.neetgames.mcmmo.party.PartyManager;
 | 
			
		||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
 | 
			
		||||
import net.shatteredlands.shatt.backup.ZipLibrary;
 | 
			
		||||
import org.bstats.bukkit.Metrics;
 | 
			
		||||
import org.bstats.charts.SimplePie;
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.entity.Player;
 | 
			
		||||
import org.bukkit.event.HandlerList;
 | 
			
		||||
@@ -68,6 +69,7 @@ import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.InputStreamReader;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -129,24 +131,24 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
    private static boolean isRetroModeEnabled;
 | 
			
		||||
 | 
			
		||||
    /* Metadata Values */
 | 
			
		||||
    public final static String REPLANT_META_KEY = "mcMMO: Recently Replanted";
 | 
			
		||||
    public static final String REPLANT_META_KEY      = "mcMMO: Recently Replanted";
 | 
			
		||||
    public static final String FISH_HOOK_REF_METAKEY = "mcMMO: Fish Hook Tracker";
 | 
			
		||||
    public static final String DODGE_TRACKER         = "mcMMO: Dodge Tracker";
 | 
			
		||||
    public static final String CUSTOM_DAMAGE_METAKEY = "mcMMO: Custom Damage";
 | 
			
		||||
    public final static String travelingBlock      = "mcMMO: Traveling Block";
 | 
			
		||||
    public final static String blockMetadataKey    = "mcMMO: Piston Tracking";
 | 
			
		||||
    public final static String tntMetadataKey      = "mcMMO: Tracked TNT";
 | 
			
		||||
    public final static String customNameKey       = "mcMMO: Custom Name";
 | 
			
		||||
    public final static String customVisibleKey    = "mcMMO: Name Visibility";
 | 
			
		||||
    public final static String droppedItemKey      = "mcMMO: Tracked Item";
 | 
			
		||||
    public final static String infiniteArrowKey    = "mcMMO: Infinite Arrow";
 | 
			
		||||
    public final static String trackedArrow        = "mcMMO: Tracked Arrow";
 | 
			
		||||
    public final static String bowForceKey         = "mcMMO: Bow Force";
 | 
			
		||||
    public final static String arrowDistanceKey    = "mcMMO: Arrow Distance";
 | 
			
		||||
    public final static String BONUS_DROPS_METAKEY = "mcMMO: Double Drops";
 | 
			
		||||
    public final static String disarmedItemKey     = "mcMMO: Disarmed Item";
 | 
			
		||||
    public final static String playerDataKey       = "mcMMO: Player Data";
 | 
			
		||||
    public final static String databaseCommandKey  = "mcMMO: Processing Database Command";
 | 
			
		||||
    public static final String travelingBlock        = "mcMMO: Traveling Block";
 | 
			
		||||
    public static final String blockMetadataKey      = "mcMMO: Piston Tracking";
 | 
			
		||||
    public static final String tntMetadataKey        = "mcMMO: Tracked TNT";
 | 
			
		||||
    public static final String customNameKey         = "mcMMO: Custom Name";
 | 
			
		||||
    public static final String customVisibleKey      = "mcMMO: Name Visibility";
 | 
			
		||||
    public static final String droppedItemKey        = "mcMMO: Tracked Item";
 | 
			
		||||
    public static final String infiniteArrowKey      = "mcMMO: Infinite Arrow";
 | 
			
		||||
    public static final String trackedArrow          = "mcMMO: Tracked Arrow";
 | 
			
		||||
    public static final String bowForceKey           = "mcMMO: Bow Force";
 | 
			
		||||
    public static final String arrowDistanceKey      = "mcMMO: Arrow Distance";
 | 
			
		||||
    public static final String BONUS_DROPS_METAKEY   = "mcMMO: Double Drops";
 | 
			
		||||
    public static final String disarmedItemKey       = "mcMMO: Disarmed Item";
 | 
			
		||||
    public static final String playerDataKey         = "mcMMO: Player Data";
 | 
			
		||||
    public static final String databaseCommandKey    = "mcMMO: Processing Database Command";
 | 
			
		||||
    public final static String bredMetadataKey     = "mcMMO: Bred Animal";
 | 
			
		||||
    public final static String PROJECTILE_ORIGIN_METAKEY = "mcMMO: Projectile Origin";
 | 
			
		||||
 | 
			
		||||
@@ -168,7 +170,9 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
            //Platform Manager
 | 
			
		||||
            platformManager = new PlatformManager();
 | 
			
		||||
 | 
			
		||||
            //Filter out any debug messages (if debug/verbose logging is not enabled)
 | 
			
		||||
            getLogger().setFilter(new LogFilter(this));
 | 
			
		||||
 | 
			
		||||
            metadataValue = new FixedMetadataValue(this, true);
 | 
			
		||||
 | 
			
		||||
            PluginManager pluginManager = getServer().getPluginManager();
 | 
			
		||||
@@ -260,12 +264,12 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
 | 
			
		||||
            if(Config.getInstance().getIsMetricsEnabled()) {
 | 
			
		||||
                metrics = new Metrics(this, 3894);
 | 
			
		||||
                metrics.addCustomChart(new Metrics.SimplePie("version", () -> getDescription().getVersion()));
 | 
			
		||||
                metrics.addCustomChart(new SimplePie("version", () -> getDescription().getVersion()));
 | 
			
		||||
 | 
			
		||||
                if(Config.getInstance().getIsRetroMode())
 | 
			
		||||
                    metrics.addCustomChart(new Metrics.SimplePie("leveling_system", () -> "Retro"));
 | 
			
		||||
                    metrics.addCustomChart(new SimplePie("leveling_system", () -> "Retro"));
 | 
			
		||||
                else
 | 
			
		||||
                    metrics.addCustomChart(new Metrics.SimplePie("leveling_system", () -> "Standard"));
 | 
			
		||||
                    metrics.addCustomChart(new SimplePie("leveling_system", () -> "Standard"));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Throwable t) {
 | 
			
		||||
@@ -279,6 +283,9 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            getServer().getPluginManager().disablePlugin(this);
 | 
			
		||||
 | 
			
		||||
            //Fixes #4438 - Don't initialize things if we are going to disable mcMMO anyway
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Init player level values
 | 
			
		||||
@@ -295,6 +302,8 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
 | 
			
		||||
        //Init Player Data Manager
 | 
			
		||||
        userManager = new UserManager();
 | 
			
		||||
 | 
			
		||||
        //Set up Adventure's audiences
 | 
			
		||||
        audiences = BukkitAudiences.create(this);
 | 
			
		||||
 | 
			
		||||
        transientMetadataTools = new TransientMetadataTools(this);
 | 
			
		||||
@@ -358,8 +367,9 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
            holidayManager.saveAnniversaryFiles();
 | 
			
		||||
            placeStore.closeAll();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        catch (Exception e) { e.printStackTrace(); }
 | 
			
		||||
        catch (Exception e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Config.getInstance().getBackupsEnabled()) {
 | 
			
		||||
            // Remove other tasks BEFORE starting the Backup, or we just cancel it straight away.
 | 
			
		||||
@@ -369,16 +379,14 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
            catch (IOException e) {
 | 
			
		||||
                getLogger().severe(e.toString());
 | 
			
		||||
            }
 | 
			
		||||
            catch (Throwable e) {
 | 
			
		||||
                if (e instanceof NoClassDefFoundError) {
 | 
			
		||||
            catch(NoClassDefFoundError e) {
 | 
			
		||||
                getLogger().severe("Backup class not found!");
 | 
			
		||||
                getLogger().info("Please do not replace the mcMMO jar while the server is running.");
 | 
			
		||||
            }
 | 
			
		||||
                else {
 | 
			
		||||
            catch (Throwable e) {
 | 
			
		||||
                getLogger().severe(e.toString());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        debug("Canceling all tasks...");
 | 
			
		||||
        getServer().getScheduler().cancelTasks(this); // This removes our tasks
 | 
			
		||||
@@ -656,7 +664,7 @@ public class mcMMO extends JavaPlugin {
 | 
			
		||||
 | 
			
		||||
    public @Nullable InputStreamReader getResourceAsReader(String fileName) {
 | 
			
		||||
        InputStream in = getResource(fileName);
 | 
			
		||||
        return in == null ? null : new InputStreamReader(in, Charsets.UTF_8);
 | 
			
		||||
        return in == null ? null : new InputStreamReader(in, StandardCharsets.UTF_8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -180,10 +180,12 @@ public class HerbalismManager extends SkillManager {
 | 
			
		||||
 | 
			
		||||
        //TODO: The design of Green Terra needs to change, this is a mess
 | 
			
		||||
        if(Permissions.greenThumbPlant(getPlayer(), originalBreak.getType())) {
 | 
			
		||||
            if(Config.getInstance().isGreenThumbReplantableCrop(originalBreak.getType())) {
 | 
			
		||||
                if(!getPlayer().isSneaking()) {
 | 
			
		||||
                    greenThumbActivated = processGreenThumbPlants(originalBreak, blockBreakEvent, isGreenTerraActive());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //When replanting a immature crop we cancel the block break event and back out
 | 
			
		||||
        if(greenThumbActivated) {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,12 @@ import com.gmail.nossr50.util.Permissions;
 | 
			
		||||
import com.gmail.nossr50.util.random.RandomChanceUtil;
 | 
			
		||||
import com.gmail.nossr50.util.skills.RankUtils;
 | 
			
		||||
import com.gmail.nossr50.util.skills.SkillActivationType;
 | 
			
		||||
import org.bukkit.block.Furnace;
 | 
			
		||||
import org.bukkit.event.inventory.FurnaceBurnEvent;
 | 
			
		||||
import org.bukkit.event.inventory.FurnaceSmeltEvent;
 | 
			
		||||
import org.bukkit.inventory.FurnaceInventory;
 | 
			
		||||
import org.bukkit.inventory.ItemStack;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
 | 
			
		||||
public class SmeltingManager extends SkillManager {
 | 
			
		||||
    public SmeltingManager(OnlineMMOPlayer mmoPlayer) {
 | 
			
		||||
@@ -106,19 +110,41 @@ public class SmeltingManager extends SkillManager {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemStack smeltProcessing(ItemStack smelting, ItemStack result) {
 | 
			
		||||
    public void smeltProcessing(@NotNull FurnaceSmeltEvent furnaceSmeltEvent, @NotNull Furnace furnace) {
 | 
			
		||||
        applyXpGain(Smelting.getResourceXp(furnaceSmeltEvent.getSource()), XPGainReason.PVE, XPGainSource.PASSIVE); //Add XP
 | 
			
		||||
 | 
			
		||||
        applyXpGain(Smelting.getResourceXp(smelting), XPGainReason.PVE, XPGainSource.PASSIVE);
 | 
			
		||||
 | 
			
		||||
        if (Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.SMELTING, result.getType())
 | 
			
		||||
                && isSecondSmeltSuccessful() && result.getAmount() < 64) {
 | 
			
		||||
            ItemStack newResult = result.clone();
 | 
			
		||||
 | 
			
		||||
            newResult.setAmount(result.getAmount() + 1);
 | 
			
		||||
            return newResult;
 | 
			
		||||
        processDoubleSmelt(furnaceSmeltEvent, furnace);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    private void processDoubleSmelt(@NotNull FurnaceSmeltEvent furnaceSmeltEvent, @NotNull Furnace furnace) {
 | 
			
		||||
        ItemStack resultItemStack = furnaceSmeltEvent.getResult();
 | 
			
		||||
        /*
 | 
			
		||||
            doubleSmeltCondition should be equal to the max
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        //Process double smelt
 | 
			
		||||
        if (Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.SMELTING, resultItemStack.getType())
 | 
			
		||||
                && canDoubleSmeltItemStack(furnace) //Effectively two less than max stack size
 | 
			
		||||
                && isSecondSmeltSuccessful()) {
 | 
			
		||||
 | 
			
		||||
            ItemStack doubleSmeltStack = resultItemStack.clone(); //TODO: Necessary?
 | 
			
		||||
            doubleSmeltStack.setAmount(resultItemStack.getAmount() + 1); //Add one
 | 
			
		||||
            furnaceSmeltEvent.setResult(doubleSmeltStack); //Set result
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean canDoubleSmeltItemStack(@NotNull Furnace furnace) {
 | 
			
		||||
        FurnaceInventory furnaceInventory = furnace.getInventory();
 | 
			
		||||
        ItemStack furnaceResult = furnaceInventory.getResult();
 | 
			
		||||
 | 
			
		||||
        if(furnaceResult == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        int resultAmount = furnaceResult.getAmount(); //Amount before double smelt
 | 
			
		||||
        int itemLimit = furnaceResult.getMaxStackSize();
 | 
			
		||||
        int doubleSmeltCondition = itemLimit - 2; //Don't double smelt if it would cause an illegal stack size
 | 
			
		||||
 | 
			
		||||
        return resultAmount <= doubleSmeltCondition;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int vanillaXPBoost(int experience) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import com.gmail.nossr50.locale.LocaleLoader;
 | 
			
		||||
import com.gmail.nossr50.mcMMO;
 | 
			
		||||
import org.bukkit.ChatColor;
 | 
			
		||||
import org.bukkit.Material;
 | 
			
		||||
import org.bukkit.NamespacedKey;
 | 
			
		||||
import org.bukkit.enchantments.Enchantment;
 | 
			
		||||
import org.bukkit.entity.Player;
 | 
			
		||||
import org.bukkit.inventory.FurnaceRecipe;
 | 
			
		||||
@@ -18,6 +19,7 @@ import org.bukkit.inventory.Recipe;
 | 
			
		||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
 | 
			
		||||
import org.bukkit.inventory.meta.ItemMeta;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
import org.jetbrains.annotations.Nullable;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -58,11 +60,99 @@ public final class ItemUtils {
 | 
			
		||||
        return player.getInventory().getItemInMainHand().getType().getKey().getKey().equalsIgnoreCase(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isCrossbow(@NotNull ItemStack item) {
 | 
			
		||||
        return mcMMO.getMaterialMapStore().isCrossbow(item.getType().getKey().getKey());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean hasItemInEitherHand(@NotNull Player player, Material material) {
 | 
			
		||||
        return player.getInventory().getItemInMainHand().getType() == material || player.getInventory().getItemInOffHand().getType() == material;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean hasItemInOffHand(@NotNull Player player, @NotNull String id) {
 | 
			
		||||
        return player.getInventory().getItemInOffHand().getType().getKey().getKey().equalsIgnoreCase(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static boolean doesPlayerHaveEnchantmentOnArmor(@NotNull Player player, @NotNull String enchantmentByName) {
 | 
			
		||||
        Enchantment enchantment = getEnchantment(enchantmentByName);
 | 
			
		||||
 | 
			
		||||
        if(enchantment == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return doesPlayerHaveEnchantmentOnArmor(player, enchantment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean doesPlayerHaveEnchantmentOnArmor(@NotNull Player player, @NotNull Enchantment enchantment) {
 | 
			
		||||
        for(ItemStack itemStack : player.getInventory().getArmorContents()) {
 | 
			
		||||
            if(itemStack != null) {
 | 
			
		||||
                if(hasEnchantment(itemStack, enchantment))
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean doesPlayerHaveEnchantmentOnArmorOrHands(@NotNull Player player, @NotNull String enchantmentName) {
 | 
			
		||||
        Enchantment enchantment = getEnchantment(enchantmentName);
 | 
			
		||||
 | 
			
		||||
        if(enchantment == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return doesPlayerHaveEnchantmentOnArmorOrHands(player, enchantment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean doesPlayerHaveEnchantmentOnArmorOrHands(@NotNull Player player, @NotNull Enchantment enchantment) {
 | 
			
		||||
        if(doesPlayerHaveEnchantmentOnArmor(player, enchantment))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if(doesPlayerHaveEnchantmentInHands(player, enchantment))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean doesPlayerHaveEnchantmentInHands(@NotNull Player player, @NotNull NamespacedKey enchantmentNameKey) {
 | 
			
		||||
        Enchantment enchantment = Enchantment.getByKey(enchantmentNameKey);
 | 
			
		||||
 | 
			
		||||
        if(enchantment == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return doesPlayerHaveEnchantmentInHands(player, enchantment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean doesPlayerHaveEnchantmentInHands(@NotNull Player player, @NotNull String enchantmentName) {
 | 
			
		||||
        Enchantment enchantment = getEnchantment(enchantmentName);
 | 
			
		||||
 | 
			
		||||
        if(enchantment == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return doesPlayerHaveEnchantmentInHands(player, enchantment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean doesPlayerHaveEnchantmentInHands(@NotNull Player player, @NotNull Enchantment enchantment) {
 | 
			
		||||
        return hasEnchantment(player.getInventory().getItemInMainHand(), enchantment) ||
 | 
			
		||||
            hasEnchantment(player.getInventory().getItemInOffHand(), enchantment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean hasEnchantment(@NotNull ItemStack itemStack, @NotNull Enchantment enchantment) {
 | 
			
		||||
        if(itemStack.getItemMeta() != null) {
 | 
			
		||||
            return itemStack.getItemMeta().hasEnchant(enchantment);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static @Nullable Enchantment getEnchantment(@NotNull String enchantmentName) {
 | 
			
		||||
        for(Enchantment enchantment : Enchantment.values()) {
 | 
			
		||||
            if(enchantment.getKey().getKey().equalsIgnoreCase(enchantmentName)) {
 | 
			
		||||
                return enchantment;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the item is a sword.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ public class MaterialMapStore {
 | 
			
		||||
    private final @NotNull HashSet<String> pickAxes;
 | 
			
		||||
    private final @NotNull HashSet<String> tridents;
 | 
			
		||||
    private final @NotNull HashSet<String> bows;
 | 
			
		||||
    private final @NotNull HashSet<String> xbows;
 | 
			
		||||
    private final @NotNull HashSet<String> crossbows;
 | 
			
		||||
    private final @NotNull HashSet<String> tools;
 | 
			
		||||
 | 
			
		||||
    private final @NotNull HashSet<String> enchantables;
 | 
			
		||||
@@ -88,7 +88,7 @@ public class MaterialMapStore {
 | 
			
		||||
        diamondTools = new HashSet<>();
 | 
			
		||||
        netheriteTools = new HashSet<>();
 | 
			
		||||
        bows = new HashSet<>();
 | 
			
		||||
        xbows = new HashSet<>();
 | 
			
		||||
        crossbows = new HashSet<>();
 | 
			
		||||
        stringTools = new HashSet<>();
 | 
			
		||||
        tools = new HashSet<>();
 | 
			
		||||
 | 
			
		||||
@@ -459,15 +459,15 @@ public class MaterialMapStore {
 | 
			
		||||
        tools.addAll(tridents);
 | 
			
		||||
        tools.addAll(stringTools);
 | 
			
		||||
        tools.addAll(bows);
 | 
			
		||||
        tools.addAll(xbows);
 | 
			
		||||
        tools.addAll(crossbows);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void fillBows() {
 | 
			
		||||
        bows.add("bow");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void fillCrossBows() {
 | 
			
		||||
        xbows.add("crossbow");
 | 
			
		||||
    private void fillCrossbows() {
 | 
			
		||||
        crossbows.add("crossbow");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void fillStringTools() {
 | 
			
		||||
@@ -782,7 +782,7 @@ public class MaterialMapStore {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isCrossbow(@NotNull String id) {
 | 
			
		||||
        return xbows.contains(id);
 | 
			
		||||
        return crossbows.contains(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isLeatherArmor(@NotNull Material material) {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
 | 
			
		||||
import com.google.common.collect.ImmutableSet;
 | 
			
		||||
import org.bukkit.Location;
 | 
			
		||||
import org.bukkit.Material;
 | 
			
		||||
import org.bukkit.World;
 | 
			
		||||
import org.bukkit.block.BlockState;
 | 
			
		||||
import org.bukkit.entity.*;
 | 
			
		||||
import org.bukkit.inventory.ItemStack;
 | 
			
		||||
@@ -258,6 +259,12 @@ public final class Misc {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int getWorldMinCompat(World world)
 | 
			
		||||
    {
 | 
			
		||||
        // TODO this method should access the world min variable in a version safe manner so that we don't restrict usage to new versions of spigot only
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void printProgress(int convertedUsers, int progressInterval, long startMillis) {
 | 
			
		||||
        if ((convertedUsers % progressInterval) == 0) {
 | 
			
		||||
            mcMMO.p.getLogger().info(String.format("Conversion progress: %d users at %.2f users/second", convertedUsers, convertedUsers / (double) ((System.currentTimeMillis() - startMillis) / TIME_CONVERSION_FACTOR)));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package com.gmail.nossr50.util.blockmeta;
 | 
			
		||||
 | 
			
		||||
import com.gmail.nossr50.util.Misc;
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.World;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
@@ -10,12 +11,13 @@ import java.util.BitSet;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
    private static final int CURRENT_VERSION = 8;
 | 
			
		||||
    private static final int CURRENT_VERSION = 9;
 | 
			
		||||
    private static final int MAGIC_NUMBER = 0xEA5EDEBB;
 | 
			
		||||
 | 
			
		||||
    private final int cx;
 | 
			
		||||
    private final int cz;
 | 
			
		||||
    private final int worldHeight;
 | 
			
		||||
    private final int worldMin;
 | 
			
		||||
    private final int worldMax;
 | 
			
		||||
    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;
 | 
			
		||||
@@ -23,15 +25,16 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
    private transient boolean dirty = false;
 | 
			
		||||
 | 
			
		||||
    public BitSetChunkStore(@NotNull World world, int cx, int cz) {
 | 
			
		||||
        this(world.getUID(), world.getMaxHeight(), cx, cz);
 | 
			
		||||
        this(world.getUID(), Misc.getWorldMinCompat(world), world.getMaxHeight(), cx, cz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private BitSetChunkStore(@NotNull UUID worldUid, int worldHeight, int cx, int cz) {
 | 
			
		||||
    private BitSetChunkStore(@NotNull UUID worldUid, int worldMin, int worldMax, int cx, int cz) {
 | 
			
		||||
        this.cx = cx;
 | 
			
		||||
        this.cz = cz;
 | 
			
		||||
        this.worldUid = worldUid;
 | 
			
		||||
        this.worldHeight = worldHeight;
 | 
			
		||||
        this.store = new BitSet(16 * 16 * worldHeight);
 | 
			
		||||
        this.worldMin = worldMin;
 | 
			
		||||
        this.worldMax = worldMax;
 | 
			
		||||
        this.store = new BitSet(16 * 16 * (worldMax - worldMin));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -54,6 +57,16 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
        return cz;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getChunkMin() {
 | 
			
		||||
        return worldMin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getChunkMax() {
 | 
			
		||||
        return worldMax;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public @NotNull UUID getWorldId() {
 | 
			
		||||
        return worldUid;
 | 
			
		||||
@@ -86,22 +99,34 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int coordToIndex(int x, int y, int z) {
 | 
			
		||||
        return coordToIndex(x, y, z, worldHeight);
 | 
			
		||||
        return coordToIndex(x, y, z, worldMin, worldMax);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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(String.format("x: %d y: %d z: %d World Height: %d", x, y, z, worldHeight));
 | 
			
		||||
        return (z * 16 + x) + (256 * y);
 | 
			
		||||
    private static int coordToIndex(int x, int y, int z, int worldMin, int worldMax) {
 | 
			
		||||
        if (x < 0 || x >= 16 || y < worldMin || y >= worldMax || z < 0 || z >= 16)
 | 
			
		||||
            throw new IndexOutOfBoundsException(String.format("x: %d y: %d z: %d World Min: %d World Max: %d", x, y, z, worldMin, worldMax));
 | 
			
		||||
        int yOffset = -worldMin; // Ensures y multiplier remains positive
 | 
			
		||||
        return (z * 16 + x) + (256 * (y + yOffset));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int getWorldHeight(@NotNull UUID worldUid, int storedWorldHeight)
 | 
			
		||||
    private static int getWorldMin(@NotNull UUID worldUid, int storedWorldMin)
 | 
			
		||||
    {
 | 
			
		||||
        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 storedWorldHeight;
 | 
			
		||||
            return storedWorldMin;
 | 
			
		||||
 | 
			
		||||
        return Misc.getWorldMinCompat(world);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int getWorldMax(@NotNull UUID worldUid, int storedWorldMax)
 | 
			
		||||
    {
 | 
			
		||||
        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 storedWorldMax;
 | 
			
		||||
 | 
			
		||||
        return world.getMaxHeight();
 | 
			
		||||
    }
 | 
			
		||||
@@ -114,7 +139,8 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
        out.writeLong(worldUid.getMostSignificantBits());
 | 
			
		||||
        out.writeInt(cx);
 | 
			
		||||
        out.writeInt(cz);
 | 
			
		||||
        out.writeInt(worldHeight);
 | 
			
		||||
        out.writeInt(worldMin);
 | 
			
		||||
        out.writeInt(worldMax);
 | 
			
		||||
 | 
			
		||||
        // Store the byte array directly so we don't have the object type info overhead
 | 
			
		||||
        byte[] storeData = store.toByteArray();
 | 
			
		||||
@@ -129,7 +155,7 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
        // Can be used to determine the format of the file
 | 
			
		||||
        int fileVersionNumber = in.readInt();
 | 
			
		||||
 | 
			
		||||
        if (magic != MAGIC_NUMBER || fileVersionNumber != CURRENT_VERSION)
 | 
			
		||||
        if (magic != MAGIC_NUMBER || fileVersionNumber < 8)
 | 
			
		||||
            throw new IOException();
 | 
			
		||||
 | 
			
		||||
        long lsb = in.readLong();
 | 
			
		||||
@@ -138,21 +164,38 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
        int cx = in.readInt();
 | 
			
		||||
        int cz = in.readInt();
 | 
			
		||||
 | 
			
		||||
        int worldHeight = in.readInt();
 | 
			
		||||
        int worldMin = 0;
 | 
			
		||||
        if (fileVersionNumber >= 9)
 | 
			
		||||
            worldMin = in.readInt();
 | 
			
		||||
        int worldMax = in.readInt();
 | 
			
		||||
        byte[] temp = new byte[in.readInt()];
 | 
			
		||||
        in.readFully(temp);
 | 
			
		||||
        BitSet stored = BitSet.valueOf(temp);
 | 
			
		||||
 | 
			
		||||
        int currentWorldHeight = getWorldHeight(worldUid, worldHeight);
 | 
			
		||||
        int currentWorldMin = getWorldMin(worldUid, worldMin);
 | 
			
		||||
        int currentWorldMax = getWorldMax(worldUid, worldMax);
 | 
			
		||||
 | 
			
		||||
        boolean worldHeightShrunk = currentWorldHeight < worldHeight;
 | 
			
		||||
        // Lop off extra data if world height has shrunk
 | 
			
		||||
        if (worldHeightShrunk)
 | 
			
		||||
            stored.clear(coordToIndex(16, currentWorldHeight, 16, worldHeight), stored.length());
 | 
			
		||||
        // The order in which the world height update code occurs here is important, the world max truncate math only holds up if done before adjusting for min changes
 | 
			
		||||
        // Lop off extra data if world max has shrunk
 | 
			
		||||
        if (currentWorldMax < worldMax)
 | 
			
		||||
            stored.clear(coordToIndex(16, currentWorldMax, 16, worldMin, worldMax), stored.length());
 | 
			
		||||
        // Left shift store if world min has shrunk
 | 
			
		||||
        if (currentWorldMin > worldMin)
 | 
			
		||||
            stored = stored.get(currentWorldMin, stored.length()); // Because BitSet's aren't fixed size, a "substring" operation is equivalent to a left shift
 | 
			
		||||
        // Right shift store if world min has expanded
 | 
			
		||||
        if (currentWorldMin < worldMin)
 | 
			
		||||
        {
 | 
			
		||||
            int offset = (worldMin - currentWorldMin) * 16 * 16; // We are adding this many bits to the front
 | 
			
		||||
            // This isn't the most efficient way to do this, however, its a rare case to occur, and in the grand scheme of things, the small performance we could gain would cost us significant reduced readability of the code
 | 
			
		||||
            BitSet shifted = new BitSet();
 | 
			
		||||
            for (int i = 0; i < stored.length(); i++)
 | 
			
		||||
                shifted.set(i + offset, stored.get(i));
 | 
			
		||||
            stored = shifted;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        BitSetChunkStore chunkStore = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz);
 | 
			
		||||
        BitSetChunkStore chunkStore = new BitSetChunkStore(worldUid, currentWorldMin, currentWorldMax, 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.dirty = currentWorldMin != worldMin || currentWorldMax != worldMax;
 | 
			
		||||
 | 
			
		||||
        return chunkStore;
 | 
			
		||||
    }
 | 
			
		||||
@@ -203,7 +246,7 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
 | 
			
		||||
                private int cx;
 | 
			
		||||
                private int cz;
 | 
			
		||||
                private int worldHeight;
 | 
			
		||||
                private int worldMax;
 | 
			
		||||
                private UUID worldUid;
 | 
			
		||||
                private boolean[][][] store;
 | 
			
		||||
 | 
			
		||||
@@ -226,19 +269,20 @@ public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
                    cz = in.readInt();
 | 
			
		||||
 | 
			
		||||
                    store = (boolean[][][]) in.readObject();
 | 
			
		||||
                    worldHeight = store[0][0].length;
 | 
			
		||||
                    worldMax = store[0][0].length;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                public @NotNull BitSetChunkStore convert()
 | 
			
		||||
                {
 | 
			
		||||
                    int currentWorldHeight = getWorldHeight(worldUid, worldHeight);
 | 
			
		||||
                    int currentWorldMin = getWorldMin(worldUid, 0);
 | 
			
		||||
                    int currentWorldMax = getWorldMax(worldUid, worldMax);
 | 
			
		||||
 | 
			
		||||
                    BitSetChunkStore converted = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz);
 | 
			
		||||
                    BitSetChunkStore converted = new BitSetChunkStore(worldUid, currentWorldMin, currentWorldMax, 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++) {
 | 
			
		||||
                            for (int y = 0; y < worldMax && y < currentWorldMax; y++) {
 | 
			
		||||
                                converted.store.set(converted.coordToIndex(x, y, z), store[x][z][y]);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,9 @@ public interface ChunkStore {
 | 
			
		||||
     */
 | 
			
		||||
    int getChunkZ();
 | 
			
		||||
 | 
			
		||||
    int getChunkMin();
 | 
			
		||||
    int getChunkMax();
 | 
			
		||||
 | 
			
		||||
    @NotNull UUID getWorldId();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -459,6 +459,14 @@ Skills:
 | 
			
		||||
        Level_Cap: 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Disable or Enable the Green Thumb auto replant feature for specific crops, use the name of the block not the crop itemstack
 | 
			
		||||
Green_Thumb_Replanting_Crops:
 | 
			
		||||
    Carrots: true
 | 
			
		||||
    Wheat: true
 | 
			
		||||
    Nether_Wart: true
 | 
			
		||||
    Potatoes: true
 | 
			
		||||
    Beetroots: true
 | 
			
		||||
    Cocoa: true
 | 
			
		||||
#
 | 
			
		||||
#  Settings for Double Drops
 | 
			
		||||
###
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
package com.gmail.nossr50.util.blockmeta;
 | 
			
		||||
 | 
			
		||||
import com.gmail.nossr50.TestUtil;
 | 
			
		||||
import com.gmail.nossr50.util.Misc;
 | 
			
		||||
import com.google.common.io.Files;
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.World;
 | 
			
		||||
@@ -22,7 +23,7 @@ import static org.mockito.Mockito.mock;
 | 
			
		||||
 * Could be a lot better.  But some tests are better than none!  Tests the major things, still kinda unit-testy.  Verifies that the serialization isn't completely broken.
 | 
			
		||||
 */
 | 
			
		||||
@RunWith(PowerMockRunner.class)
 | 
			
		||||
@PrepareForTest(Bukkit.class)
 | 
			
		||||
@PrepareForTest({ Bukkit.class, Misc.class })
 | 
			
		||||
public class ChunkStoreTest {
 | 
			
		||||
    private static File tempDir;
 | 
			
		||||
    @BeforeClass
 | 
			
		||||
@@ -76,6 +77,34 @@ public class ChunkStoreTest {
 | 
			
		||||
        assertEqual(original, deserialized);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testNegativeWorldMin() throws IOException {
 | 
			
		||||
        PowerMockito.mockStatic(Misc.class);
 | 
			
		||||
        Mockito.when(Misc.getWorldMinCompat(mockWorld)).thenReturn(-64);
 | 
			
		||||
 | 
			
		||||
        BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
 | 
			
		||||
        original.setTrue(14, -32, 12);
 | 
			
		||||
        original.setTrue(14, -64, 12);
 | 
			
		||||
        original.setTrue(13, -63, 12);
 | 
			
		||||
        byte[] serializedBytes = serializeChunkstore(original);
 | 
			
		||||
        ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
 | 
			
		||||
        assertEqual(original, deserialized);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testNegativeWorldMinUpgrade() throws IOException {
 | 
			
		||||
        BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
 | 
			
		||||
        original.setTrue(14, 1, 12);
 | 
			
		||||
        original.setTrue(14, 2, 12);
 | 
			
		||||
        original.setTrue(13, 3, 12);
 | 
			
		||||
        byte[] serializedBytes = serializeChunkstore(original);
 | 
			
		||||
 | 
			
		||||
        PowerMockito.mockStatic(Misc.class);
 | 
			
		||||
        Mockito.when(Misc.getWorldMinCompat(mockWorld)).thenReturn(-64);
 | 
			
		||||
        ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
 | 
			
		||||
        assertEqualIgnoreMinMax(original, deserialized);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testChunkCoords() throws IOException {
 | 
			
		||||
        for (int x = -96; x < 0; x++) {
 | 
			
		||||
@@ -175,15 +204,26 @@ public class ChunkStoreTest {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void assertEqual(ChunkStore expected, ChunkStore actual)
 | 
			
		||||
    {
 | 
			
		||||
        Assert.assertEquals(expected.getChunkMin(), actual.getChunkMin());
 | 
			
		||||
        Assert.assertEquals(expected.getChunkMax(), actual.getChunkMax());
 | 
			
		||||
        assertEqualIgnoreMinMax(expected, actual);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void assertEqualIgnoreMinMax(ChunkStore expected, ChunkStore actual)
 | 
			
		||||
    {
 | 
			
		||||
        Assert.assertEquals(expected.getChunkX(), actual.getChunkX());
 | 
			
		||||
        Assert.assertEquals(expected.getChunkZ(), actual.getChunkZ());
 | 
			
		||||
        Assert.assertEquals(expected.getWorldId(), actual.getWorldId());
 | 
			
		||||
        for (int y = 0; y < 256; y++)
 | 
			
		||||
        for (int y = Math.min(actual.getChunkMin(), expected.getChunkMin()); y < Math.max(actual.getChunkMax(), expected.getChunkMax()); y++)
 | 
			
		||||
        {
 | 
			
		||||
            if (expected.getChunkMin() > y || actual.getChunkMin() > y || expected.getChunkMax() <= y || actual.getChunkMax() <= y)
 | 
			
		||||
                continue; // Ignore
 | 
			
		||||
            for (int x = 0; x < 16; x++)
 | 
			
		||||
                for (int z = 0; z < 16; z++)
 | 
			
		||||
                    Assert.assertTrue(expected.isTrue(x, y, z) == actual.isTrue(x, y, z));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static byte[] serializeChunkstore(@NotNull ChunkStore chunkStore) throws IOException {
 | 
			
		||||
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
 | 
			
		||||
@@ -231,6 +271,16 @@ public class ChunkStoreTest {
 | 
			
		||||
            return cz;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int getChunkMin() {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int getChunkMax() {
 | 
			
		||||
            return store[0][0].length;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public @NotNull UUID getWorldId() {
 | 
			
		||||
            return worldUid;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user