commit 8712e4ee3c42fc174efc7ef99dade3fc59d3b654 Author: hibo98 Date: Sun Aug 11 16:06:09 2019 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84fcc51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +dist/ +build/ +nbproject/private \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..04b2c20 --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project RealisticMinecraft. + + + diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..79e223b --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1768 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..04b5843 --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=384066bc +build.xml.script.CRC32=61428c28 +build.xml.stylesheet.CRC32=f85dc8f2@1.91.1.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=384066bc +nbproject/build-impl.xml.script.CRC32=6151eca2 +nbproject/build-impl.xml.stylesheet.CRC32=3a2fa800@1.91.1.48 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..87405b5 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,102 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=RealisticMinecraft +application.vendor=NMerkelt +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.modulepath=\ + ${run.modulepath} +debug.test.classpath=\ + ${run.test.classpath} +debug.test.modulepath=\ + ${run.test.modulepath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/RealisticMinecraft.jar +dist.javadoc.dir=${dist.dir}/javadoc +dist.jlink.dir=${dist.dir}/jlink +dist.jlink.output=${dist.jlink.dir}/RealisticMinecraft +endorsed.classpath= +excludes= +file.reference.AnuraCore.jar=..\\libs\\AnuraCore.jar +file.reference.LogBlock.jar=../libs/LogBlock.jar +includes=** +jar.compress=false +javac.classpath=\ + ${libs.Spigot-Server.classpath}:\ + ${file.reference.LogBlock.jar}:\ + ${file.reference.AnuraCore.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=true +javac.modulepath= +javac.processormodulepath= +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.modulepath=\ + ${javac.modulepath} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.html5=false +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +# The jlink additional root modules to resolve +jlink.additionalmodules= +# The jlink additional command line parameters +jlink.additionalparam= +jlink.launcher=true +jlink.launcher.name=RealisticMinecraft +main.class=de.anura.realisticminecraft.RealisticMinecraft +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.modulepath=\ + ${javac.modulepath} +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +run.test.modulepath=\ + ${javac.test.modulepath} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..5a5f754 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + RealisticMinecraft + + + + + + + + + diff --git a/src/de/anura/realisticminecraft/RealisticMinecraft.java b/src/de/anura/realisticminecraft/RealisticMinecraft.java new file mode 100644 index 0000000..e417399 --- /dev/null +++ b/src/de/anura/realisticminecraft/RealisticMinecraft.java @@ -0,0 +1,93 @@ +package de.anura.realisticminecraft; + +import de.anura.core.AnuraThread; +import de.anura.core.database.DB; +import de.anura.core.msg.Msg; +import de.anura.core.msg.Msg.PluginData; +import de.anura.realisticminecraft.command.InfobarCmd; +import de.anura.realisticminecraft.command.InfobarSetCmd; +import de.anura.realisticminecraft.fishing.FishingChunk; +import de.anura.realisticminecraft.infobar.InfobarUtil; +import de.anura.realisticminecraft.listener.Chairs; +import de.anura.realisticminecraft.util.ChairManager; +import de.anura.realisticminecraft.infobar.ValueHolder; +import de.anura.realisticminecraft.listener.Fishing; +import de.anura.realisticminecraft.listener.Infobar; +import de.anura.realisticminecraft.listener.Timber; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.java.JavaPlugin; + +public class RealisticMinecraft extends JavaPlugin { + + private static RealisticMinecraft instance; + public static final PluginData PLUGIN_DATA = new PluginData("RealisticMC", Msg.PluginType.SYSTEM); + + @Override + public void onEnable() { + instance = this; + DB.queryUpdate("CREATE TABLE IF NOT EXISTS `playerTemperature` (" + + " `playerId` INT(10) UNSIGNED NOT NULL," + + " `value` DECIMAL(7,5) NOT NULL," + + " `bar` BIT(1) NOT NULL DEFAULT b'0'," + + " PRIMARY KEY (`playerId`)," + + " INDEX `playerTemperature_playerId` (`playerId`)," + + " CONSTRAINT `playerTemperature_playerId` FOREIGN KEY (`playerId`) REFERENCES `players` (`id`) ON DELETE CASCADE ON UPDATE CASCADE" + + ") ENGINE=InnoDB COLLATE='utf8_general_ci' COMMENT='RealisticMinecraft | hibo98'"); + DB.queryUpdate("CREATE TABLE IF NOT EXISTS `playerThirst` (" + + " `playerId` INT(10) UNSIGNED NOT NULL," + + " `value` INT(11) NOT NULL," + + " `bar` BIT(1) NOT NULL DEFAULT b'0'," + + " PRIMARY KEY (`playerId`)," + + " INDEX `playerTemperature_playerId` (`playerId`)," + + " CONSTRAINT `playerThirst_playerId` FOREIGN KEY (`playerId`) REFERENCES `players` (`id`) ON DELETE CASCADE ON UPDATE CASCADE" + + ") ENGINE=InnoDB COLLATE='utf8_general_ci' COMMENT='RealisticMinecraft | hibo98'"); + DB.queryUpdate("CREATE TABLE IF NOT EXISTS `fishingChunks` ( " + + " `primeKey` INT(11) NOT NULL AUTO_INCREMENT, " + + " `x` INT(11) NOT NULL, " + + " `z` INT(11) NOT NULL, " + + " `world` VARCHAR(50) COLLATE latin1_german1_ci NOT NULL, " + + " `catched` INT(11) NOT NULL, " + + " `lastUpdated` INT(11) NOT NULL, " + + " PRIMARY KEY (`primeKey`) " + + ") ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci COMMENT='RealisticMinecraft | LukBukkit'"); + // Chairs + Bukkit.getPluginManager().registerEvents(new Chairs(), instance); + // Infobar + AnuraThread.async(() -> Bukkit.getOnlinePlayers().stream().filter((p) -> p.getGameMode().equals(GameMode.SURVIVAL)).forEach(ValueHolder::new)); + InfobarCmd infobarCmd = new InfobarCmd(); + PluginCommand infobar = instance.getCommand("infobar"); + if (infobar != null) { + infobar.setExecutor(infobarCmd); + infobar.setTabCompleter(infobarCmd); + } + InfobarSetCmd infobarSetCmd = new InfobarSetCmd(); + PluginCommand infobarset = instance.getCommand("infobarset"); + if (infobarset != null) { + infobarset.setExecutor(infobarSetCmd); + infobarset.setTabCompleter(infobarSetCmd); + } + InfobarUtil.enableInfobarTasks(); + Bukkit.getPluginManager().registerEvents(new Infobar(), instance); + // Fishing + FishingChunk.init(); + Bukkit.getPluginManager().registerEvents(new Fishing(), instance); + // Trees + Bukkit.getPluginManager().registerEvents(new Timber(), instance); + } + + @Override + public void onDisable() { + ValueHolder.destroyAll(); + ChairManager.destoryAll(); + } + + public static RealisticMinecraft getInstance() { + return instance; + } + + public static boolean hasLogBlock() { + return Bukkit.getServer().getPluginManager().getPlugin("LogBlock") != null; + } +} diff --git a/src/de/anura/realisticminecraft/command/InfobarCmd.java b/src/de/anura/realisticminecraft/command/InfobarCmd.java new file mode 100644 index 0000000..ce6aa97 --- /dev/null +++ b/src/de/anura/realisticminecraft/command/InfobarCmd.java @@ -0,0 +1,88 @@ +package de.anura.realisticminecraft.command; + +import de.anura.core.msg.Msg; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import de.anura.realisticminecraft.RealisticMinecraft; +import de.anura.realisticminecraft.infobar.TemperaturePlayer; +import de.anura.realisticminecraft.infobar.ThirstPlayer; +import de.anura.realisticminecraft.infobar.BarStatus; +import de.anura.realisticminecraft.infobar.ValueHolder; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; + +public class InfobarCmd implements TabExecutor { + + public InfobarCmd() { +// new Help("infobar", "Konfigurieret die Anzeige der Infobars") +// .addArgument("durst | temp", "Wählt Durst oder Temperatur aus") +// .addArgument("an | standard", "Schaltet die Bar auf AN oder STANDARD") +// .build(); + } + + @Override + public boolean onCommand(CommandSender cs, Command cmnd, String label, String[] args) { + if (!(cs instanceof Player)) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "This is a player command!"); + return false; + } + if (args.length < 2 || args.length > 3) { + return false; + } + if (!((HumanEntity) cs).getGameMode().equals(GameMode.SURVIVAL)) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Dieses Command kann nur im SURVIVAL Mode verwendet werden"); + return true; + } + BarStatus status; + try { + status = BarStatus.valueOf(args[1].toUpperCase()); + } catch (IllegalArgumentException e) { + return false; + } + ValueHolder valueHolder = ValueHolder.getValueHolder((Player) cs); + if (valueHolder == null) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Es ist einer interner Fehler aufgetreten (0)"); + return true; + } + ThirstPlayer thirstPlayer = valueHolder.getPlayer(ThirstPlayer.class); + TemperaturePlayer temperaturePlayer = valueHolder.getPlayer(TemperaturePlayer.class); + if (thirstPlayer == null || temperaturePlayer == null) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Es ist einer interner Fehler aufgetreten (1)"); + return true; + } + switch (args[0].toLowerCase()) { + case "durst": + thirstPlayer.getBar().setBarStatus(status); + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.SUCCESS, "Die Durst Infobar wird dir nun" + (status.equals(BarStatus.STANDARD) ? " nicht " : " ") + "immer angezeigt!"); + return true; + case "temp": + case "temperatur": + temperaturePlayer.getBar().setBarStatus(status); + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.SUCCESS, "Die Temperatur Infobar wird dir nun" + (status.equals(BarStatus.STANDARD) ? " nicht " : " ") + "immer angezeigt!"); + return true; + default: + return false; + } + } + + @Override + public List onTabComplete(CommandSender cs, Command cmnd, String label, String[] args) { + List list = new ArrayList<>(); + switch (args.length) { + case 1: + list.add("durst"); + list.add("temp"); + break; + case 2: + list.add("an"); + list.add("standard"); + break; + } + return list.stream().filter((s) -> s.startsWith(args[args.length - 1].toLowerCase())).collect(Collectors.toList()); + } +} diff --git a/src/de/anura/realisticminecraft/command/InfobarSetCmd.java b/src/de/anura/realisticminecraft/command/InfobarSetCmd.java new file mode 100644 index 0000000..9769ffc --- /dev/null +++ b/src/de/anura/realisticminecraft/command/InfobarSetCmd.java @@ -0,0 +1,91 @@ +package de.anura.realisticminecraft.command; + +import de.anura.core.msg.Msg; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import de.anura.realisticminecraft.RealisticMinecraft; +import de.anura.realisticminecraft.infobar.TemperaturePlayer; +import de.anura.realisticminecraft.infobar.ThirstPlayer; +import de.anura.realisticminecraft.infobar.ValueHolder; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; + +public class InfobarSetCmd implements TabExecutor { + + @Override + public boolean onCommand(CommandSender cs, Command cmnd, String label, String[] args) { + if (!cs.hasPermission("anura.realisticmc.infobarset")) { + Msg.noPerms(cs); + return true; + } + if (!(cs instanceof Player)) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "This is a player command!"); + return true; + } + if (args.length < 2 || args.length > 3) { + return false; + } + if (!((HumanEntity) cs).getGameMode().equals(GameMode.SURVIVAL)) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Dieses Command kann nur im SURVIVAL Mode verwendet werden"); + return true; + } + ValueHolder valueHolder = ValueHolder.getValueHolder((Player) cs); + if (valueHolder == null) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Es ist einer interner Fehler aufgetreten (0)"); + return true; + } + ThirstPlayer thirstPlayer = valueHolder.getPlayer(ThirstPlayer.class); + TemperaturePlayer temperaturePlayer = valueHolder.getPlayer(TemperaturePlayer.class); + if (thirstPlayer == null || temperaturePlayer == null) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Es ist einer interner Fehler aufgetreten (1)"); + return true; + } + Float value; + try { + value = Float.parseFloat(args[1]); + } catch (NumberFormatException e) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "%s ist keine Zahl!", args[1]); + return true; + } + switch (args[0].toLowerCase()) { + case "durst": + if (thirstPlayer.getMIN() > value || thirstPlayer.getMAX() < value) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.SUCCESS, "Der Wert liegt außerhalb des Wertebereich."); + return true; + } + thirstPlayer.setValue(value); + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.SUCCESS, "Der Durst wurde auf %i gesetzt!", value); + return true; + case "temp": + case "temperatur": + if (temperaturePlayer.getMIN() > value || temperaturePlayer.getMAX() < value) { + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.SUCCESS, "Der Wert liegt außerhalb des Wertebereich."); + return true; + } + temperaturePlayer.setValue(value); + Msg.send(cs, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.SUCCESS, "Die Temperatur wurde auf %i gesetzt!", value); + return true; + default: + return false; + } + } + + @Override + public List onTabComplete(CommandSender cs, Command cmnd, String label, String[] args) { + List list = new ArrayList<>(); + switch (args.length) { + case 1: + list.add("durst"); + list.add("temp"); + break; + case 2: + return list; + } + return list.stream().filter((s) -> s.startsWith(args[args.length - 1])).collect(Collectors.toList()); + } +} diff --git a/src/de/anura/realisticminecraft/fishing/FishingChunk.java b/src/de/anura/realisticminecraft/fishing/FishingChunk.java new file mode 100644 index 0000000..aec34d5 --- /dev/null +++ b/src/de/anura/realisticminecraft/fishing/FishingChunk.java @@ -0,0 +1,135 @@ +package de.anura.realisticminecraft.fishing; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import de.anura.core.AnuraThread; +import de.anura.core.database.DB; +import java.sql.ResultSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import de.anura.realisticminecraft.RealisticMinecraft; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; + +public class FishingChunk { + + private static final double CHUNK_REGENERATION = 9.0 / 60.0; // 1 per 7 minutes + private static final int MAX_FISHES = 15; // within chunk + + ////////////////// + private int id; + private final Chunk chunk; + private double catched; + private long lastUpdated; + + public FishingChunk(Chunk chunk) { + this(chunk, 0, System.currentTimeMillis() / 1000, -1); + } + + public FishingChunk(Chunk chunk, int catched, long lastUpdated, int id) { + this.id = id; + this.chunk = chunk; + this.catched = catched; + this.lastUpdated = lastUpdated; + } + + public Chunk getChunk() { + return chunk; + } + + private void update() { + long notUpdatedTime = System.currentTimeMillis() / 1000 - lastUpdated; + long minutes = notUpdatedTime / 60; + + catched -= minutes * CHUNK_REGENERATION; + if (catched < 0) { + catched = 0; + } + + this.lastUpdated = System.currentTimeMillis() / 1000 - (notUpdatedTime % 60); + } + + private int getFishes() { + update(); + int fishes = MAX_FISHES - (int) Math.ceil(catched); + if (fishes < 0) { + fishes = 0; + } + return fishes; + } + + public boolean isOverfished() { + return getFishes() == 0; + } + + public void addCatched() { + catched += 1; + save(true); + } + + public void save(boolean async) { + update(); + if (id <= 0) { + DB.queryUpdate(async, DB.getFirstKey((key) -> { + id = key; + }), "INSERT INTO fishingChunks (x, z, world, catched, lastUpdated) VALUES (?, ?, ?, ?, ?)", + chunk.getX(), chunk.getZ(), chunk.getWorld().getName(), catched, lastUpdated); + } else { + DB.queryUpdate(async, "UPDATE fishingChunks SET catched = ?, lastUpdated = ? WHERE primeKey = ?", + catched, lastUpdated, id); + } + } + + ///////////////////// + private static LoadingCache FISHING_CACHE; + private static final List tasks = Collections.synchronizedList(new LinkedList<>()); + + public static void init() { + CacheLoader loader = new CacheLoader() { + @Override + public FishingChunk load(Chunk chunk) throws Exception { + ResultSet rs = DB.querySelect( + "SELECT * FROM fishingChunks WHERE x = ? AND z = ? AND world = ?", + chunk.getX(), chunk.getZ(), chunk.getWorld().getName() + ); + if (!rs.first()) { + return new FishingChunk(chunk); + } + return new FishingChunk(chunk, rs.getInt("catched"), rs.getInt("lastUpdated"), rs.getInt("primeKey")); + } + }; + + FISHING_CACHE = CacheBuilder.newBuilder() + .expireAfterAccess(1, TimeUnit.HOURS) + .build(loader); + + AnuraThread.add(Bukkit.getScheduler().runTaskTimerAsynchronously(RealisticMinecraft.getInstance(), () -> { + synchronized (tasks) { + Iterator it = tasks.iterator(); + while (it.hasNext()) { + FishingTask task = it.next(); + if (task.getTimestamp() < System.currentTimeMillis()) { + task.getR().run(); + it.remove(); + } + } + } + }, 2, 2)); + } + + public static FishingChunk get(Chunk chunk) { + return FISHING_CACHE.getUnchecked(chunk); + } + + public static boolean contains(Chunk chunk) { + return FISHING_CACHE.getIfPresent(chunk) != null; + } + + public static void queueTask(int secounds, Runnable r) { + tasks.add(new FishingTask(System.currentTimeMillis() + (secounds * 1000), r)); + } +} diff --git a/src/de/anura/realisticminecraft/fishing/FishingTask.java b/src/de/anura/realisticminecraft/fishing/FishingTask.java new file mode 100644 index 0000000..04b00ec --- /dev/null +++ b/src/de/anura/realisticminecraft/fishing/FishingTask.java @@ -0,0 +1,21 @@ + +package de.anura.realisticminecraft.fishing; + +public class FishingTask { + + private final long timestamp; + private final Runnable r; + + public FishingTask(long timestamp, Runnable r) { + this.timestamp = timestamp; + this.r = r; + } + + public long getTimestamp() { + return timestamp; + } + + public Runnable getR() { + return r; + } +} diff --git a/src/de/anura/realisticminecraft/infobar/BarStatus.java b/src/de/anura/realisticminecraft/infobar/BarStatus.java new file mode 100644 index 0000000..633f773 --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/BarStatus.java @@ -0,0 +1,6 @@ +package de.anura.realisticminecraft.infobar; + +public enum BarStatus { + STANDARD, + AN; +} diff --git a/src/de/anura/realisticminecraft/infobar/Infobar.java b/src/de/anura/realisticminecraft/infobar/Infobar.java new file mode 100644 index 0000000..b99d8d8 --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/Infobar.java @@ -0,0 +1,79 @@ +package de.anura.realisticminecraft.infobar; + +import de.anura.core.AnuraThread; +import java.util.Objects; +import de.anura.realisticminecraft.RealisticMinecraft; +import org.bukkit.Bukkit; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.scheduler.BukkitTask; + +public abstract class Infobar { + + protected final T player; + protected final BossBar bossbar; + private BarStatus barstatus; + private byte displaytime = 0; + private BukkitTask task = null; + + public Infobar(T p, String title, BarColor color, BarStyle style, BarStatus status) { + Objects.requireNonNull(p); + Objects.requireNonNull(status); + player = p; + bossbar = Bukkit.createBossBar(title, color, style); + bossbar.addPlayer(player.getPlayer()); + barstatus = status; + update(); + } + + public BarStatus getBarStatus() { + return barstatus; + } + + public void setBarStatus(BarStatus status) { + Objects.requireNonNull(status); + barstatus = status; + update(); + player.updateDatabase(true); + } + + private void setBarVisible(boolean visible) { + if (stayCondition()) { + bossbar.setVisible(true); + } else { + bossbar.setVisible(visible); + } + } + + protected void destroy() { + if (bossbar.getPlayers().contains(player.getPlayer())) { + bossbar.removePlayer(player.getPlayer()); + } + task.cancel(); + } + + public void update() { + bossbar.setProgress((player.getValue() - player.getMIN()) / (player.getMAX() - player.getMIN())); + setBarVisible(true); + startCountdown((byte) 5); + } + + protected boolean stayCondition() { + return getBarStatus().equals(BarStatus.AN); + } + + protected void startCountdown(byte time) { + displaytime = time; + if (task == null) { + task = Bukkit.getScheduler().runTaskTimerAsynchronously(RealisticMinecraft.getInstance(), () -> { + if (displaytime > 0) { + displaytime--; + } else { + setBarVisible(false); + } + }, 20, 20); + AnuraThread.add(task); + } + } +} diff --git a/src/de/anura/realisticminecraft/infobar/InfobarUtil.java b/src/de/anura/realisticminecraft/infobar/InfobarUtil.java new file mode 100644 index 0000000..caa9e8a --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/InfobarUtil.java @@ -0,0 +1,74 @@ +package de.anura.realisticminecraft.infobar; + +import de.anura.core.AnuraThread; +import de.anura.core.msg.Message; +import de.anura.realisticminecraft.RealisticMinecraft; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.plugin.java.JavaPlugin; + +public class InfobarUtil { + + private static int round = 1; + + public static void enableInfobarTasks() { + JavaPlugin instance = RealisticMinecraft.getInstance(); + AnuraThread.add(Bukkit.getScheduler().runTaskTimerAsynchronously(instance, () -> { + Bukkit.getOnlinePlayers().stream().filter((p) -> (p.getGameMode().equals(GameMode.SURVIVAL) && !p.isDead())).forEach((p) -> { + ValueHolder vh = ValueHolder.getValueHolder(p); + if (vh == null) { + return; + } + ThirstPlayer thP = vh.getPlayer(ThirstPlayer.class); + if (thP != null && vh.getPlayer(TemperaturePlayer.class) != null) { + thP.calculateNewValue(p.getLocation()); + } + if (round == 5) { + TemperaturePlayer tempP = vh.getPlayer(TemperaturePlayer.class); + if (tempP != null) { + tempP.calculateNewValue(p.getLocation()); + if (tempP.isMIN()) { + Message.sendActionBar(p, ChatColor.RED + "Dir ist zu kalt!"); + AnuraThread.queueSync(() -> p.damage(0.5)); + } else if (tempP.isMAX()) { + Message.sendActionBar(p, ChatColor.RED + "Dir ist zu heiß!"); + AnuraThread.queueSync(() -> p.damage(0.5)); + } + } + if (thP != null) { + if (thP.getValue() == 0) { + Message.sendActionBar(p, ChatColor.RED + "Du bist durstig, trinke etwas!"); + if (p.getHealth() - 0.5 <= 0) { + } + AnuraThread.queueSync(() -> p.damage(0.5)); + } else if (thP.isThirsty() || thP.isWeak()) { + Message.sendActionBar(p, ChatColor.YELLOW + "Du bist durstig, trinke etwas!"); + } + } + } + }); + if (round == 5) { + round = 0; + } + round++; + }, 20, 20)); + + AnuraThread.add(Bukkit.getScheduler().runTaskTimerAsynchronously(instance, () -> { + Bukkit.getOnlinePlayers().stream().filter((p) -> (p.getGameMode().equals(GameMode.SURVIVAL) && !p.isDead())).forEach((p) -> { + ValueHolder vh = ValueHolder.getValueHolder(p); + if (vh == null) { + return; + } + TemperaturePlayer tmp = vh.getPlayer(TemperaturePlayer.class); + if (tmp != null) { + tmp.updateDatabase(false); + } + ThirstPlayer thp = vh.getPlayer(ThirstPlayer.class); + if (thp != null) { + thp.updateDatabase(false); + } + }); + }, 20 * 60, 20 * 60)); + } +} diff --git a/src/de/anura/realisticminecraft/infobar/RealisticPlayer.java b/src/de/anura/realisticminecraft/infobar/RealisticPlayer.java new file mode 100644 index 0000000..9cec3bd --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/RealisticPlayer.java @@ -0,0 +1,101 @@ +package de.anura.realisticminecraft.infobar; + +import de.anura.core.database.DB; +import java.util.Objects; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public abstract class RealisticPlayer { + + protected final short MIN; + protected final short MAX; + protected final short DEFAULT; + protected float value; + protected final Player player; + protected Infobar bar; + + protected RealisticPlayer(Player p, float value, short MIN, short MAX, short DEFAULT) { + Objects.requireNonNull(p); + player = p; + this.MIN = MIN; + this.MAX = MAX; + this.DEFAULT = DEFAULT; + if (value == -1) { + this.value = DEFAULT; + } else { + this.value = value; + } + } + + public float getValue() { + return ((int) (value * 10)) / 10f; + } + + public float setValue(float value) { + boolean updated = this.value != value; + if (updated) { + changeValue(value); + if (value > MAX) { + this.value = MAX; + } else if (value < MIN) { + this.value = MIN; + } else { + this.value = value; + } + bar.update(); + } + return getValue(); + } + + public float addValue(float modifier) { + if (blockmodify() || modifier == 0) { + return getValue(); + } + return setValue(value + modifier); + } + + public Player getPlayer() { + return player; + } + + public short getMIN() { + return MIN; + } + + public boolean isMIN() { + return value == MIN; + } + + public short getMAX() { + return MAX; + } + + public boolean isMAX() { + return value == MAX; + } + + public short getDEFAULT() { + return DEFAULT; + } + + public boolean isExtremeValue() { + return isMIN() || isMAX(); + } + + public Infobar getBar() { + return bar; + } + + protected void changeValue(float newValue) { + } + + protected abstract boolean blockmodify(); + + protected abstract float calculateNewValue(Location l); + + protected void updateDatabase(boolean async) { + DB.queryUpdate(async, "INSERT INTO " + getTableName() + " (`playerId`, `value`, `bar`) VALUES((SELECT id FROM players WHERE uuid = ?),?,?) ON DUPLICATE KEY UPDATE value = ?, bar = ?", player.getUniqueId().toString(), value, bar.getBarStatus().ordinal(), value, bar.getBarStatus().ordinal()); + } + + public abstract String getTableName(); +} diff --git a/src/de/anura/realisticminecraft/infobar/TemperatureBar.java b/src/de/anura/realisticminecraft/infobar/TemperatureBar.java new file mode 100644 index 0000000..ed4e7d0 --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/TemperatureBar.java @@ -0,0 +1,29 @@ +package de.anura.realisticminecraft.infobar; + +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; + +public class TemperatureBar extends Infobar { + + public TemperatureBar(TemperaturePlayer p, BarStatus status) { + super(p, "Temperatur", BarColor.GREEN, BarStyle.SOLID, status); + } + + @Override + public void update() { + super.update(); + bossbar.setTitle("Temperatur (" + player.getValue() + "°M)"); + if (player.isExtremeValue()) { + bossbar.setColor(BarColor.RED); + } else if (player.isWarning()) { + bossbar.setColor(BarColor.YELLOW); + } else { + bossbar.setColor(BarColor.GREEN); + } + } + + @Override + protected boolean stayCondition() { + return super.stayCondition() || player.isWarning(); + } +} diff --git a/src/de/anura/realisticminecraft/infobar/TemperatureModifier.java b/src/de/anura/realisticminecraft/infobar/TemperatureModifier.java new file mode 100644 index 0000000..66dfff1 --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/TemperatureModifier.java @@ -0,0 +1,221 @@ +package de.anura.realisticminecraft.infobar; + +import de.anura.core.selections.CuboidSelection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.Furnace; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +public abstract class TemperatureModifier { + + private static final float RAIN_MOD = -0.075f; + private static final float WATER_MOD = -0.5f; + private static final float FIRE_MOD = 0.5f; + private static final float WARM_MOD = 0.2f; + + public static final float DRINK_MOD = -1f; + public static final float EAT_MOD = 2f; + + public static float getModifier(Player p, Location l, TemperaturePlayer tp) { + float modifier = Biome.getModifier(l.getBlock()); + if (modifier == 0) { + if (tp.getValue() < tp.getDEFAULT()) { + modifier += 0.1f; + } else if (tp.getValue() > tp.getDEFAULT()) { + modifier += -0.1f; + } + } else if (modifier > 0) { + World w = l.getWorld(); + if (w != null && w.hasStorm() && tp.getValue() > tp.getDEFAULT() && tp.isWarning()) { + modifier = 0; + } + } + modifier += getRainModifier(l); + modifier += getWaterModifier(l, tp); + modifier += getFireModifier(p); + modifier += getLavaModifer(l); + modifier += getWarmModifier(l); + modifier += Equipment.getModifier(p.getEquipment(), modifier); +// if (TheTownAPI.isInWarriors() && WarriorsProvider.isNomaden(p)) { +// if (tp.getValue() > tp.getDEFAULT() && modifier > 0 +// || tp.getValue() < tp.getDEFAULT() && modifier < 0) { +// modifier *= 0.25f; +// } +// } + return modifier; + } + + private static float getRainModifier(Location l) { + World w = l.getWorld(); + return w != null && w.hasStorm() && l.getBlock().getRelative(BlockFace.UP).getLightFromSky() == 0xF ? RAIN_MOD : 0; + } + + private static float getWaterModifier(Location l, TemperaturePlayer tp) { + if (l.getWorld() != null) { + Block b = l.getBlock(); + if (b.getTemperature() >= 0.9f && tp.getValue() < tp.getDEFAULT()) { + return 0f; + } + return b.getType().equals(Material.WATER) ? WATER_MOD : 0; + } + return 0f; + } + + private static float getFireModifier(Player p) { + return p.getFireTicks() > 0 ? FIRE_MOD : 0; + } + + private static float getLavaModifer(Location l) { + return new CuboidSelection(l.getBlock(), 3).getCount(Material.LAVA) > 0 ? FIRE_MOD : 0; + } + + private static float getWarmModifier(Location l) { + CuboidSelection sel = new CuboidSelection(l.getBlock(), 3); + ArrayList blocks = sel.getBlocks(Material.FURNACE); + boolean lit_furnace = false; + for (Block b : blocks) { + BlockData bd = b.getBlockData(); + if (bd instanceof Furnace) { + Furnace f = (Furnace) bd; + if (f.isLit()) { + lit_furnace = true; + } + } + } + return lit_furnace || sel.getCount(Material.FIRE) > 0 ? WARM_MOD : 0; + } + + private static enum Biome { + COLD(-1f, 0.5f, 1.5f, 1f, -0.05f, 0.5f), + WARM(0.9f, 1f, 1.5f, 1f, 0.05f, 1f), + HOT(1f, 2f, 2f, 1f, 0.05f, 1.5f); + + private final float minTemp, maxTemp, daySkalar, nightSkalar, modifier, sunModifier; + + private Biome(float min, float max, float day, float night, float modifier, float sun) { + minTemp = min; + maxTemp = max; + daySkalar = day; + nightSkalar = night; + this.modifier = modifier; + sunModifier = sun; + } + + public float getMinTemp() { + return minTemp; + } + + public float getMaxTemp() { + return maxTemp; + } + + public float getDaySkalar() { + return daySkalar; + } + + public float getNightSkalar() { + return nightSkalar; + } + + public float getModifier() { + return modifier; + } + + public float getSunModifier() { + return sunModifier; + } + + public static float getModifier(Block b) { + float modifier = 0; + for (Biome biome : values()) { + if (b.getTemperature() >= biome.getMinTemp() && b.getTemperature() <= biome.getMaxTemp()) { + if (isDay(b)) { + if (b.getLightFromSky() == 0xF && !b.getWorld().hasStorm()) { + modifier += biome.getModifier() * biome.getDaySkalar() * biome.getSunModifier(); + } else { + modifier += biome.getModifier() * biome.getDaySkalar(); + } + } else { + modifier += biome.getModifier() * biome.getNightSkalar(); + } + break; + } + } + return modifier; + } + + public static boolean isDay(Block b) { + return b.getWorld().getTime() > 500 && b.getWorld().getTime() < 11500; + } + } + + private static enum Equipment { + LEATHER_BHL(5, Material.LEATHER_BOOTS, Material.LEATHER_HELMET, Material.LEATHER_LEGGINGS), + LEATHER_C(15, Material.LEATHER_CHESTPLATE), + IRON_BHL(7.5f, Material.IRON_BOOTS, Material.IRON_HELMET, Material.IRON_LEGGINGS), + IRON_C(17.5f, Material.IRON_CHESTPLATE), + GOLD_BHL(10, Material.GOLDEN_BOOTS, Material.GOLDEN_HELMET, Material.GOLDEN_LEGGINGS), + GOLD_C(20, Material.GOLDEN_CHESTPLATE), + DIAMOND_BHL(12.5f, Material.DIAMOND_BOOTS, Material.DIAMOND_HELMET, Material.DIAMOND_LEGGINGS), + DIAMOND_C(22.5f, Material.DIAMOND_CHESTPLATE); + + private Equipment(float percentage, Material... mat) { + this.percentage = percentage; + this.material = Arrays.asList(mat); + } + + private final List material; + private final float percentage; + + private boolean contains(Material m) { + return material.contains(m); + } + + private float getPercentage() { + return percentage; + } + + private float getModifier(float tempModifier) { + return tempModifier * getPercentage() / 100; + } + + private static float getModifier(Material m, float tempModifier) { + for (Equipment e : values()) { + if (e.contains(m)) { + return e.getModifier(tempModifier); + } + } + return 0; + } + + public static float getModifier(EntityEquipment ee, float tempModifier) { + float modifier = 0; + ItemStack boots = ee.getBoots(); + if (boots != null) { + modifier += getModifier(boots.getType(), tempModifier); + } + ItemStack chestplate = ee.getChestplate(); + if (chestplate != null) { + modifier += getModifier(chestplate.getType(), tempModifier); + } + ItemStack helmet = ee.getHelmet(); + if (helmet != null) { + modifier += getModifier(helmet.getType(), tempModifier); + } + ItemStack leggins = ee.getLeggings(); + if (leggins != null) { + modifier += getModifier(leggins.getType(), tempModifier); + } + return modifier * -1; + } + } +} diff --git a/src/de/anura/realisticminecraft/infobar/TemperaturePlayer.java b/src/de/anura/realisticminecraft/infobar/TemperaturePlayer.java new file mode 100644 index 0000000..f920057 --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/TemperaturePlayer.java @@ -0,0 +1,67 @@ +package de.anura.realisticminecraft.infobar; + +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class TemperaturePlayer extends RealisticPlayer { + + private static final short MIN_TEMP_WARNING = 20; + private static final short MAX_TEMP_WARNING = 40; + private static final short MIN_TEMP = 15; + private static final short MAX_TEMP = 45; + private static final short DEFAULT_TEMP = 30; + + private static final short MIN_TEMP_DRINK = 30; + private static final short MAX_TEMP_EAT = 35; + + public TemperaturePlayer(Player p) { + this(p, -1, BarStatus.STANDARD); + } + + public TemperaturePlayer(Player p, float temp, BarStatus status) { + super(p, temp, MIN_TEMP, MAX_TEMP, DEFAULT_TEMP); + bar = new TemperatureBar(this, status); + } + + public boolean isWarning() { + return value >= MAX_TEMP_WARNING || value <= MIN_TEMP_WARNING; + } + + @Override + public float calculateNewValue(Location l) { + float modifier = TemperatureModifier.getModifier(player, l, this); + float returnField = modifier == 0 ? getValue() : addValue(modifier); + if (value >= MAX_TEMP_WARNING) { + ValueHolder vh = ValueHolder.getValueHolder(player); + if (vh != null) { + ThirstPlayer tp = vh.getPlayer(ThirstPlayer.class); + if (tp != null) { + tp.addValue(-1); + } + } + } + return returnField; + } + + public float drink() { + return value > MIN_TEMP_DRINK ? addValue(TemperatureModifier.DRINK_MOD) : getValue(); + } + + public float eatSoup() { + return value < MAX_TEMP_EAT ? addValue(TemperatureModifier.EAT_MOD) : getValue(); + } + + @Override + protected boolean blockmodify() { + return false; + } + + @Override + public String getTableName() { + return getStaticTableName(); + } + + public static String getStaticTableName() { + return "playerTemperature"; + } +} diff --git a/src/de/anura/realisticminecraft/infobar/ThirstBar.java b/src/de/anura/realisticminecraft/infobar/ThirstBar.java new file mode 100644 index 0000000..4707991 --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/ThirstBar.java @@ -0,0 +1,16 @@ +package de.anura.realisticminecraft.infobar; + +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; + +public class ThirstBar extends Infobar { + + public ThirstBar(ThirstPlayer p, BarStatus status) { + super(p, "Durst", BarColor.BLUE, BarStyle.SOLID, status); + } + + @Override + protected boolean stayCondition() { + return super.stayCondition() || player.isNoSprint(); + } +} diff --git a/src/de/anura/realisticminecraft/infobar/ThirstPlayer.java b/src/de/anura/realisticminecraft/infobar/ThirstPlayer.java new file mode 100644 index 0000000..f01adac --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/ThirstPlayer.java @@ -0,0 +1,118 @@ +package de.anura.realisticminecraft.infobar; + +import de.anura.core.tools.Potions; +import de.anura.core.tools.Potions.CustomPotion; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class ThirstPlayer extends RealisticPlayer { + + private static final CustomPotion POTION_THIRSTY = new CustomPotion(new PotionEffect(PotionEffectType.CONFUSION, 0, 0), true); + private static final CustomPotion POTION_WEAK_I = new CustomPotion(new PotionEffect(PotionEffectType.WEAKNESS, 0, 0), true); + private static final CustomPotion POTION_WEAK_II = new CustomPotion(new PotionEffect(PotionEffectType.SLOW, 0, 0), true); + + private static final short THIRSTY = 5; + private static final short WEAK = 10; + private static final short NO_SPRINT = 20; + + private static final short MIN_V = 0; + private static final short MAX_V = 100; + + private static final float BASIC_MOD = -0.015f; + private static final float DRINK_MOD = 10f; + private static final float RAIN_MOD = 1f; + private static final float FOODLEVEL_MOD = -1f; + + private static final float NOMADEN_MOD = 0.25f; + + public ThirstPlayer(Player p) { + this(p, -1, BarStatus.STANDARD); + } + + public ThirstPlayer(Player p, float thirst, BarStatus status) { + super(p, thirst, MIN_V, MAX_V, MAX_V); + bar = new ThirstBar(this, status); + } + + @Override + protected boolean blockmodify() { + return false; + } + + public boolean isThirsty() { + return value <= THIRSTY; + } + + public boolean isWeak() { + return value <= WEAK; + } + + public boolean isNoSprint() { + return value <= NO_SPRINT; + } + + @Override + public float calculateNewValue(Location l) { + float mod = BASIC_MOD; +// if (TheTownAPI.isInWarriors() && WarriorsProvider.isNomaden(player)) { +// mod *= NOMADEN_MOD; +// } + World w = l.getWorld(); + if (w != null && w.hasStorm() && l.getPitch() < -75 && l.getBlock().getRelative(BlockFace.UP).getLightFromSky() == 0xF) { + mod += RAIN_MOD; + } + return addValue(mod); + } + + @Override + protected void changeValue(float newValue) { + if (!isWeak() && newValue <= WEAK) { + Potions.addCustomPotion(player, POTION_WEAK_I); + Potions.addCustomPotion(player, POTION_WEAK_II); + } else if (isWeak() && newValue > WEAK) { + Potions.removeCustomPotion(player, POTION_WEAK_I); + Potions.removeCustomPotion(player, POTION_WEAK_II); + } + if (!isThirsty() && newValue <= THIRSTY) { + Potions.addCustomPotion(player, POTION_THIRSTY); + } else if (isThirsty() && newValue > THIRSTY) { + Potions.removeCustomPotion(player, POTION_THIRSTY); + } + } + + public float drink() { + return addValue(DRINK_MOD); + } + + public float foodLevelChange() { +// if (TheTownAPI.isInWarriors() && WarriorsProvider.isNomaden(player)) { +// return addValue(FOODLEVEL_MOD * NOMADEN_MOD); +// } + return addValue(FOODLEVEL_MOD); + } + + @Override + public String getTableName() { + return getStaticTableName(); + } + + public static String getStaticTableName() { + return "playerThirst"; + } + + public static boolean isOcean(Biome b) { + switch (b) { + case OCEAN: + case DEEP_OCEAN: + case FROZEN_OCEAN: + return true; + default: + return false; + } + } +} diff --git a/src/de/anura/realisticminecraft/infobar/ValueHolder.java b/src/de/anura/realisticminecraft/infobar/ValueHolder.java new file mode 100644 index 0000000..143a1ad --- /dev/null +++ b/src/de/anura/realisticminecraft/infobar/ValueHolder.java @@ -0,0 +1,90 @@ +package de.anura.realisticminecraft.infobar; + +import de.anura.core.database.DB; +import java.lang.reflect.InvocationTargetException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class ValueHolder { + + private static final List> PLAYERS = Arrays.asList(TemperaturePlayer.class, ThirstPlayer.class); + private static final Map ALL = new ConcurrentHashMap<>(); + private final Map, RealisticPlayer> values = new ConcurrentHashMap<>(); + private final Player player; + + public ValueHolder(Player p) { + Objects.requireNonNull(p); + ALL.put(p, this); + player = p; + PLAYERS.forEach(this::createPlayer); + } + + public Player getPlayer() { + return player; + } + + @SuppressWarnings("unchecked") + @Nullable + public T getPlayer(Class clazz) { + return (T) values.get(clazz); + } + + public Collection getPlayersList() { + return values.values(); + } + + private void createPlayer(Class clazz) { + ResultSet rs = null; + try { + rs = DB.querySelect("SELECT value, bar FROM " + clazz.getDeclaredMethod("getStaticTableName").invoke(null) + " WHERE playerId = (SELECT id FROM players WHERE uuid = ?)", player.getUniqueId().toString()); + if (rs.next()) { + T rp = clazz.getConstructor(Player.class, float.class, BarStatus.class).newInstance(player, rs.getFloat("value"), BarStatus.values()[rs.getInt("bar")]); + values.put(clazz, rp); + } else { + values.put(clazz, clazz.getConstructor(Player.class).newInstance(player)); + } + } catch (SQLException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException ex) { + Logger.getLogger(ValueHolder.class.getName()).log(Level.SEVERE, null, ex); + } finally { + DB.closeRessources(rs); + } + } + + private void resetPlayer(Class clazz) { + values.get(clazz).setValue(values.get(clazz).getDEFAULT()); + } + + public void resetHoldings() { + PLAYERS.forEach(this::resetPlayer); + } + + @Nullable + public static ValueHolder getValueHolder(Player p) { + return ALL.get(p); + } + + public static void removeValueHolder(Player p) { + remove(ALL.remove(p)); + } + + public static void destroyAll() { + ALL.values().forEach(ValueHolder::remove); + ALL.clear(); + } + + private static void remove(ValueHolder vh) { + if (vh != null) { + vh.getPlayersList().forEach((rp) -> { + rp.updateDatabase(false); + rp.getBar().destroy(); + rp.changeValue(rp.getDEFAULT()); + }); + } + } +} diff --git a/src/de/anura/realisticminecraft/listener/Chairs.java b/src/de/anura/realisticminecraft/listener/Chairs.java new file mode 100644 index 0000000..e742635 --- /dev/null +++ b/src/de/anura/realisticminecraft/listener/Chairs.java @@ -0,0 +1,105 @@ +package de.anura.realisticminecraft.listener; + +import de.anura.core.events.AnuraLeaveEvent; +import de.anura.core.msg.Msg; +import de.anura.realisticminecraft.RealisticMinecraft; +import de.anura.realisticminecraft.util.ChairManager; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.Bisected.Half; +import org.bukkit.block.data.type.Stairs; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.spigotmc.event.entity.EntityDismountEvent; + +public class Chairs implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent e) { + if (e.getBlock().getBlockData() instanceof Stairs && ChairManager.isSittingAnyone(e.getBlock())) { + ChairManager.playerStandUp(e.getBlock()); + } + } + + @EventHandler + public void onDismount(EntityDismountEvent e) { + if (e.getEntity() instanceof Player && e.getDismounted() instanceof ArmorStand) { + Player p = (Player) e.getEntity(); + if (ChairManager.isSitting(p)) { + ChairManager.playerStandUp(p); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBlockInteract(PlayerInteractEvent e) { + final Player p = e.getPlayer(); + EquipmentSlot eq = e.getHand(); + if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && !p.isSneaking() && eq != null && eq.equals(EquipmentSlot.HAND)) { + final Block b = e.getClickedBlock(); + if (b != null && b.getBlockData() instanceof Stairs) { + Stairs s = (Stairs) b.getBlockData(); + if (s.getHalf().equals(Half.BOTTOM)) { + e.setCancelled(true); + Material above = b.getLocation().add(0, 1, 0).getBlock().getType(); + if (!cloudAbove(above) && above.isSolid()) { + Msg.send(p, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Da ist kein Platz zum Sitzen!"); + } else if (ChairManager.isSittingAnyone(b)) { + if (!ChairManager.isSittingPlayer(b, p)) { + Msg.send(p, RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, "Hier sitzt bereits jemand!"); + } + } else if (ChairManager.isSitting(p)) { + ChairManager.playerStandUp(p); + ChairManager.playerSitDown(p, s, b); + } else { + ChairManager.playerSitDown(p, s, b); + } + } + } + } + } + + @EventHandler + public void onPlayerLeave(AnuraLeaveEvent e) { + if (ChairManager.isSitting(e.getPlayer())) { + ChairManager.playerStandUp(e.getPlayer()); + } + } + + private static boolean cloudAbove(Material m) { + switch(m) { + case BLACK_WALL_BANNER: + case BLUE_WALL_BANNER: + case BROWN_WALL_BANNER: + case CYAN_WALL_BANNER: + case GRAY_WALL_BANNER: + case GREEN_WALL_BANNER: + case LIGHT_BLUE_WALL_BANNER: + case LIGHT_GRAY_WALL_BANNER: + case LIME_WALL_BANNER: + case MAGENTA_WALL_BANNER: + case ORANGE_WALL_BANNER: + case PINK_WALL_BANNER: + case PURPLE_WALL_BANNER: + case RED_WALL_BANNER: + case WHITE_WALL_BANNER: + case YELLOW_WALL_BANNER: + case ACACIA_WALL_SIGN: + case BIRCH_WALL_SIGN: + case DARK_OAK_WALL_SIGN: + case JUNGLE_WALL_SIGN: + case OAK_WALL_SIGN: + case SPRUCE_WALL_SIGN: + return true; + default: + return false; + } + } +} diff --git a/src/de/anura/realisticminecraft/listener/Fishing.java b/src/de/anura/realisticminecraft/listener/Fishing.java new file mode 100644 index 0000000..e7fda26 --- /dev/null +++ b/src/de/anura/realisticminecraft/listener/Fishing.java @@ -0,0 +1,67 @@ +package de.anura.realisticminecraft.listener; + +import de.anura.core.msg.Message; +import de.anura.core.msg.Msg; +import de.anura.realisticminecraft.RealisticMinecraft; +import de.anura.realisticminecraft.fishing.FishingChunk; +import org.bukkit.ChatColor; +import org.bukkit.Chunk; +import org.bukkit.entity.FishHook; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerFishEvent; + +public class Fishing implements Listener { + + private static final String FAILED_HOOK_NAME = ChatColor.DARK_RED + "✖✖✖"; + + @EventHandler(ignoreCancelled = true) + public void onFish(PlayerFishEvent ev) { + + FishHook hook = ev.getHook(); + Chunk chunk = hook.getLocation().getChunk(); + + + switch (ev.getState()) { + case FISHING: { + if (!FishingChunk.contains(chunk)) { + FishingChunk.queueTask(0, () -> FishingChunk.get(chunk)); + } + // Wir warten 5 Sekunden, da dieser State (FISHING) direkt aufgerufen wird, wenn man die Angel auswirft. + FishingChunk.queueTask(5 , () -> { + if (hook.isDead() || !hook.isValid()) return; + + FishingChunk newHookChunk = FishingChunk.get(hook.getLocation().getChunk()); + if (!newHookChunk.isOverfished()) return; + + Message.sendActionBar(ev.getPlayer(), Msg.getMsg(RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, true, "Dieser Teil des Gewässer scheint überfischt zu sein!")); + + hook.setCustomName(FAILED_HOOK_NAME); + hook.setCustomNameVisible(true); + }); + break; + } + case BITE: { + FishingChunk fishingChunk = FishingChunk.get(chunk); + if (fishingChunk.isOverfished()) { + Message.sendActionBar(ev.getPlayer(), Msg.getMsg(RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, true, "Dieser Teil des Gewässer scheint überfischt zu sein!")); + hook.setCustomName(FAILED_HOOK_NAME); + hook.setCustomNameVisible(true); + ev.setCancelled(true); + } + break; + } + case CAUGHT_FISH: { + FishingChunk fishingChunk = FishingChunk.get(chunk); + if (fishingChunk.isOverfished()) { + Message.sendActionBar(ev.getPlayer(), Msg.getMsg(RealisticMinecraft.PLUGIN_DATA, Msg.MsgType.ERROR, true, "Dieser Teil des Gewässer scheint überfischt zu sein!")); + ev.setCancelled(true); + ev.setExpToDrop(0); + } else { + fishingChunk.addCatched(); + } + break; + } + } + } +} diff --git a/src/de/anura/realisticminecraft/listener/Infobar.java b/src/de/anura/realisticminecraft/listener/Infobar.java new file mode 100644 index 0000000..39f9f30 --- /dev/null +++ b/src/de/anura/realisticminecraft/listener/Infobar.java @@ -0,0 +1,132 @@ +package de.anura.realisticminecraft.listener; + +import de.anura.core.AnuraThread; +import de.anura.core.events.AnuraLeaveEvent; +import de.anura.core.tools.Potions; +import de.anura.core.tools.Potions.CustomPotion; +import de.anura.realisticminecraft.infobar.TemperaturePlayer; +import de.anura.realisticminecraft.infobar.ThirstPlayer; +import de.anura.realisticminecraft.infobar.ValueHolder; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerGameModeChangeEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerToggleSprintEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class Infobar implements Listener { + + private static final CustomPotion POTION_HUNGER_I = new CustomPotion(new PotionEffect(PotionEffectType.SLOW_DIGGING, 0, 0), true); + private static final CustomPotion POTION_HUNGER_II = new CustomPotion(new PotionEffect(PotionEffectType.SLOW, 0, 0), true); + + @EventHandler + public void onDeath(PlayerDeathEvent e) { + if (e.getEntity().getHealth() <= 0) { + AnuraThread.async(() -> { + final ValueHolder vh = ValueHolder.getValueHolder(e.getEntity()); + if (vh != null) { + vh.resetHoldings(); + } + Potions.removeCustomPotion(e.getEntity(), POTION_HUNGER_I); + Potions.removeCustomPotion(e.getEntity(), POTION_HUNGER_II); + }); + } + } + + @EventHandler(ignoreCancelled = true) + public void onItemConsume(PlayerItemConsumeEvent e) { + final ValueHolder vh = ValueHolder.getValueHolder(e.getPlayer()); + if (vh == null) { + return; + } + final TemperaturePlayer tp = vh.getPlayer(TemperaturePlayer.class); + final ThirstPlayer thp = vh.getPlayer(ThirstPlayer.class); + switch (e.getItem().getType()) { + case MILK_BUCKET: + case POTION: + if (tp != null) { + tp.drink(); + } + if (thp != null) { + thp.drink(); + } + break; + case MUSHROOM_STEW: + if (tp != null) { + tp.eatSoup(); + } + break; + } + } + + @EventHandler + public void onFoodLevelChange(FoodLevelChangeEvent e) { + if (e.getEntity() instanceof Player) { + Player p = (Player) e.getEntity(); + if (e.getFoodLevel() < p.getFoodLevel()) { + final ValueHolder vh = ValueHolder.getValueHolder(p); + if (vh != null) { + ThirstPlayer tP = vh.getPlayer(ThirstPlayer.class); + if (tP != null) { + tP.foodLevelChange(); + } + } + } + if (e.getFoodLevel() < 4) { + Potions.addCustomPotion(p, POTION_HUNGER_I); + Potions.addCustomPotion(p, POTION_HUNGER_II); + } else { + Potions.removeCustomPotion(p, POTION_HUNGER_I); + Potions.removeCustomPotion(p, POTION_HUNGER_II); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onLogin(PlayerJoinEvent e) { + if (e.getPlayer().getGameMode().equals(GameMode.SURVIVAL)) { + AnuraThread.async(() -> new ValueHolder(e.getPlayer())); + } + } + + @EventHandler(ignoreCancelled = true) + public void onChangeGameMode(PlayerGameModeChangeEvent e) { + AnuraThread.async(() -> { + if (e.getNewGameMode().equals(GameMode.SURVIVAL)) { + new ValueHolder(e.getPlayer()); + if (e.getPlayer().getFoodLevel() < 4) { + Potions.addCustomPotion(e.getPlayer(), POTION_HUNGER_I); + Potions.addCustomPotion(e.getPlayer(), POTION_HUNGER_II); + } + } else { + ValueHolder.removeValueHolder(e.getPlayer()); + Potions.removeCustomPotion(e.getPlayer(), POTION_HUNGER_I); + Potions.removeCustomPotion(e.getPlayer(), POTION_HUNGER_II); + } + }); + } + + @EventHandler(ignoreCancelled = true) + public void onSprint(PlayerToggleSprintEvent e) { + if (e.isSprinting()) { + ValueHolder vh = ValueHolder.getValueHolder(e.getPlayer()); + if (vh != null) { + ThirstPlayer tp = vh.getPlayer(ThirstPlayer.class); + if (tp != null) { + e.setCancelled(tp.isNoSprint()); + } + } + } + } + + @EventHandler + public void onDisconnect(AnuraLeaveEvent e) { + AnuraThread.async(() -> ValueHolder.removeValueHolder(e.getPlayer())); + } +} diff --git a/src/de/anura/realisticminecraft/listener/Timber.java b/src/de/anura/realisticminecraft/listener/Timber.java new file mode 100644 index 0000000..3fe0b2f --- /dev/null +++ b/src/de/anura/realisticminecraft/listener/Timber.java @@ -0,0 +1,83 @@ +package de.anura.realisticminecraft.listener; + +import de.anura.core.AnuraThread; +import java.util.Map; +import java.util.WeakHashMap; +import de.anura.realisticminecraft.timber.TreeFeller; +import de.anura.realisticminecraft.timber.parser.TreeParser; +import org.bukkit.Axis; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Orientable; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EntityEquipment; + +public class Timber implements Listener { + + private static final Map LAST_INTERACT = new WeakHashMap<>(); + + public static BlockFace getLastInteract(Player p) { + return LAST_INTERACT.get(p); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent e) { + if (isInOwnRegion() == e.getPlayer().isSneaking()) { + return; + } + if (isTimberLog(e.getBlock().getType())) { + EntityEquipment eq = e.getPlayer().getEquipment(); + if (e.getPlayer().getGameMode().equals(GameMode.SURVIVAL) + && eq != null && TreeFeller.isAxe(eq.getItemInMainHand().getType()) + && ((Orientable) e.getBlock().getBlockData()).getAxis().equals(Axis.Y)) { + e.setCancelled(true); + AnuraThread.async(() -> { + TreeParser treeParser = TreeParser.newTreeParser(e.getBlock()); + new TreeFeller(e.getPlayer(), treeParser.parse(getLastInteract(e.getPlayer()))) + .cut(eq.getItemInMainHand()); + }); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBlockInteract(PlayerInteractEvent e) { + final Player p = e.getPlayer(); + if (e.getAction().equals(Action.LEFT_CLICK_BLOCK)) { + if (!p.getGameMode().equals(GameMode.SURVIVAL)) { + return; + } + Block clickedBlock = e.getClickedBlock(); + if (clickedBlock != null && isTimberLog(clickedBlock.getType())) { + LAST_INTERACT.put(p, e.getBlockFace()); + } + } + } + + private static boolean isInOwnRegion() { + return false;//TODO: Get from region management + } + + public static boolean isTimberLog(Material m) { + switch (m) { + case ACACIA_LOG: + case BIRCH_LOG: + case DARK_OAK_LOG: + case JUNGLE_LOG: + case OAK_LOG: + case SPRUCE_LOG: + //@TODO: Check BARK + return true; + default: + return false; + } + } +} diff --git a/src/de/anura/realisticminecraft/timber/Tree.java b/src/de/anura/realisticminecraft/timber/Tree.java new file mode 100644 index 0000000..47ddb1e --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/Tree.java @@ -0,0 +1,88 @@ +package de.anura.realisticminecraft.timber; + +import java.util.HashSet; +import java.util.Random; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; + +public class Tree { + + private final HashSet logList; + private final HashSet leaveList; + private final Block lowestLog; + private Block highestBlock; + private final BlockFace direction; + private final Material wood; + private final Material leave; + private final TreeSpecies species; + + public Tree(Block first, HashSet logs, HashSet leaves, BlockFace fallDirection, Material wood, Material leave, TreeSpecies species) { + this.logList = logs; + this.leaveList = leaves; + this.lowestLog = first; + this.wood = wood; + this.leave = leave; + this.species = species; + if (fallDirection == null || fallDirection.equals(BlockFace.UP) || fallDirection.equals(BlockFace.DOWN)) { + direction = BlockFace.values()[new Random().nextInt(4)]; + } else { + direction = fallDirection.getOppositeFace(); + } + Location loc = first.getLocation(); + loc.setY(0); + highestBlock = loc.getBlock(); + logs.forEach((b) -> { + if (b.getLocation().getY() > highestBlock.getLocation().getY()) { + highestBlock = b; + } + }); + leaves.forEach((b) -> { + if (b.getLocation().getY() > highestBlock.getLocation().getY()) { + highestBlock = b; + } + }); + } + + public HashSet getLogs() { + return logList; + } + + public HashSet getLeaves() { + return leaveList; + } + + public int getHeigth() { + return highestBlock.getLocation().getBlockY() - lowestLog.getLocation().getBlockY(); + } + + public Block getLowestLog() { + return lowestLog; + } + + public Location getLowestLogLoc() { + return lowestLog.getLocation(); + } + + public BlockFace getFallDirection() { + return direction; + } + + public boolean isCuttable() { + return leaveList.size() > 5; + } + + public Material getWood() { + return wood; + } + + public Material getLeave() { + return leave; + } + + public TreeSpecies getSpecies() { + return species; + } +} diff --git a/src/de/anura/realisticminecraft/timber/TreeFeller.java b/src/de/anura/realisticminecraft/timber/TreeFeller.java new file mode 100644 index 0000000..b7b702b --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/TreeFeller.java @@ -0,0 +1,212 @@ +package de.anura.realisticminecraft.timber; + +import de.anura.core.AnuraThread; +import java.util.Random; +import de.anura.realisticminecraft.RealisticMinecraft; +import de.anura.realisticminecraft.timber.event.AppleDropEvent; +import de.anura.realisticminecraft.timber.event.TreeCutEvent; +import de.anura.realisticminecraft.util.LogBlockProvider; +import org.bukkit.Axis; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Orientable; +import org.bukkit.block.data.type.Leaves; +import org.bukkit.entity.FallingBlock; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; + +public class TreeFeller { + + private final Tree t; + private final Player p; + private TreeCutEvent treeCutEvent; + + public TreeFeller(Player p, Tree t) { + this.p = p; + this.t = t; + } + + public void cut(ItemStack axe) { + if (!t.isCuttable()) { + AnuraThread.queueSync(() -> { + t.getLowestLogLoc().getBlock().breakNaturally(axe); + }); + calculateDamage(axe, 1); + p.getInventory().setItemInMainHand(axe); + return; + } + treeCutEvent = new TreeCutEvent(p, t.getLowestLog()); + Bukkit.getPluginManager().callEvent(treeCutEvent); + if (treeCutEvent.isCancelled()) { + return; + } + calculateDamage(axe, 5); + p.getInventory().setItemInMainHand(axe); + World w = t.getLowestLogLoc().getWorld(); + if (w != null) { + w.playSound(t.getLowestLogLoc(), Sound.BLOCK_GRASS_BREAK, 1, 0.1f); + } + for (int i = 0; i <= t.getHeigth(); i++) { + cutLog(i); + cutLeaves(i); + } + } + + private void cutLog(int stage) { + BlockData log = getLogOrientation(); + t.getLogs().stream().filter(b -> b.getLocation().getBlockY() == t.getLowestLogLoc().getBlockY() + stage).forEach(b -> { + AnuraThread.queueSync(() -> { + queueBlockBreak(p, b); + b.setType(Material.AIR); + Location pLocation = getPossibleLocation(b, stage); + FallingBlock spawnFallingBlock = b.getWorld().spawnFallingBlock(pLocation.add(0.5, 0, 0.5), log); + spawnFallingBlock.setDropItem(false); + queueFallingBlock(p, pLocation, log); + }); + }); + } + + private void cutLeaves(int stage) { + BlockData ld = getLeavesData(); + Random r = new Random(); + short appleCount = 0; + for (Block b : t.getLeaves()) { + if (b.getLocation().getBlockY() == t.getLowestLogLoc().getBlockY() + stage) { + if (r.nextBoolean()) { + ItemStack is; + if (treeCutEvent.getSaplingDropChance() > 0 && r.nextInt(treeCutEvent.getSaplingDropChance()) == 0) { + is = new ItemStack(getSaplingMaterial()); + } else if (treeCutEvent.getAppleDropChance() > 0 && r.nextInt(treeCutEvent.getAppleDropChance()) == 0) { + is = new ItemStack(Material.APPLE); + appleCount++; + } else if (treeCutEvent.getLeavesDropChance() > 0 && r.nextInt(treeCutEvent.getLeavesDropChance()) == 0) { + is = new ItemStack(t.getLeave()); + } else { + AnuraThread.queueSync(() -> { + queueBlockBreak(p, b); + b.setType(Material.AIR); + }); + continue; + } + AnuraThread.queueSync(() -> { + b.getWorld().dropItemNaturally(getPossibleLocation(b, stage).add(0.5, 0, 0.5), is); + queueBlockBreak(p, b); + b.setType(Material.AIR); + }); + } else { + AnuraThread.queueSync(() -> { + queueBlockBreak(p, b); + b.setType(Material.AIR); + Location pLocation = getPossibleLocation(b, stage); + b.getWorld().spawnFallingBlock(pLocation.add(0.5, 0, 0.5), ld); + queueFallingBlock(p, pLocation, ld); + }); + } + } + } + if (appleCount > 0) { + Bukkit.getPluginManager().callEvent(new AppleDropEvent(p, appleCount)); + } + } + + private static void calculateDamage(ItemStack is, int damage) { + ItemMeta im = is.getItemMeta(); + if (!(im instanceof Damageable)) { + return; + } + Damageable d = (Damageable) im; + if (is.getType().getMaxDurability() - d.getDamage() - damage < 0) { + is.setType(Material.AIR); + } else { + d.setDamage((short) (d.getDamage() + damage)); + } + } + + private BlockData getLogOrientation() { + Orientable o = (Orientable) t.getWood().createBlockData(); + o.setAxis(convertDirection(t.getFallDirection())); + return o; + } + + private BlockData getLeavesData() { + Leaves l = (Leaves) t.getLeave().createBlockData(); + l.setPersistent(true);//@TODO: Check if needed + return l; + } + + private Material getSaplingMaterial() { + switch (t.getWood()) { + case BIRCH_LOG: + return Material.BIRCH_SAPLING; + case SPRUCE_LOG: + return Material.SPRUCE_SAPLING; + case JUNGLE_LOG: + return Material.JUNGLE_SAPLING; + case ACACIA_LOG: + return Material.ACACIA_SAPLING; + case DARK_OAK_LOG: + return Material.DARK_OAK_SAPLING; + case OAK_LOG: + default: + return Material.OAK_SAPLING; + } + } + + private Location getPossibleLocation(Block start, int stage) { + if (stage != 0) { + for (int i = 1; i <= stage; i++) { + Block b = start.getRelative(t.getFallDirection(), i); + if (b.getType().isSolid() && !t.getLogs().contains(b) && !t.getLeaves().contains(b)) { + return start.getRelative(t.getFallDirection(), i - 1).getLocation(); + } + } + return start.getRelative(t.getFallDirection(), stage).getLocation(); + } + return start.getLocation(); + } + + private static Axis convertDirection(BlockFace bf) { + switch (bf) { + case NORTH: + case SOUTH: + return Axis.Z; + case EAST: + case WEST: + default: + return Axis.X; + } + } + + public static boolean isAxe(Material mat) { + switch (mat) { + case WOODEN_AXE: + case STONE_AXE: + case IRON_AXE: + case GOLDEN_AXE: + case DIAMOND_AXE: + return true; + default: + return false; + } + } + + public static void queueBlockBreak(Player p, Block b) { + if (RealisticMinecraft.hasLogBlock()) { + LogBlockProvider.queueBlockBreak(p, b); + } + } + + public static void queueFallingBlock(Player p, Location l, BlockData bd) { + if (RealisticMinecraft.hasLogBlock()) { + LogBlockProvider.queueFalling(p, l, bd); + } + } +} diff --git a/src/de/anura/realisticminecraft/timber/event/AppleDropEvent.java b/src/de/anura/realisticminecraft/timber/event/AppleDropEvent.java new file mode 100644 index 0000000..9e1d74b --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/event/AppleDropEvent.java @@ -0,0 +1,29 @@ +package de.anura.realisticminecraft.timber.event; + +import de.anura.core.events.AnuraPlayerEvent; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +public class AppleDropEvent extends AnuraPlayerEvent { + + private static final HandlerList HANDLERS = new HandlerList(); + private final short amount; + + public AppleDropEvent(Player p, short amount) { + super(p, true); + this.amount = amount; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + public int getAmount() { + return amount; + } +} diff --git a/src/de/anura/realisticminecraft/timber/event/TreeCutEvent.java b/src/de/anura/realisticminecraft/timber/event/TreeCutEvent.java new file mode 100644 index 0000000..2404c23 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/event/TreeCutEvent.java @@ -0,0 +1,69 @@ +package de.anura.realisticminecraft.timber.event; + +import de.anura.core.events.AnuraPlayerEvent; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public class TreeCutEvent extends AnuraPlayerEvent implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + private final Block block; + private boolean cancel = false; + private short saplingDropChance = 25; + private short appleDropChance = 110; + private short leavesDropChance = 45; + + public TreeCutEvent(Player p, Block b) { + super(p, true); + this.block = b; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + public Block getBlock() { + return block; + } + + public short getSaplingDropChance() { + return saplingDropChance; + } + + public void setSaplingDropChance(short dropChance) { + this.saplingDropChance = dropChance; + } + + public short getAppleDropChance() { + return appleDropChance; + } + + public void setAppleDropChance(short dropChance) { + this.appleDropChance = dropChance; + } + + public short getLeavesDropChance() { + return leavesDropChance; + } + + public void setLeavesDropChance(short dropChance) { + this.leavesDropChance = dropChance; + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/AcaciaTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/AcaciaTreeParser.java new file mode 100644 index 0000000..7e9f7b6 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/AcaciaTreeParser.java @@ -0,0 +1,41 @@ +package de.anura.realisticminecraft.timber.parser; + +import com.google.common.collect.Sets; +import java.util.Collection; +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class AcaciaTreeParser extends TreeParser { + + protected AcaciaTreeParser(Block first) { + super(first); + wood = Material.ACACIA_LOG; + leave = Material.ACACIA_LEAVES; + species = TreeSpecies.ACACIA; + } + + @Override + protected int getMaxLeavesDistance() { + return 3; + } + + @Override + protected int getMaxLogDistance() { + return 4; + } + + @Override + protected int getMaxTrunkSize() { + return 0; + } + + @Override + protected Collection getDirsLog() { + return Sets.newHashSet(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0), + new Vector(1, 1, 1), new Vector(-1, 1, -1), new Vector(-1, 1, 1), new Vector(1, 1, -1), + new Vector(0, 1, 1), new Vector(0, 1, -1), new Vector(-1, 1, 0), new Vector(1, 1, 0), + new Vector(0, 1, 0)); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/BigBirchTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/BigBirchTreeParser.java new file mode 100644 index 0000000..b25102d --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/BigBirchTreeParser.java @@ -0,0 +1,33 @@ +package de.anura.realisticminecraft.timber.parser; + +import com.google.common.collect.Sets; +import java.util.Collection; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class BigBirchTreeParser extends BirchTreeParser { + + protected BigBirchTreeParser(Block first) { + super(first); + } + + @Override + protected int getMaxLeavesDistance() { + return 3; + } + + @Override + protected int getMaxLogDistance() { + return 2; + } + + @Override + protected int getMaxTrunkSize() { + return 1; + } + + @Override + protected Collection getDirsLog() { + return Sets.newHashSet(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0)); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/BigJungleTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/BigJungleTreeParser.java new file mode 100644 index 0000000..8ce8661 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/BigJungleTreeParser.java @@ -0,0 +1,36 @@ +package de.anura.realisticminecraft.timber.parser; + +import com.google.common.collect.Sets; +import java.util.Collection; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class BigJungleTreeParser extends JungleTreeParser { + + protected BigJungleTreeParser(Block first) { + super(first); + } + + @Override + protected int getMaxLeavesDistance() { + return 5; + } + + @Override + protected int getMaxLogDistance() { + return 5; + } + + @Override + protected int getMaxTrunkSize() { + return 1; + } + + @Override + protected Collection getDirsLog() { + return Sets.newHashSet(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0), + new Vector(1, 1, 1), new Vector(-1, 1, -1), new Vector(-1, 1, 1), new Vector(1, 1, -1), + new Vector(1, 0, 1), new Vector(-1, 0, -1), new Vector(-1, 0, 1), new Vector(1, 0, -1), + new Vector(0, 1, 1), new Vector(0, 1, -1), new Vector(-1, 1, 0), new Vector(1, 1, 0)); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/BigOakTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/BigOakTreeParser.java new file mode 100644 index 0000000..6a61531 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/BigOakTreeParser.java @@ -0,0 +1,33 @@ +package de.anura.realisticminecraft.timber.parser; + +import com.google.common.collect.Sets; +import java.util.Collection; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class BigOakTreeParser extends OakTreeParser { + + protected BigOakTreeParser(Block first) { + super(first); + } + + @Override + protected int getMaxLeavesDistance() { + return 3; + } + + @Override + protected int getMaxLogDistance() { + return 2; + } + + @Override + protected int getMaxTrunkSize() { + return 1; + } + + @Override + protected Collection getDirsLog() { + return Sets.newHashSet(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0)); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/BigSpruceTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/BigSpruceTreeParser.java new file mode 100644 index 0000000..94b4025 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/BigSpruceTreeParser.java @@ -0,0 +1,34 @@ +package de.anura.realisticminecraft.timber.parser; + +import com.google.common.collect.Sets; +import java.util.Collection; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class BigSpruceTreeParser extends SpruceTreeParser { + + public BigSpruceTreeParser(Block first) { + super(first); + } + + @Override + protected int getMaxLeavesDistance() { + return 4; + } + + @Override + protected int getMaxLogDistance() { + return 2; + } + + @Override + protected int getMaxTrunkSize() { + return 1; + } + + @Override + protected Collection getDirsLog() { + return Sets.newHashSet(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0), + new Vector(0, 1, 0)); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/BirchTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/BirchTreeParser.java new file mode 100644 index 0000000..2039b60 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/BirchTreeParser.java @@ -0,0 +1,30 @@ +package de.anura.realisticminecraft.timber.parser; + +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; + +public class BirchTreeParser extends TreeParser { + + protected BirchTreeParser(Block first) { + super(first); + wood = Material.BIRCH_LOG; + leave = Material.BIRCH_LEAVES; + species = TreeSpecies.BIRCH; + } + + @Override + protected int getMaxLeavesDistance() { + return 2; + } + + @Override + protected int getMaxLogDistance() { + return 0; + } + + @Override + protected int getMaxTrunkSize() { + return 0; + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/DarkOakTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/DarkOakTreeParser.java new file mode 100644 index 0000000..a1d19a1 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/DarkOakTreeParser.java @@ -0,0 +1,40 @@ +package de.anura.realisticminecraft.timber.parser; + +import com.google.common.collect.Sets; +import java.util.Collection; +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class DarkOakTreeParser extends TreeParser { + + protected DarkOakTreeParser(Block first) { + super(first); + wood = Material.DARK_OAK_LOG; + leave = Material.DARK_OAK_LEAVES; + species = TreeSpecies.DARK_OAK; + } + + @Override + protected int getMaxLeavesDistance() { + return 4; + } + + @Override + protected int getMaxLogDistance() { + return 3; + } + + @Override + protected int getMaxTrunkSize() { + return 1; + } + + @Override + protected Collection getDirsLog() { + return Sets.newHashSet(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0), + new Vector(1, 0, 1), new Vector(-1, 0, -1), new Vector(-1, 0, 1), new Vector(1, 0, -1), + new Vector(0, 1, 0)); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/JungleTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/JungleTreeParser.java new file mode 100644 index 0000000..32d5abc --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/JungleTreeParser.java @@ -0,0 +1,30 @@ +package de.anura.realisticminecraft.timber.parser; + +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; + +public class JungleTreeParser extends TreeParser { + + protected JungleTreeParser(Block first) { + super(first); + wood = Material.JUNGLE_LOG; + leave = Material.JUNGLE_LEAVES; + species = TreeSpecies.JUNGLE; + } + + @Override + protected int getMaxLeavesDistance() { + return 2; + } + + @Override + protected int getMaxLogDistance() { + return 0; + } + + @Override + protected int getMaxTrunkSize() { + return 0; + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/LeaveParser.java b/src/de/anura/realisticminecraft/timber/parser/LeaveParser.java new file mode 100644 index 0000000..9e9421b --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/LeaveParser.java @@ -0,0 +1,43 @@ + +package de.anura.realisticminecraft.timber.parser; + +import java.util.HashSet; +import org.bukkit.block.Block; + +public class LeaveParser { + + private final Block firstBlock; + private final TreeParser tp; + private final HashSet leaveList = new HashSet<>(); + + protected LeaveParser(TreeParser tp, Block firstBlock) { + this.tp = tp; + this.firstBlock = firstBlock; + } + + protected HashSet parse() { + tp.getDirsLeaves().stream() + .map((v) -> firstBlock.getRelative(v.getBlockX(), v.getBlockY(), v.getBlockZ())) + .forEach(this::parseLeaves); + return leaveList; + } + + private void parseLeaves(Block b) { + if (leaveList.contains(b) || + TreeParser.isDistanceBiggerThan(firstBlock.getLocation(), b.getLocation(), tp.getMaxLeavesDistance())) { + return; + } + if (isLeaves(b)) { + leaveList.add(b); + } else { + return; + } + tp.getDirsLeaves().stream().map((v) -> b.getRelative(v.getBlockX(), v.getBlockY(), v.getBlockZ())) + .filter((r) -> (r.getLocation().getBlockY() >= firstBlock.getLocation().getBlockY())) + .forEach(this::parseLeaves); + } + + private boolean isLeaves(Block b) { + return b.getType().equals(tp.leave); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/LogParser.java b/src/de/anura/realisticminecraft/timber/parser/LogParser.java new file mode 100644 index 0000000..57eca4d --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/LogParser.java @@ -0,0 +1,54 @@ + +package de.anura.realisticminecraft.timber.parser; + +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.bukkit.block.Block; + +public class LogParser { + + private final Block firstBlock; + private final TreeParser tp; + private final HashSet logList = new HashSet<>(); + + protected LogParser(TreeParser tp, Block firstBlock) { + this.tp = tp; + this.firstBlock = firstBlock; + } + + protected HashSet parse() { + tp.getDirsLog().stream() + .map((v) -> firstBlock.getRelative(v.getBlockX(), v.getBlockY(), v.getBlockZ())) + .forEach(this::parseLog); + return logList; + } + + private void parseLog(Block b) { + if (logList.contains(b) || + TreeParser.isDistanceBiggerThan(firstBlock.getLocation(), b.getLocation(), tp.getMaxLogDistance())) { + return; + } + if (isWood(b)) { + logList.add(b); + } else { + return; + } + tp.getDirsLog().stream().map((v) -> b.getRelative(v.getBlockX(), v.getBlockY(), v.getBlockZ())) + .forEach(this::parseLog); + } + + private boolean isWood(Block b) { + try { + return b.getType().equals(tp.wood); + } catch (ConcurrentModificationException ex) { + try { + Thread.sleep(2); + } catch (InterruptedException ex1) { + Logger.getLogger(LogParser.class.getName()).log(Level.SEVERE, null, ex1); + } + return isWood(b); + } + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/OakTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/OakTreeParser.java new file mode 100644 index 0000000..8e5f38d --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/OakTreeParser.java @@ -0,0 +1,41 @@ +package de.anura.realisticminecraft.timber.parser; + +import com.google.common.collect.Sets; +import java.util.Collection; +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class OakTreeParser extends TreeParser { + + protected OakTreeParser(Block first) { + super(first); + wood = Material.OAK_LOG; + leave = Material.OAK_LEAVES; + species = TreeSpecies.GENERIC; + } + + @Override + protected int getMaxLeavesDistance() { + return 3; + } + + @Override + protected int getMaxLogDistance() { + return 5; + } + + @Override + protected int getMaxTrunkSize() { + return 1; + } + + @Override + protected Collection getDirsLog() { + return Sets.newHashSet(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0), + new Vector(1, 1, 1), new Vector(-1, 1, -1), new Vector(-1, 1, 1), new Vector(1, 1, -1), + new Vector(1, 0, 1), new Vector(-1, 0, -1), new Vector(-1, 0, 1), new Vector(1, 0, -1), + new Vector(1, 1, 0), new Vector(-1, 1, 0), new Vector(0, 1, 1), new Vector(0, 1, -1)); + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/SpruceTreeParser.java b/src/de/anura/realisticminecraft/timber/parser/SpruceTreeParser.java new file mode 100644 index 0000000..9598680 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/SpruceTreeParser.java @@ -0,0 +1,30 @@ +package de.anura.realisticminecraft.timber.parser; + +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; + +public class SpruceTreeParser extends TreeParser { + + SpruceTreeParser(Block first) { + super(first); + wood = Material.SPRUCE_LOG; + leave = Material.SPRUCE_LEAVES; + species = TreeSpecies.REDWOOD; + } + + @Override + protected int getMaxLeavesDistance() { + return 3; + } + + @Override + protected int getMaxLogDistance() { + return 0; + } + + @Override + protected int getMaxTrunkSize() { + return 0; + } +} diff --git a/src/de/anura/realisticminecraft/timber/parser/TreeParser.java b/src/de/anura/realisticminecraft/timber/parser/TreeParser.java new file mode 100644 index 0000000..d82df94 --- /dev/null +++ b/src/de/anura/realisticminecraft/timber/parser/TreeParser.java @@ -0,0 +1,128 @@ +package de.anura.realisticminecraft.timber.parser; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import de.anura.realisticminecraft.listener.Timber; +import de.anura.realisticminecraft.timber.Tree; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.util.Vector; + +public abstract class TreeParser { + + private final HashSet logList = new HashSet<>(); + private final HashSet leaveList = new HashSet<>(); + private final Block firstBlock; + protected Material wood; + protected Material leave; + protected TreeSpecies species; + + protected TreeParser(Block first) { + firstBlock = first; + } + + public Tree parse(BlockFace bf) { + parseTrunk(firstBlock); + if (getMaxTrunkSize() > 0) { + getDirsStamm().stream().map((v) -> firstBlock.getRelative(v.getBlockX(), v.getBlockY(), v.getBlockZ())) + .forEach(this::parseTrunk); + } + new HashSet<>(logList).forEach((b) -> { + logList.addAll(new LogParser(this, b).parse()); + }); + logList.forEach((b) -> { + leaveList.addAll(new LeaveParser(this, b).parse()); + }); + return new Tree(firstBlock, logList, leaveList, bf, wood, leave, species); + } + + private void parseTrunk(Block b) { + if (logList.size() > 1 && logList.contains(b) || TreeParser.isDistanceBiggerThan(firstBlock.getLocation(), b.getLocation(), getMaxTrunkSize())) { + return; + } + if (isWood(b)) { + logList.add(b); + } else { + return; + } + parseTrunk(b.getRelative(BlockFace.UP)); + } + + private boolean isWood(Block b) { + return b.getType().equals(wood); + } + + protected Collection getDirsLog() { + return Collections.emptySet(); + } + + protected Collection getDirsStamm() { + return Arrays.asList(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0), + new Vector(1, 0, 1), new Vector(-1, 0, 1), new Vector(-1, 0, -1), new Vector(1, 0, -1)); + } + + protected Collection getDirsLeaves() { + return Arrays.asList(new Vector(0, 1, 0), new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), + new Vector(-1, 0, 0)); + } + + protected abstract int getMaxLeavesDistance(); + + protected abstract int getMaxLogDistance(); + + protected abstract int getMaxTrunkSize(); + + protected static boolean isDistanceBiggerThan(Location l1, Location l2, int distance) { + int loc1 = l1.getBlockX(); + int loc2 = l2.getBlockX(); + int loc3 = l1.getBlockZ(); + int loc4 = l2.getBlockZ(); + return (Math.max(loc1, loc2) - Math.min(loc1, loc2)) > distance || (Math.max(loc3, loc4) - Math.min(loc3, loc4)) > distance; + } + + public static TreeParser newTreeParser(Block b) { + if (!Timber.isTimberLog(b.getType())) { + return null; + } + boolean fourLog = false; + for (Vector v : Arrays.asList(new Vector(0, 0, -1), new Vector(1, 0, 0), new Vector(0, 0, 1), new Vector(-1, 0, 0))) { + if (b.getRelative(v.getBlockX(), v.getBlockY(), v.getBlockZ()).getType().equals(b.getType())) { + fourLog = true; + break; + } + } + switch (b.getType()) { + case DARK_OAK_LOG: + return new DarkOakTreeParser(b); + case ACACIA_LOG: + return new AcaciaTreeParser(b); + case JUNGLE_LOG: + if (fourLog) { + return new BigJungleTreeParser(b); + } + return new JungleTreeParser(b); + case SPRUCE_LOG: + if (fourLog) { + return new BigSpruceTreeParser(b); + } + return new SpruceTreeParser(b); + case BIRCH_LOG: + if (fourLog) { + return new BigBirchTreeParser(b); + } + return new BirchTreeParser(b); + case OAK_LOG: + if (fourLog) { + return new BigOakTreeParser(b); + } + return new OakTreeParser(b); + default: + return null; + } + } +} diff --git a/src/de/anura/realisticminecraft/util/ChairManager.java b/src/de/anura/realisticminecraft/util/ChairManager.java new file mode 100644 index 0000000..cb96ec0 --- /dev/null +++ b/src/de/anura/realisticminecraft/util/ChairManager.java @@ -0,0 +1,105 @@ +package de.anura.realisticminecraft.util; + +import de.anura.core.util.Blocks; +import de.anura.core.util.Util; +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.server.v1_14_R1.EntityArmorStand; +import net.minecraft.server.v1_14_R1.PlayerConnection; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.block.Block; +import org.bukkit.block.data.type.Stairs; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; + +public abstract class ChairManager { + + private static final Map SITTING_PLAYERS = new WeakHashMap<>(); + private static final Map SITTING_BLOCKS = new WeakHashMap<>(); + private static Field f; + + static { + try { + f = PlayerConnection.class.getDeclaredField("B"); + f.setAccessible(true); + } catch (NoSuchFieldException | SecurityException ex) { + Logger.getLogger(ChairManager.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public static void playerSitDown(Player p, Stairs s, Block b) { + Objects.requireNonNull(p); + Objects.requireNonNull(s); + Objects.requireNonNull(b); + //Anti Fly-Kick + try { + f.set(((CraftPlayer) p).getHandle().playerConnection, false); + } catch (SecurityException | IllegalArgumentException | IllegalAccessException ex) { + Logger.getLogger(ChairManager.class.getName()).log(Level.SEVERE, null, ex); + } + //ArmorStand creation + Location loc = b.getLocation().add(0.5, 0.25, 0.5); + loc.setYaw(Blocks.faceToYaw(s.getFacing())); + World w = loc.getWorld(); + if (w != null) { + CraftWorld cw = (CraftWorld) w; + EntityArmorStand nms_as = new EntityArmorStand(cw.getHandle(), loc.getX(), loc.getY(), loc.getZ()); + nms_as.setInvisible(true); + nms_as.setMarker(true); + ArmorStand as = (ArmorStand) cw.addEntity(nms_as, SpawnReason.CUSTOM); + as.setGravity(false); + AttributeInstance maxHealth = as.getAttribute(Attribute.GENERIC_MAX_HEALTH); + if (maxHealth != null) { + maxHealth.setBaseValue(0); + } + as.teleport(loc); + as.addPassenger(p); + SITTING_PLAYERS.put(p, as); + SITTING_BLOCKS.put(p, b); + } + } + + public static void playerStandUp(Player p) { + Objects.requireNonNull(p); + ArmorStand as = SITTING_PLAYERS.remove(p); + as.removePassenger(p); + as.remove(); + SITTING_BLOCKS.remove(p); + } + + public static void playerStandUp(Block b) { + Objects.requireNonNull(b); + playerStandUp(Util.getKeyByValue(SITTING_BLOCKS, b)); + } + + public static boolean isSittingAnyone(Block b) { + Objects.requireNonNull(b); + return SITTING_BLOCKS.containsValue(b); + } + + public static boolean isSittingPlayer(Block b, Player p) { + Objects.requireNonNull(p); + Objects.requireNonNull(b); + return SITTING_BLOCKS.containsValue(b) && Util.getKeyByValue(SITTING_BLOCKS, b).equals(p); + } + + public static boolean isSitting(Player p) { + Objects.requireNonNull(p); + return SITTING_PLAYERS.containsKey(p); + } + + public static void destoryAll() { + new HashSet<>(SITTING_PLAYERS.keySet()).stream().forEach(ChairManager::playerStandUp); + } +} diff --git a/src/de/anura/realisticminecraft/util/LogBlockProvider.java b/src/de/anura/realisticminecraft/util/LogBlockProvider.java new file mode 100644 index 0000000..f6eee4c --- /dev/null +++ b/src/de/anura/realisticminecraft/util/LogBlockProvider.java @@ -0,0 +1,91 @@ +package de.anura.realisticminecraft.util; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import de.anura.core.AnuraThread; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.util.BukkitUtils; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import de.anura.realisticminecraft.RealisticMinecraft; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; + +public class LogBlockProvider { + + private static final LogBlock plugin = (LogBlock) Bukkit.getServer().getPluginManager().getPlugin("LogBlock"); + private static final Cache cache = CacheBuilder.newBuilder() + .expireAfterWrite(15, TimeUnit.SECONDS).build(); + private static final Queue queue = new ConcurrentLinkedQueue<>(); + private static final Object value = new Object(); + private static boolean queueStarted = false; + + private static Actor getActor(Player p) { + return new Actor(p.getName(), p.getUniqueId()); + } + + public static void queueFalling(Player p, Location l, BlockData bd) { + startQueue(); + Actor a = getActor(p); + queue.add(() -> queueFallingBlock(a, l, bd)); + } + + private static void queueFallingBlock(Actor a, Location l, BlockData bd) { + Location loc = l; + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + // Blocks only fall if they have a chance to start a velocity + if (l.getBlock().getRelative(BlockFace.DOWN).getType() == Material.AIR) { + while (y > 0 && canFall(loc.getWorld(), x, (y - 1), z)) { + y--; + } + } + // If y is 0 then the sand block fell out of the world :( + if (y != 0) { + Location finalLoc = new Location(loc.getWorld(), x, y, z); + Block finalBlock = finalLoc.getBlock(); + // Run this check to avoid false positives + if (!BukkitUtils.getFallingEntityKillers().contains(finalBlock.getType())) { + cache.put(finalLoc, value); + if (finalBlock.getType() == Material.AIR || finalLoc.equals(l)) { + plugin.getConsumer().queueBlockPlace(a, finalLoc, bd); + } else { + plugin.getConsumer().queueBlockReplace(a, finalLoc, finalBlock.getBlockData(), bd); + } + } + } + } + + public static void queueBlockBreak(Player p, Block b) { + plugin.getConsumer().queueBlockBreak(getActor(p), b.getState()); + } + + public static boolean canFall(World w, int x, int y, int z) { + return BukkitUtils.canFallIn(w, x, (y - 1), z) && cache.getIfPresent(new Location(w, x, y, z)) == null; + } + + public static void startQueue() { + if (queueStarted) { + return; + } + queueStarted = true; + AnuraThread.add(Bukkit.getScheduler().runTaskTimerAsynchronously(RealisticMinecraft.getInstance(), () -> { + for (int i = 0; i < 200; i++) { + Runnable poll = queue.poll(); + if (poll == null) { + continue; + } + poll.run();//TODO: Check SYNC + } + }, 10, 10)); + } +} diff --git a/src/plugin.yml b/src/plugin.yml new file mode 100644 index 0000000..807152a --- /dev/null +++ b/src/plugin.yml @@ -0,0 +1,15 @@ +# Maven Generated Properties +name: RealisticMinecraft +version: 0.1 +author: hibo98 +# Custom Properties +main: de.anura.realisticminecraft.RealisticMinecraft +depend: [Core] +api-version: 1.14 +commands: + infobar: + description: Konfigurieret die Infobars + usage: §c/infobar + infobarset: + description: Setzt Werte für die Infobar + usage: §c/infobarset