87 Commits
Author SHA1 Message Date
ProgrammerDan 2ec3b0e232 NMS setting was premature here based on incomplete understanding. This should be better. To access the real bed position and not a safe spawn location as computed, use the handle() and getBed() method on the underlying EntityHuman object. 2017-06-10 01:02:29 -04:00
ProgrammerDan c7f310da18 Untested but should forceset bedpos as well as spawnpoint 2017-06-09 16:35:50 -04:00
ProgrammerDan 27d37b5519 Fixes to game tuning and game fixes and game features where events should be ignored due to cancel but werent due to bukkits weird default choice for eventhandler. Also added bypass for ops or permission to InvisibleFixTwo same as InvisibleFix 2017-06-05 13:09:06 -04:00
ProgrammerDan 9861b67ab1 Came up with a much more elegant approach, removed manual instrumentation and went full reflection. Its pretty sweet. 2017-05-19 02:08:22 -04:00
ProgrammerDan 40f1810322 Adding a new thought experiment called Insight. It tracks strickly speaking the running time of events, computing a running max, min, and standard deviation, as well as counts of events and other indicators. 2017-05-18 23:07:52 -04:00
ProgrammerDan abcc4f318f Merge branch 'master' of github.com:DevotedMC/SimpleAdminHacks 2017-05-16 17:26:07 -04:00
ProgrammerDan 34602a43da Minor fix for TimingsHack to deal with weird failure to produce map objects 2017-05-16 17:25:53 -04:00
Daniel Boston 3f57b8cac3 Readme formatting fixes 2017-05-04 12:52:48 -04:00
ProgrammerDan 51994670f9 Trying to bypass another new metrics-lite transitive dependency issue, sigh. 2017-05-02 22:21:06 -04:00
Daniel Boston 0ce0b16a33 Merge pull request #13 from DevotedMC/tps-monitor
Tps monitor
2017-04-29 10:16:11 -04:00
ProgrammerDan 947225a8ce Added some better error trapping around vulnerable ticking methods. Added documentation to the javadocs. 2017-04-29 09:45:23 -04:00
ProgrammerDan 173c29d76f Refinements of tps and binding meters 2017-04-29 02:23:09 -04:00
ProgrammerDan a77a433303 Added a bunch of extra tracking to monitor for ticks exceeding a configured threshold, and more 2017-04-28 03:47:12 -04:00
ProgrammerDan c3ee1b3975 Adding showtimings command that generates a map. Thanks to LagMonitor for the map inspiration. 2017-04-27 02:47:50 -04:00
ProgrammerDan a41089d8f2 CrazyCrates does not broadcast prizes to console but does fire an event. New hack lets you capture this event.
Also tweaked some weird default configs.
2017-04-24 14:08:25 -04:00
ProgrammerDan 5c26b304da README long overdue, added. 2017-04-24 02:04:12 -04:00
ProgrammerDan 20020bc97f marginally working fix default to false 2017-04-24 01:37:00 -04:00
Daniel Boston c2fcca0ed5 Merge pull request #12 from CivilizatonExperiment/master
Merging In things that CivEx uses and simple dupe fix
2017-04-24 01:19:58 -04:00
Ryan Reuther 3624823d71 fixed something that i glossed over 2017-04-21 19:22:57 -07:00
Ryan Reuther e0bfcf7d5a Style Clean Up 2017-04-21 17:34:09 -07:00
Ryan Reuther a0df064a76 Style Clean Up 2017-04-21 17:25:13 -07:00
Ryan Reuther fb710722b5 Fixes pointed out on github 2017-04-21 17:09:20 -07:00
Ryan Reuther 8f21a6a6d8 Added carpet to the rail duplication problem 2017-04-19 17:27:37 -07:00
Ryan Reuther 9045f1c2a3 Stopping Deletion of end portals 2017-04-19 16:25:14 -07:00
Ryan Reuther fd23d6ec55 Fix for piston rail duping 2017-04-19 15:27:39 -07:00
Ryan Reuther fdaa22e435 Fix for issue #6 2017-04-19 14:23:42 -07:00
Ryan Reuther a8671c27f6 Minecart dupe bug hopefully fixed 2017-04-19 13:18:09 -07:00
Ryan Reuther 540e091661 Added a description for weeping angel, removed some whitespace, updated pom.xml 2017-04-19 10:41:28 -07:00
Ryan Reuther 064ff5d338 Fixed the through the nether portal thing to prevent duping. However this means that no horses can get into the nether. 2017-04-19 01:09:34 -07:00
Ryan Reuther 3b22331bb1 Removed a config option for something that doesn't need to happen 2017-04-19 01:08:12 -07:00
Ryan Reuther c9d882bf71 opps 2017-03-22 22:33:14 -07:00
Ryan Reuther 2c76209b08 Added a not feature complete replacement for apoc ban (ban on player deaths) that utilizes the built in bukkit ban system. 2017-03-22 22:12:40 -07:00
Ryan Reuther 0299987a17 Allows nether to be 1:1 and disableing of return portals 2017-02-23 16:21:36 -08:00
Ryan00793 5a17d84a86 Merge pull request #1 from DevotedMC/master
thing
2017-02-23 11:11:35 -08:00
Daniel Boston b637d83a93 Update pom.xml for new changes 2017-02-23 13:07:55 -05:00
Daniel Boston 811ce25a7f Merge pull request #11 from CivilizatonExperiment/master
Added the ability to disable: villagers, withers, enderchests.
2017-02-23 13:07:31 -05:00
Ryan Reuther 7e822d0bb3 Tabs 2017-02-23 09:51:40 -08:00
Ryan Reuther d6f69499bc Tabs 2017-02-23 09:46:06 -08:00
Ryan Reuther 1214e3a885 Added status stuff 2017-02-22 22:35:38 -08:00
Ryan Reuther 0363713fb6 Added the ability to disable: villagers, withers, enderchests. 2017-02-22 22:17:40 -08:00
ProgrammerDan ba2507cdab Small fixes to GameFixes, attempting to figure out why it causes CastleGates to stop working have failed so far. 2017-01-10 04:37:48 -05:00
ProgrammerDan 60ccc22e20 Fix to BadBoyWatch adding lowtracking for canceled breaks 2017-01-08 16:34:42 -05:00
ProgrammerDan 34410393f1 Some fixes: Bedsetting during day allow cancel, label fixes and expansion for teals GameFixes, add location to SanityHack 2017-01-08 16:04:08 -05:00
Daniel Boston 88cdcc6ebb Merge pull request #10 from TealNerd/master
Added GameFixes with elytra bug blocking
2017-01-08 15:35:41 -05:00
TealNerd 306b4a9087 Added hopper dupe glitch 2016-12-10 13:37:25 -06:00
TealNerd 4e09b5bc2c Added minecart dupe glitch to GameFixes 2016-12-05 20:11:53 -06:00
TealNerd 755be49173 Added GameFixes with elytra bug blocking 2016-12-04 22:15:52 -06:00
ProgrammerDan ab83ed2500 This actually does appear to address boat and horse invisibility and desync, although its a bit hackish. 2016-11-12 13:43:04 -05:00
ProgrammerDan 71c7ef1ae2 Adding a new invisible fix type, no idea if it will work or not. Updated Invsee to reset styling after item record. 2016-11-11 23:53:16 -05:00
Daniel Boston 99fd783aa6 Merge pull request #9 from Maxopoly/patch-3
Add CivModCore dependency
2016-11-02 02:00:32 -04:00
Maxopoly 6d9b21dcad Add CivModCore dependency 2016-11-02 03:32:10 +01:00
Daniel Boston e08078b050 Merge pull request #8 from Maxopoly/patch-1
Update dependencies
2016-11-01 09:11:03 -04:00
Maxopoly a5630d47ba Update dependencies 2016-11-01 02:44:23 +01:00
ProgrammerDan 8449e3e261 Tab error on default config 2016-10-23 19:05:28 -04:00
ProgrammerDan ae41533e60 Fixing live-disable for Bed fix and other Tuning hacks, and added PotatoXP disabling to GameFeatures hack. 2016-10-23 13:58:21 -04:00
ProgrammerDan 0d13425ae2 Defaults fix for ReinforcedCheckBreak so that player name is displayed. 2016-10-23 13:04:40 -04:00
ProgrammerDan 0a613a7f14 Another typo fix 2016-10-19 03:19:35 -04:00
ProgrammerDan 601294ae91 Reordering boolean checks on bedset, adding null checks to address some NPE sightings. 2016-10-19 01:47:53 -04:00
Daniel Boston 58dc0576c3 Merge pull request #5 from Ameliorate/master
Allow players to set their spawns in beds during the daytime
2016-10-19 01:30:20 -04:00
Amelorate 25dcf204f6 Increment version 2016-10-17 20:39:07 -04:00
Amelorate 6ca76cce58 Add null check to GameTuningConfig.wireupDaytimeBed 2016-10-17 20:38:49 -04:00
Amelorate 45e8fd8414 Allow players to set their spawns in beds during the daytime 2016-10-17 19:35:50 -04:00
ProgrammerDan b838f148a8 Incrementing version 2016-10-15 18:22:06 -04:00
bigbrainiac10 605309ef33 Updated config.yml
Conflicts:
	src/main/resources/config.yml
2016-10-15 18:20:04 -04:00
bigbrainiac10 f19fcd5abb Added SanityHack
Conflicts:
	pom.xml
2016-10-15 18:14:12 -04:00
ProgrammerDan 6f6d97af07 Small fixes to example config 2016-10-15 01:53:59 -04:00
ProgrammerDan 73b26beb86 Adding some config options -- although untried -- and updated pom to represent latest, generally. 2016-10-14 20:45:09 -04:00
ProgrammerDan 8a0e6f6b29 Compilable, first tuning element is limiting tile entities in a chunk. Needs example configuration section. 2016-10-14 19:58:50 -04:00
ProgrammerDan 50c9be6bcf Adding simple game tuning 2016-10-13 18:42:07 -04:00
ProgrammerDan a6832c864d Should have reinforcedchestbreak fixed now, it was missing a generate static method. 2016-10-13 13:26:01 -04:00
ProgrammerDan f2b0adf9d4 Correcting some default values 2016-10-10 22:08:40 -04:00
ProgrammerDan da1541c6fb Trying a new type of fix for invisiblity problem interacting with vanish 2016-10-10 02:07:30 -04:00
ProgrammerDan 2bf277a627 Some fixes 2016-10-04 00:05:50 -04:00
Daniel Boston a04e60eec8 Merge pull request #3 from schweinefilet/cb-message
Added chest break broadcast
2016-10-01 02:40:44 -04:00
Teserox 16c5cb96b5 Changed the list to a HashSet.
Changed event priority to lowest.
Added logging.
2016-09-30 18:05:02 +02:00
Teserox 7ff18f2808 Hotfix: The event is only fired, if the block is a chest. 2016-09-30 17:04:15 +02:00
Teserox b1c712bd79 Changed code formatting. 2016-09-29 13:06:57 +02:00
Teserox 6266a6e9da Broadcast is now made via SimpleAdminHack#serverOperatorBroadcast.
Removed operator list from config.
2016-09-29 13:04:28 +02:00
Teserox e61bb5159d Minor changes.
Fixed a scheduler bug.
2016-09-27 21:06:56 +02:00
Teserox 052b8b0834 Added ReinforcedChestBreak (two new classes: ReinforcedChestBreakConfig and ReinforcedChestBreak) and updated the POM so Citadel is a dependency.
Admins are now automatically added.
Added player list to send messages.
2016-09-25 12:55:07 +02:00
ProgrammerDan 56566ef997 Fixes from observed runtime exceptions, and adding UUID to badboy log 2016-09-24 23:52:34 -04:00
ProgrammerDan 5bd0b64e4c Fixes to config, badboy watch works now, yey. 2016-09-24 08:34:41 -04:00
ProgrammerDan 07fb6624eb Incrementing pom and finishing out badboy watch, simple as can be watched block and break path watching. 2016-09-22 19:57:52 -04:00
ProgrammerDan 824d3a876f ADding a watch task for bad boys 2016-09-22 07:06:39 -04:00
ProgrammerDan f6c457759e Potential fix for invisible players. 2016-09-20 18:26:45 -04:00
ProgrammerDan d8d65b19f4 Fixing announce message loading, wasn't working for some reason. 2016-09-16 15:05:12 -04:00
ProgrammerDan 2c15a6fd99 Significant updates. Implemented autoloading of all hacks in .hacks package if valid and a configuration exists. Addresses #1 and #2. 2016-09-16 02:07:52 -04:00
37 changed files with 4576 additions and 120 deletions

No files matched your search

+158
View File
@@ -0,0 +1,158 @@
Simple Admin Hacks
==================
Landing spot for fixes, tuning, micro game modifications that have no other home.
This is tweak central.
Brought to you by DevotedMC, we're taking a pretty inclusive and permissive view
for this plugin; if you need a particular "hack" to get things working in your
use case, send us a PR for inclusion, we'd love to host it.
This plugin is designed from the ground up to support easy creation of "microplugins"
-- things with featuresets too small to make sense for a full sized plugin, or fixes
for game problems or simple heuristic loggers.
Making a new "Hack" is always an option, but take a look at this list of active
Hacks and see if your contribution might fit with an existing module.
## Active Hacks
Any hack can be theoretically live-disabled and enabled using:
`hacks <hackname> enable` or `hacks <hackname> disable` where the hackname is
as listed.
Type `hacks` to see what the current enable/disable status of all loaded hacks is.
Type `hacks <hackname>` to see a readout of that particular hack's status or config.
Type `hacks <hackname> get <configitem>` to see the current configuration setting for that config element in that hack.
Type `hacks <hackname> set <configitem> <new value>` to set a new configuratoin value for that config element in that hack. Not all hacks support this.
### BadBoyWatch
This hack keeps track of who breaks what, and what they broke to get there. I've got
bigger plans for more heuristics but for now, that's it. Please, feel free to improve
on this.
### CTAnnounce
An integration into CombatTagPlus, this announces to in-game operators when combat
is under way. Invaluable when you like your admins to monitor in-game fights.
### Experimental
This is a catch-all for anything micro that has no other home. Useful features like
`/serialize` command live here too, but mostly its logging trackers that are great
for debugging edge cases and useless for normal runtime.
### GameFeatures
The principle focus here is on enabling and disabling game features, like elytra or
skulker boxes or those sorts of things. Try to keep the focus on boolean or otherwise
simple on / off or discrete state options that relate to existing Minecraft game
mechanics.
### GameFixes
Sometimes things in Minecraft just... don't work. Or don't work the way you want them
to. Put those fixes here.
### GameTuning
Similar to Game Fixes, but distinct in that the intent here is on hacks that _tune_
or alter existing mechanics without fully disabling them or without fixing them.
Things like altering armor damage reduction or tool impact would be good fits.
### HackBot
An experimental module whose complexity is approaching divorce from SAH, this is
a bit of mid-work code that allows operators to define static Bots that show up
like "real" players but aren't.
### Introbook
Allows the creation of customizeable, helpful first join books that are distributed
to players when they first join the server. Safe to turn off but super useful
if you have a core set of additional "good to know" information or introductory
material you'd like players to receive.
### InvControl
Host of the `/invsee` command, gives insight into active player inventory, armor,
health and more. I hope / intend to add another portion to this that allows online
and offline inventory management via `/invmod` but this is as of now not started.
### InvisibleFix and InvisibleFixTwo
My hope is to remove these in time, but for 1.9 and 1.10 minecraft players
go invisible all the time. Both of these hacks use different approaches to help
preserve the visiblity of players. The second hack is the more invasive, sending
explicit location packets when the situation indicates its usefulness.
### NewfriendAssist
A new join tracker, that also doubles as an introkit delivery mechanism.
It also exposes the `introkit` command you can use to send specific people a new
introkit if they lost theirs.
### ReinforcedChestBreak
Another useful alerting hack, sends a message to online operators when players
are actively breaking reinforced containers.
### SanityHack
This one is pretty Devoted iteration 3 specific. Allows tracking server wide of
all breaks or builds under a specific y level.
### TimingsHack
This is mature enough to be its own plugin now, but for the moment I'm leaving it in
because IMHO it is an essential component for debugging and maintaining your server.
This hack begins where /timings and warmroast end. It generates in-game visualizations
of per-tick time utilization, and allows you to investigate with extreme effectiveness
exactly what is causing instantaneous or irregular lag. Things that average out, but lead
to bad experiences for players.
To use it, there are a number of important commands.
First:
* `/showtimings` This gives the requester a "map" object that paints a heatmap, heightmap,
and linegraph of TPS. Each vertical pixel is a full 20 ticks; each dot in the heatmap
is a single tick. The heightmap above the heatmap (heatmap is on the bottom) is both
colored and sized based on time-in-tick. At the very top is line graph of relative "health"
of the tick. The closer to the grey line the better; the closer to the top, the worse.
* `/thresholdtimings` This starts a very high resolution (1ms) stack inspection tracker.
It keeps track _per tick_ with usually 50 samples per tick of which methods and classes
are being taking up time within the tick. Give it a threshold factor -- please, something more
then 1, 5 to 10 works well -- where any tick that takes longer then threshold factor * average
tick length, dumps the class/method list to requestor (either console or in-game character).
The list is in sorted order of class where most time is spent down to class where least time
is spent.
* `/listtimings` In you already suspect something, you can use this command to begin a spammy
dump of all classes encountered during high-resolution tracking. Only new classes are printed,
and it shuts itself off if nothing new has been printed for a full second.
* `/bindtimings` This generates an in-game map displaying (like `/showtimings`) a heatmap, heightmap,
and line graph of per-tick utilization of any Classes that _contain_ whatever is passed in after
bindtimings. Note this is saved, and on reload of the server if HQ timings are restarted those
maps will re-bind to the saved bindings. This is a massively useful deep inspection tool;
you can put the "based" class of a suspect plugin as the parameter, and watch its actual per-tick
time utilization in real-time. Destroy the map object to release the drain on overall TPS that
results from the comparison functions used to update the map.
* `/stoptimings` This turns off any HQ functions. Has the impact of _pausing_ any bindtimings maps,
and releasing all HQ functions from impacting Tick (also stops tracking data, so data tracking will
reset if HQ restarted by calling any other HQ-tagged command).
In game example with some descriptions:
* From live server: http://imgur.com/a/zWNWo
## Conclusion
There is room for many more hacks and the list grows regularly. The pattern is simple;
check any of the existing hacks for details by example.
+38 -7
View File
@@ -6,10 +6,10 @@
<groupId>com.programmerdan.minecraft</groupId>
<artifactId>SimpleAdminHacks</artifactId>
<packaging>jar</packaging>
<version>1.0.6</version>
<version>1.1.2</version>
<name>SimpleAdminHacks</name>
<url>https://github.com/DevotedMC/SimpleAdminHacks</url>
<build>
<finalName>${project.name}-${project.version}</finalName>
<plugins>
@@ -17,8 +17,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
<compilerArgument></compilerArgument>
</configuration>
</plugin>
@@ -97,13 +97,17 @@
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/content/groups/public/</url>
</repository>
<repository>
<id>devoted-repo</id>
<url>https://build.devotedmc.com/plugin/repository/everything/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.10</version>
<version>1.10.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -111,6 +115,12 @@
<artifactId>CombatTagPlus</artifactId>
<version>1.2.4-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.mcstats.bukkit</groupId>
<artifactId>metrics-lite</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.inventivetalent.npclib</groupId>
@@ -137,9 +147,30 @@
<dependency>
<groupId>vg.civcraft.mc.namelayer</groupId>
<artifactId>NameLayer</artifactId>
<version>2.8.03</version>
<version>2.9.08</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>vg.civcraft.mc.citadel</groupId>
<artifactId>Citadel</artifactId>
<version>3.7.02</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>vg.civcraft.mc.civmodcore</groupId>
<artifactId>CivModCore</artifactId>
<version>1.5.09</version>
<scope>provided</scope>
</dependency>
<!-- Note: CrazyCrates isn't distributed in POM form.
For this to build successfully, use install:install-file with
attributes as below. -->
<dependency>
<groupId>me.badbones69</groupId>
<artifactId>CrazyCrates</artifactId>
<version>1.7.1.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -1,5 +1,8 @@
package com.programmerdan.minecraft.simpleadminhacks;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.Collection;
import java.util.List;
@@ -8,10 +11,10 @@ import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
@@ -19,6 +22,8 @@ import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.CommandExecutor;
import com.google.common.reflect.ClassPath;
/**
* Wrapper for simple admin hacks, each doing a thing and each configurable.
*
@@ -50,14 +55,76 @@ public class SimpleAdminHacks extends JavaPlugin {
try {
this.config = new SimpleAdminHacksConfig(this, conf);
} catch(InvalidConfigException e) {
this.log(Level.SEVERE, "Failed to load config. Disabling plugin.", e);
log(Level.SEVERE, "Failed to load config. Disabling plugin.", e);
this.setEnabled(false);
return;
}
// Now load all the Hacks and register.
ConfigurationSection hackConfigs = this.getConfig().getConfigurationSection("hacks");
try {
ClassPath getSamplersPath = ClassPath.from(this.getClassLoader());
for (ClassPath.ClassInfo clsInfo : getSamplersPath.getTopLevelClasses("com.programmerdan.minecraft.simpleadminhacks.hacks")) {
try {
Class<?> clazz = clsInfo.load();
if (clazz != null && SimpleHack.class.isAssignableFrom(clazz)) {
log(Level.INFO, "Found a hack class {0}, attempting to find a generating method and constructor", clazz.getName());
ConfigurationSection hackConfig = hackConfigs.getConfigurationSection(clazz.getSimpleName());
SimpleHackConfig hackingConfig = null;
if (hackConfig != null) {
try {
Method genBasic = clazz.getMethod("generate", SimpleAdminHacks.class, ConfigurationSection.class);
hackingConfig = (SimpleHackConfig) genBasic.invoke(null, this, hackConfig);
} catch (IllegalAccessException failure) {
log(Level.WARNING, "Creating configuration for hack {0} failed, illegal access failure", clazz.getName());
} catch (IllegalArgumentException failure) {
log(Level.WARNING, "Creating configuration for hack {0} failed, illegal argument failure", clazz.getName());
} catch (InvocationTargetException failure) {
log(Level.WARNING, "Creating configuration for hack {0} failed, invocation target failure", clazz.getName());
}
} else {
log(Level.INFO, "Hack for {0} found but no configuration, skipping.", clazz.getSimpleName());
}
if (hackingConfig != null) {
log(Level.INFO, "Configuration for Hack {0} found, instance: {1}", clazz.getSimpleName(), hackingConfig.toString());
SimpleHack<?> hack = null;
try {
Constructor<?> constructBasic = clazz.getConstructor(SimpleAdminHacks.class, hackingConfig.getClass());
hack = (SimpleHack<?>) constructBasic.newInstance(this, hackingConfig);
log(Level.INFO, "Created a new Hack of type {0}", clazz.getSimpleName());
} catch (InvalidConfigException ice) {
log(Level.WARNING, "Failed to activate {0} hack, configuration failed", clazz.getSimpleName());
} catch (Exception e) {
log(Level.WARNING, "Failed to activate {0} hack, configuration failed: {1}", clazz.getSimpleName(), e.getMessage());
}
if (hack == null) {
log(Level.WARNING, "Failed to create a Hack of type {0}", clazz.getSimpleName());
} else {
register(hack);
log(Level.INFO, "Registered a new hack: {0}", clazz.getSimpleName());
}
} else {
log(Level.INFO, "Configuration generation for Hack {0} failed, skipping.", clazz.getSimpleName());
}
}
} catch (NoClassDefFoundError e) {
log(Level.INFO, "Unable to load discovered class {0} due to dependency failure", clsInfo.getName());
} catch (Exception e) {
log(Level.WARNING, "Failed to complete hack discovery {0}", clsInfo.getName());
}
}
} catch (Exception e) {
log(Level.WARNING, "Failed to complete hack registration");
}
// Warning if no hacks.
if (hacks == null || hacks.size() == 0) {
this.log("No hacks enabled.");
log(Level.WARNING, "No hacks enabled.");
return;
}
@@ -263,5 +330,13 @@ public class SimpleAdminHacks extends JavaPlugin {
log(Level.WARNING, "Failed to register Executor for {0}, please define that command in the plugin.yml first.", command);
}
}
/**
* Definitely not safe. Used for Insight hack.
* @return the ClassLoader for this plugin.
*/
public ClassLoader exposeClassLoader() {
return this.getClassLoader();
}
}
@@ -4,19 +4,6 @@ import java.util.logging.Level;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.configs.CTAnnounceConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.HackBotConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.IntrobookConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.InvControlConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.NewfriendAssistConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.ExperimentalConfig;
import com.programmerdan.minecraft.simpleadminhacks.hacks.CTAnnounce;
import com.programmerdan.minecraft.simpleadminhacks.hacks.HackBot;
import com.programmerdan.minecraft.simpleadminhacks.hacks.Introbook;
import com.programmerdan.minecraft.simpleadminhacks.hacks.InvControl;
import com.programmerdan.minecraft.simpleadminhacks.hacks.NewfriendAssist;
import com.programmerdan.minecraft.simpleadminhacks.hacks.Experimental;
/**
* Baseline configuration for SimpleAdminHacks and parser for all actual Hacks.
*
@@ -60,23 +47,6 @@ public class SimpleAdminHacksConfig {
if (this.debug) {
this.plugin.log(Level.INFO, "broadcast_permission set to {0}", this.broadcastPermission);
}
// Now load all the Hacks and register.
ConfigurationSection hacks = config.getConfigurationSection("hacks");
for (String key : hacks.getKeys(false)) {
ConfigurationSection hack = hacks.getConfigurationSection(key);
// TODO eventually, replace this with reflection based load. For tonight, hack it.
try {
SimpleHack<?> newHack = bootstrapHack(hack);
plugin.register(newHack);
if (this.debug) {
this.plugin.log(Level.INFO, "Registered a new hack: {0}", key);
}
} catch (InvalidConfigException ice) {
this.plugin.log(Level.WARNING, key + " could not be mapped to a Hack, config values problem", ice);
}
}
}
public boolean isDebug() {
@@ -96,31 +66,4 @@ public class SimpleAdminHacksConfig {
public String getBroadcastPermission() {
return this.broadcastPermission;
}
private SimpleHack<?> bootstrapHack(ConfigurationSection boot) {
String hackName = boot.getString("name");
if (hackName == null) {
throw new InvalidConfigException("Hack stubbed but config lacks a name, cannot determine which hack to load");
}
try {
if (hackName.equals(CTAnnounce.NAME)) {
return new CTAnnounce(this.plugin, new CTAnnounceConfig(this.plugin, boot));
} else if (hackName.equals(NewfriendAssist.NAME)) {
return new NewfriendAssist(this.plugin, new NewfriendAssistConfig(this.plugin, boot));
} else if (hackName.equals(Introbook.NAME)) {
return new Introbook(this.plugin, new IntrobookConfig(this.plugin, boot));
} else if (hackName.equals(HackBot.NAME)) {
return new HackBot(this.plugin, new HackBotConfig(this.plugin, boot));
} else if (hackName.equals(InvControl.NAME)) {
return new InvControl(this.plugin, new InvControlConfig(this.plugin, boot));
} else if (hackName.equals(Experimental.NAME)) {
return new Experimental(this.plugin, new ExperimentalConfig(this.plugin, boot));
}
} catch (InvalidConfigException ice) {
plugin.log(Level.WARNING, "Failed to activate " + hackName + " hack");
}
throw new InvalidConfigException("Claimed to be a viable hack but isn't: " + hackName);
}
}
@@ -27,9 +27,9 @@ public abstract class SimpleHackConfig {
public SimpleHackConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
this.plugin = plugin;
this.base = base;
this.name = base.getString("name");
this.name = base.getString("name", base.getName());
this.enabled = base.getBoolean("enabled", false);
plugin.log(Level.INFO, "Config for {0}, enabled set to {1}", this.name, this.enabled);
plugin.log(Level.INFO, "Config for {0}, enabled set to {1}, instance {2}", this.name, this.enabled, this.toString());
this.wireup(base);
}
@@ -85,4 +85,4 @@ public abstract class SimpleHackConfig {
return null;
}
}
}
}
@@ -0,0 +1,84 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
/**
* Configures this hack, determines which blocks to watch and the like.
*
* @author ProgrammerDan
*/
public class BadBoyWatchConfig extends SimpleHackConfig {
private int trackingDepth;
private boolean clearOnMatch;
private int minDepthToMatch;
private Set<Material> watchedMaterials;
private static final List<Material> defaultWatched = Arrays.asList(
Material.CHEST,
Material.JUKEBOX,
Material.NOTE_BLOCK,
Material.BEACON,
Material.ENCHANTMENT_TABLE,
Material.ENDER_CHEST,
Material.FURNACE,
Material.SPONGE);
public BadBoyWatchConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
this.trackingDepth = config.getInt("trackingDepth", 8); // keep up to 8 breaks
plugin().log(Level.INFO, " Tracking Depth: {0}", this.trackingDepth);
this.clearOnMatch = config.getBoolean("clearOnMatch", true); // once you print out don't keep old records
plugin().log(Level.INFO, " Clear On Match: {0}", this.clearOnMatch);
this.minDepthToMatch = config.getInt("minDepthToMatch", 2); // this block + 1
plugin().log(Level.INFO, " Minimum Breakpath Depth To Match: {0}", this.minDepthToMatch);
@SuppressWarnings("unchecked")
List<String> watch = (List<String>) config.getList("watchedMaterials");
this.watchedMaterials = new HashSet<Material>();
if (watch == null || watch.isEmpty()) {
this.watchedMaterials.addAll(defaultWatched);
plugin().log(Level.INFO, " Adding default watch set");
} else {
for (String mat : watch) {
try {
Material nmat = Material.matchMaterial(mat);
this.watchedMaterials.add(nmat);
plugin().log(Level.INFO, " Watching material {0}", nmat);
} catch (Exception e) {
plugin().log(Level.WARNING, " Invalid material {0} listed", mat);
}
}
}
}
public int getTrackingDepth() {
return trackingDepth;
}
public boolean isClearOnMatch() {
return clearOnMatch;
}
public int getMinDepthToMatch() {
return minDepthToMatch;
}
public Set<Material> getWatchedMaterials() {
return watchedMaterials;
}
}
@@ -0,0 +1,28 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
/**
* CrazyCrates event hook hack
*
* @author ProgrammerDan
*
*/
public class CrazyCratesAnnounceConfig extends SimpleHackConfig {
public CrazyCratesAnnounceConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
public CrazyCratesAnnounceConfig(ConfigurationSection base) {
super(SimpleAdminHacks.instance(), base);
}
@Override
protected void wireup(ConfigurationSection config) {
// noop
}
}
@@ -0,0 +1,98 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.UUID;
import java.util.List;
import java.util.logging.Level;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.ChatColor;
import org.bukkit.Bukkit;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
/**
* Holds configurations for the GameFeatures module.
*
* @author ProgrammerDan
*/
public class GameFeaturesConfig extends SimpleHackConfig {
private boolean potatoXPEnabled;
private boolean villagerTrading;
private boolean witherSpawning;
private boolean enderChestPlacement;
private boolean weepingAngel;
private int weepingAngelEnv;
private int weepingAngelPlayer;
public GameFeaturesConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
this.potatoXPEnabled = !config.getBoolean("disablePotatoXP", false);
if (!this.potatoXPEnabled) plugin().log(" Potato XP Disabled");
this.villagerTrading = config.getBoolean("villagerTrading", false);
if (!villagerTrading) plugin().log("VillagerTrading is disabled");
this.witherSpawning = config.getBoolean("witherSpawning", false);
if (!this.witherSpawning) plugin().log("Wither Spawning is disabled");
this.enderChestPlacement = config.getBoolean("enderChestPlacement", false);
if (!this.enderChestPlacement) plugin().log("Placeing EnderChests is disabled");
this.weepingAngel = config.getBoolean("weepingAngel.enabled", false);
if (this.weepingAngel) {
this.weepingAngelEnv = config.getInt("weepingAngel.environment", 1);
this.weepingAngelPlayer = config.getInt("weepingAngel.playerKill", 5);
plugin().log("Weeping Angel is enabled. Times | Env[" + weepingAngelEnv + "] PK[" + weepingAngelPlayer + "]");
}
/* Add additional feature config grabs here. */
}
/**
* @return If getting XP from potatos is enabled.
*/
public boolean isPotatoXPEnabled() {
return this.potatoXPEnabled;
}
public boolean isVillagerTrading() {
return this.villagerTrading;
}
public boolean isWitherSpawning() {
return this.witherSpawning;
}
public boolean isEnderChestPlacement() {
return this.enderChestPlacement;
}
public boolean isWeepingAngel() {
return this.weepingAngel;
}
public int getWeepingAngelEnv() {
return this.weepingAngelEnv;
}
public int getWeepingAngelPlayer() {
return this.weepingAngelPlayer;
}
}
@@ -0,0 +1,102 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
import java.util.ArrayList;
public class GameFixesConfig extends SimpleHackConfig {
private boolean blockElytraBreakBug;
private double damageOnElytraBreakBug;
private boolean canStorageTeleport;
private boolean stopHopperDupe;
private boolean stopRailDupe;
private boolean stopEndPortalDeletion;
private ArrayList<BlockFace> bfArray;
private ArrayList<Material> railArray;
private ArrayList<Material> pistonArray;
public GameFixesConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
wireUpArrays();
}
protected void wireup(ConfigurationSection config) {
blockElytraBreakBug = config.getBoolean("blockElytraBreakBug", true);
damageOnElytraBreakBug = config.getDouble("damageOnElytraBreakBug", 0.0d);
canStorageTeleport = config.getBoolean("canStorageTeleport");
stopHopperDupe = config.getBoolean("stopHopperDupe");
stopRailDupe = config.getBoolean("stopRailDupe", true);
if (stopRailDupe) plugin().log("Stop Rail Dupe is enabled.");
stopEndPortalDeletion = config.getBoolean("stopEndPortalDeletion", true);
if (stopEndPortalDeletion) plugin().log("Stop End Portal Deletion is enabled.");
}
private void wireUpArrays() {
bfArray = new ArrayList<BlockFace>();
railArray = new ArrayList<Material>();
pistonArray = new ArrayList<Material>();
railArray.add(Material.RAILS);
railArray.add(Material.ACTIVATOR_RAIL);
railArray.add(Material.DETECTOR_RAIL);
railArray.add(Material.POWERED_RAIL);
railArray.add(Material.CARPET);
bfArray.add(BlockFace.NORTH);
bfArray.add(BlockFace.SOUTH);
bfArray.add(BlockFace.EAST);
bfArray.add(BlockFace.WEST);
bfArray.add(BlockFace.UP);
bfArray.add(BlockFace.DOWN);
pistonArray.add(Material.PISTON_BASE);
pistonArray.add(Material.PISTON_EXTENSION);
pistonArray.add(Material.PISTON_MOVING_PIECE);
pistonArray.add(Material.PISTON_STICKY_BASE);
}
public boolean isBlockElytraBreakBug() {
return blockElytraBreakBug;
}
public double getDamageOnElytraBreakBug() {
return damageOnElytraBreakBug;
}
public boolean canStorageTeleport() {
return canStorageTeleport;
}
public boolean isStopHopperDupe() {
return stopHopperDupe;
}
public boolean isStopRailDupe() {
return stopRailDupe;
}
public boolean isStopEndPortalDeletion() {
return stopEndPortalDeletion;
}
public ArrayList<BlockFace> getBfArray() {
return bfArray;
}
public ArrayList<Material> getRailArray() {
return railArray;
}
public ArrayList<Material> getPistonArray() {
return pistonArray;
}
}
@@ -0,0 +1,208 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.UUID;
import java.util.List;
import java.util.logging.Level;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.ChatColor;
import org.bukkit.Bukkit;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
/**
* Holds configurations for the GameTuning module.
*
* @author ProgrammerDan
*/
public class GameTuningConfig extends SimpleHackConfig {
private boolean chunkLimitsEnabled;
private Map<Material, Integer> blockEntityLimits;
private Set<UUID> exemptFromLimits;
private String chunkLimitsExceededMessage;
private boolean daytimeBedEnabled;
private String daytimeBedSpawnSetMessage;
private boolean oneToOneNether;
private boolean returnNetherPortal;
private boolean chestedMinecartInventories;
private boolean hopperMinecartInventories;
public GameTuningConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
ConfigurationSection chunkLimits = config.getConfigurationSection("chunkLimits");
wireupChunkLimits(chunkLimits);
ConfigurationSection daytimeBed = config.getConfigurationSection("daytimeBed");
wireupDaytimeBed(daytimeBed);
this.oneToOneNether = config.getBoolean("oneToOneNether", false);
if (oneToOneNether) plugin().log("One to One Nether is enabled.");
this.returnNetherPortal = config.getBoolean("returnNetherPortal", true);
if (!returnNetherPortal) plugin().log("Return Nether Portals disabled.");
this.chestedMinecartInventories = config.getBoolean("chestedMinecartInventories", true);
if (!chestedMinecartInventories) plugin().log("Chested Minecart Inventories are disabled.");
this.hopperMinecartInventories = config.getBoolean("hopperMinecartInventories", true);
if (!hopperMinecartInventories) plugin().log("Hopper Minecart Inventories are disabled.");
/* Add additional tuning config grabs here. */
}
/**
* Wireup for Chunk Limits configuration
*
* @author ProgrammerDan
*/
private void wireupChunkLimits(ConfigurationSection config) {
this.blockEntityLimits = new HashMap<Material, Integer>();
this.exemptFromLimits = new HashSet<UUID>();
this.chunkLimitsEnabled = false;
if (config == null) return;
this.chunkLimitsEnabled = config.getBoolean("enabled", false);
this.chunkLimitsExceededMessage = ChatColor.translateAlternateColorCodes('&',
config.getString("exceededMessage",
ChatColor.RED + "Limit for this chunk reached, you cannot place that! Use a different block."));
// for each "chunkLimits.tileEntities: " entry, record limit.
ConfigurationSection tileEntities = config.getConfigurationSection("tileEntities");
for (String key : tileEntities.getKeys(false)) {
int limit = tileEntities.getInt(key);
Material toBlock = Material.getMaterial(key);
if (toBlock == null) continue;
this.blockEntityLimits.put(toBlock, limit);
plugin().log(Level.INFO, " Limiting {0} to {1} per chunk", toBlock.toString(), limit);
}
// for each "chunkLimits.exempt: " entry, record limit.
List<String> exempts = config.getStringList("exempt");
for (String exempt : exempts) {
UUID pExempt = null;
try {
pExempt = UUID.fromString(exempt);
} catch (IllegalArgumentException iae) {
pExempt = null;
}
if (pExempt == null) {
OfflinePlayer ofp = Bukkit.getOfflinePlayer(exempt);
if (ofp != null) {
pExempt = ofp.getUniqueId();
}
}
if (pExempt == null) {
plugin().log(Level.INFO, " Unable to exempt {0}, player not found", exempt);
} else {
plugin().log(Level.INFO, " Adding limits exemption for {0} as {1}", exempt, pExempt);
this.exemptFromLimits.add(pExempt);
}
}
}
/**
* Wireup for enabling setting your spawn during the day.
*
* @author Amelorate
*/
private void wireupDaytimeBed(ConfigurationSection config) {
if (config == null) {
this.daytimeBedEnabled = false;
return;
}
this.daytimeBedEnabled = config.getBoolean("enabled", false);
this.daytimeBedSpawnSetMessage = ChatColor.translateAlternateColorCodes('&',
config.getString("spawnSetMessage", ChatColor.GRAY + "Your spawn has been set."));
}
/**
* @return true / false if chunk limits are on
*/
public boolean areChunkLimitsEnabled() {
return this.chunkLimitsEnabled;
}
/**
* Gets the chunk limit for a specified material.
* Returns null if no limit is set.
*
* @param mat the Material to return a limit on, if set.
* @return the max # per chunk, or null if no limit set.
*/
public Integer getChunkLimit(Material mat) {
return this.chunkLimitsEnabled ? this.blockEntityLimits.get(mat) : null;
}
/**
* Returns false if the supplied UUID is exempt from limits.
*
* @param player the UUID to check
* @returns false it the player is exempt, true otherwise.
*/
public boolean applyChunkLimits(UUID player) {
return this.chunkLimitsEnabled ? !this.exemptFromLimits.contains(player) : false;
}
/**
* @return the generic limit message. TODO: Replace with customized message tailored per player.
*/
public String getChunkLimitsExceededMessage() {
return this.chunkLimitsExceededMessage;
}
/**
* @return The message that is sent to the player if they right click on a bed and set their spawn.
* Empty string if there should not be a message, and null if daytime beds are disabled.
*/
public String getDaytimeBedSpawnSetMessage() {
return daytimeBedEnabled ? daytimeBedSpawnSetMessage : null;
}
/**
* @return If setting your spawn with a bed during the daytime is enabled.
*/
public boolean areDaytimeBedsEnabled() {
return daytimeBedEnabled;
}
/**
* @return If one to one nether is enabled.
*/
public boolean isOneToOneNether() {
return oneToOneNether;
}
/**
* @return If return portals are enabled.
*/
public boolean isReturnNetherPortal() {
return returnNetherPortal;
}
public boolean isChestedMinecartInventories() {
return chestedMinecartInventories;
}
public boolean isHopperMinecartInventories() {
return hopperMinecartInventories;
}
}
@@ -0,0 +1,44 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import java.util.List;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
/**
* Define an "into" list of Strings where the Strings are event classpaths.
* Can monitor not only bukkit events but also plugin / custom events; just give the
* valid classpath.
*
* This bypasses the "safe" bukkit way of registering listeners for events. So it
* also bypasses Timings.
*
* This is the only warning I'll give. Be careful with this and prefer to leave it off.
*
* Wireups only happen on restart, I'm dubious about injecting live for now.
*
* @author ProgrammerDan
*
*/
public class InsightConfig extends SimpleHackConfig {
private List<String> insightOn;
public InsightConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
if (config.contains("into")) {
insightOn = config.getStringList("into");
} else {
insightOn = null;
}
}
public List<String> getInsightOn() {
return insightOn;
}
}
@@ -7,6 +7,7 @@ import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.ItemMeta;
@@ -82,12 +83,25 @@ public class IntrobookConfig extends SimpleHackConfig {
*
* @return ItemStack containing the book.
*/
public ItemStack getIntroBook() {
public ItemStack getIntroBook(Player p) {
ItemStack is = new ItemStack(Material.WRITTEN_BOOK);
BookMeta bm = (BookMeta) is.getItemMeta();
bm.setTitle(this.title);
if (p == null) {
bm.setTitle(this.title.replaceAll("\\$\\{player\\}", ""));
} else {
bm.setTitle(this.title.replaceAll("\\$\\{player\\}", p.getName()));
}
bm.setAuthor(this.author);
bm.setPages(this.pages);
ArrayList<String> alist = null;
if (p == null) {
alist = new ArrayList<String>(this.pages);
} else {
alist = new ArrayList<String>();
for (String page : this.pages) {
alist.add(page.replaceAll("\\$\\{player\\}", p.getName()));
}
}
bm.setPages(alist);
is.setItemMeta(bm);
return is;
}
@@ -99,7 +113,9 @@ public class IntrobookConfig extends SimpleHackConfig {
if (!(meta instanceof BookMeta)) return false;
BookMeta book = (BookMeta) meta;
if (!(getTitle().equals(book.getTitle()))) return false;
if (!(getTitle().contains(
book.getTitle().replaceAll("\\$\\{player\\}", "")
))) return false;
if (!(getAuthor().equals(book.getAuthor()))) return false;
return true;
@@ -0,0 +1,49 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
/**
* Lightweight config for InvisibleFix
*
* @author ProgrammerDan
*/
public class InvisibleFixConfig extends SimpleHackConfig {
private boolean ignoreOps;
private String ignorePermission;
private long recheckInterval;
private int maxPlayersPerRecheck;
public InvisibleFixConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
this.ignoreOps = config.getBoolean("ignoreOps", true);
this.ignorePermission = config.getString("ignorePermission", "sah.allowInvisible");
if (this.ignorePermission.equals("")) this.ignorePermission = null;
this.recheckInterval = config.getLong("recheckInterval", 500l);
this.maxPlayersPerRecheck = config.getInt("maxPlayerPerRecheck", 5);
}
public boolean getIgnoreOps() {
return this.ignoreOps;
}
public String getIgnorePermission() {
return this.ignorePermission;
}
public long getRecheckInterval() {
return this.recheckInterval;
}
public int getMaxPlayersPerRecheck() {
return this.maxPlayersPerRecheck;
}
}
@@ -0,0 +1,57 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
/**
* Lightweight config for InvisibleFixTwo
*
* @author ProgrammerDan
*/
public class InvisibleFixTwoConfig extends SimpleHackConfig {
/**
* How many ticks after teleport to attempt to force a refresh of player position.
*/
private long teleportFixDelay;
/**
* How many milliseconds in between each forced update of player position.
*/
private long fixInterval;
private boolean ignoreOps;
private String ignorePermission;
public InvisibleFixTwoConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
this.teleportFixDelay = config.getLong("teleportDelay", 5l);
this.fixInterval = config.getLong("minInterval", 100l);
this.ignoreOps = config.getBoolean("ignoreOps", true);
this.ignorePermission = config.getString("ignorePermission", "sah.allowInvisible");
if (this.ignorePermission.equals("")) this.ignorePermission = null;
}
public long getTeleportFixDelay() {
return this.teleportFixDelay;
}
public long getFixInterval() {
return this.fixInterval;
}
public boolean getIgnoreOps() {
return this.ignoreOps;
}
public String getIgnorePermission() {
return this.ignorePermission;
}
}
@@ -22,7 +22,7 @@ public class NewfriendAssistConfig extends SimpleHackConfig {
/**
* What should I send to people when I announce a newfriend?
*/
private String announceMessage = "&%Player% is brand new!";
private String announceMessage;
/**
* Who should I broadcast to?
*/
@@ -48,7 +48,7 @@ public class NewfriendAssistConfig extends SimpleHackConfig {
@Override
protected void wireup(ConfigurationSection config) {
this.announceMessage = config.getString("announce.message", announceMessage);
this.announceMessage = config.getString("announce", "&f%Player% is brand new!");
List<String> broadcastTo = config.getStringList("broadcast");
if (this.announceBroadcast == null) {
@@ -0,0 +1,36 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
import org.bukkit.configuration.ConfigurationSection;
public class ReinforcedChestBreakConfig extends SimpleHackConfig{
/**
* This is the delay between messages
*/
private int delay;
/**
* The message if someone is raiding a reinforced chest
*/
private String message;
public ReinforcedChestBreakConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
this.delay = config.getInt("delay", 180);
this.message = config.getString("message", "&4%player% is raiding a chest at %x% %y% %z%.");
}
public int getDelay() {
return delay;
}
public String getMessage() {
return message;
}
}
@@ -0,0 +1,52 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import java.util.logging.Level;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
public class SanityHackConfig extends SimpleHackConfig {
private boolean trackPlace; //Track block placed
private boolean trackBreak; //Track block broken
//private boolean trackOpen; //Track block opened (chest)
private int belowYLevel; //Track things below this y level
public SanityHackConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
this.trackPlace = config.getBoolean("trackPlace", true);
plugin().log(Level.INFO, "Tracking placement: {0}", this.trackPlace);
this.trackBreak = config.getBoolean("trackBreak", true);
plugin().log(Level.INFO, "Tracking breaking: {0}", this.trackBreak);
/*this.trackOpen = config.getBoolean("trackOpen", false);
plugin().log(Level.INFO, "Tracking opening: {0}", this.trackOpen);*/
this.belowYLevel = config.getInt("belowYLevel", 7);
plugin().log(Level.INFO, "Tracking below Y: {0}", this.belowYLevel);
}
public boolean isTrackingPlace(){
return this.trackPlace;
}
public boolean isTrackingBreak(){
return this.trackBreak;
}
/*public boolean isTrackingOpen(){
return this.trackOpen;
}*/
public int getTrackingLevel(){
return this.belowYLevel;
}
}
@@ -0,0 +1,65 @@
package com.programmerdan.minecraft.simpleadminhacks.configs;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHackConfig;
public class TimingsHackConfig extends SimpleHackConfig {
private Short timingsMap;
private Map<String, Short> bindMaps;
private Map<Short, String> reverseBindMaps;
public TimingsHackConfig(SimpleAdminHacks plugin, ConfigurationSection base) {
super(plugin, base);
}
@Override
protected void wireup(ConfigurationSection config) {
timingsMap = config.contains("timingMap") ? (short) config.getInt("timingMap") : null;
bindMaps = new ConcurrentHashMap<String, Short>();
reverseBindMaps = new ConcurrentHashMap<Short, String>();
if (config.contains("bindings")) {
ConfigurationSection bindings = config.getConfigurationSection("bindings");
Set<String> keys = bindings.getKeys(false);
for (String key : keys) {
bindMaps.put(key, (short) bindings.getInt(key));
reverseBindMaps.put((short) bindings.getInt(key), key);
}
}
}
public Short getTimingsMap() {
return timingsMap;
}
public void setTimingsMap(short mapId) {
timingsMap = mapId;
getBase().set("timingMap", mapId);
}
public Short getBindMap(String bind) {
if (bind == null) {
return null;
}
return bindMaps.get(bind);
}
public void setBindMap(String bind, short mapId) {
Short prior = bindMaps.replace(bind, mapId);
if (prior != null) {
reverseBindMaps.remove(prior);
}
reverseBindMaps.replace(mapId, bind);
getBase().set("bindings." + bind, mapId);
}
public String getBindFromId(short mapId) {
return reverseBindMaps.get(mapId);
}
}
@@ -0,0 +1,240 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.BadBoyWatchConfig;
/**
* Watches for bad boys by tracking block breaks.
* FIRST HIT will also catch breaks that block protection plugins cancel, which if you skip
* can lead to confusion
*
* @author ProgrammerDan
*
*/
public class BadBoyWatch extends SimpleHack<BadBoyWatchConfig> implements Listener {
/**
* tracking cache, persists and ignores logouts.
*/
private Map<UUID, BadBoyRecord> boys = null;
private Map<UUID, BadBoyRecord> lowBoys = null;
public BadBoyWatch(SimpleAdminHacks plugin, BadBoyWatchConfig config) {
super(plugin, config);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true)
public void breakListenLow(BlockBreakEvent bbe) {
if (!config.isEnabled()) return;
try {
Player player = bbe.getPlayer();
Block block = bbe.getBlock();
BlockState bs = block.getState();
Material material = bs.getType();
lowTrackAndReport(player.getUniqueId(), block.getLocation(), material);
} catch (Exception e) {
// insane catchall
plugin().log(Level.WARNING, "Failed to track a low break for badboy", e);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled=true)
public void breakListen(BlockBreakEvent bbe) {
if (!config.isEnabled()) return;
try {
Player player = bbe.getPlayer();
Block block = bbe.getBlock();
BlockState bs = block.getState();
Material material = bs.getType();
trackAndReport(player.getUniqueId(), block.getLocation(), material);
} catch (Exception e) {
// insane catchall
plugin().log(Level.WARNING, "Failed to track a break for badboy", e);
}
}
private void trackAndReport(UUID uuid, Location location, Material material) {
if (uuid == null || location == null || material == null) return;
BadBoyRecord record = boys.get(uuid);
if (record == null) {
record = new BadBoyRecord(config);
boys.put(uuid, record);
}
String report = record.registerBreak(location, material);
if (report != null) {
plugin().log(uuid.toString() + "} " + report);
}
}
private void lowTrackAndReport(UUID uuid, Location location, Material material) {
if (uuid == null || location == null || material == null) return;
BadBoyRecord record = lowBoys.get(uuid);
if (record == null) {
record = new BadBoyRecord(config);
lowBoys.put(uuid, record);
}
String report = record.registerBreak(location, material);
if (report != null) {
plugin().log(uuid.toString() + "} FIRST HITS " + report);
}
}
@Override
public void registerListeners() {
if (config.isEnabled()) {
plugin().log("Registering BadBoyWatcher listener");
plugin().registerListener(this);
}
}
@Override
public void dataBootstrap() {
boys = new ConcurrentHashMap<UUID, BadBoyRecord>();
lowBoys = new ConcurrentHashMap<UUID, BadBoyRecord>();
}
@Override
public void unregisterListeners() {
}
@Override
public void unregisterCommands() {
}
@Override
public void dataCleanup() {
boys.clear();
lowBoys.clear();
}
@Override
public String status() {
if (!config.isEnabled()) {
return "Bad Boy Listening disabled.";
}
StringBuffer status = new StringBuffer("Listening for bad boy breaks");
status.append("\n Currently watching ").append(boys.size()).append(" players.");
status.append("\n Currently watching LOW ").append(lowBoys.size()).append(" players.");
return status.toString();
}
public static BadBoyWatchConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new BadBoyWatchConfig(plugin, config);
}
@Override
public void registerCommands() {}
private static class BadBoyRecord {
private int nextBreak;
private int breakCount;
private BadBoyBlock[] breaks;
private BadBoyWatchConfig config;
public BadBoyRecord(BadBoyWatchConfig config) {
this.config = config;
this.nextBreak = 0;
this.breakCount = 0;
this.breaks = new BadBoyBlock[config.getTrackingDepth() + 1];
}
/**
* Returns a string if this is a watched break that passes all checks.
* Handles abiding the config too.
* @return null if not watched break or if conditions not met, string of
* breaks leading to this otherwise.
*/
public synchronized String registerBreak(Location location, Material material) {
breaks[nextBreak] = new BadBoyBlock(location, material);
nextBreak = (nextBreak + 1) % breaks.length;
breakCount ++;
if (config.getWatchedMaterials().contains(material)) {
String breaks = null;
if (breakCount >= config.getMinDepthToMatch()) {
breaks = showBreaks();
}
if (config.isClearOnMatch()) {
this.clear();
breakCount = 0;
}
return breaks;
}
return null;
}
/**
* Clears everything tracked and resets break pointer.
*/
private void clear() {
for (int i = 0; i < breaks.length; i++) {
breaks[i] = null;
}
nextBreak = 0;
}
/**
* Just outputs what we know right now.
* @return a String showing locations and block materials
*/
private String showBreaks() {
StringBuilder sb = new StringBuilder("BadBoyBreak: ");
int max = breaks.length;
for (int i = (nextBreak - 1 + max) % max; i != nextBreak; i = (i - 1 + max) % max) {
BadBoyBlock bbb = breaks[i];
if (bbb == null) break;
sb.append('[').append(bbb.toString()).append(']');
}
return sb.toString();
}
}
private static class BadBoyBlock {
public final Location location;
public final Material material;
public BadBoyBlock(Location location, Material material) {
this.location = location;
this.material = material;
}
@Override
public String toString() {
StringBuilder se = new StringBuilder();
se.append(location.getWorld().getName());
se.append('(').append(location.getBlockX()).append(',')
.append(location.getBlockY()).append(',')
.append(location.getBlockZ()).append(')');
se.append(material.toString());
return se.toString();
}
}
}
@@ -4,11 +4,12 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.ChatColor;
import net.md_5.bungee.api.ChatColor;
import net.minelink.ctplus.event.PlayerCombatTagEvent;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
@@ -123,4 +124,8 @@ public class CTAnnounce extends SimpleHack<CTAnnounceConfig> implements Listener
return "CombatTagPlus.PlayerCombatTagEvent monitoring not active";
}
}
public static CTAnnounceConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new CTAnnounceConfig(plugin, config);
}
}
@@ -0,0 +1,133 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import java.util.logging.Level;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import me.badbones69.crazycrates.api.PlayerPrizeEvent;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.CrazyCratesAnnounceConfig;
/**
* Ties into CrazyCrates, listens for {@link PlayerPrizeEvent}
*
* @author ProgrammerDan
*/
public class CrazyCratesAnnounce extends SimpleHack<CrazyCratesAnnounceConfig> implements Listener{
public static final String NAME = "CrazyCratesAnnounce";
public CrazyCratesAnnounce(SimpleAdminHacks plugin, CrazyCratesAnnounceConfig config) {
super(plugin, config);
if (!plugin.serverHasPlugin("CrazyCrates")){
plugin.log("CrazyCrates not found, disabling broadcast hook.");
config.setEnabled(false);
}
}
/**
* Capture player combattag and broadcast it
*
* @param event
*/
@EventHandler(priority=EventPriority.MONITOR, ignoreCancelled = true)
public void onPrizeEvent(PlayerPrizeEvent event) {
if (!config.isEnabled()) return; // ignore if off
StringBuilder sb = new StringBuilder();
sb.append("CrazyCrates opening crate ").append(event.getCrateName()).append(" [").append(event.getCrateType()).append("] for ");
if (event.getPlayer() != null) {
sb.append(event.getPlayer().getName());
} else {
sb.append("Unknown");
}
if (event.getPrize() != null) {
sb.append(" holding ").append(event.getPrize().getName());
} else {
sb.append(" holding unnamed with empty");
return;
}
sb.append(" with");
if (event.getPrize().getItems() != null && !event.getPrize().getItems().isEmpty()) {
for (ItemStack item : event.getPrize().getItems()) {
sb.append(" ").append(item.getType());
if (item.getAmount() > 1) {
sb.append("x").append(item.getAmount());
}
if (item.hasItemMeta()) {
ItemMeta meta = item.getItemMeta();
if (meta.hasDisplayName()) {
sb.append(" {").append(meta.getDisplayName()).append("}");
}
if (meta.hasLore()) {
sb.append(" ");
for (String lore : meta.getLore()) {
sb.append(" [").append(lore).append("]");
}
}
if (meta.hasEnchants()) {
sb.append(" Ench");
}
}
}
} else {
sb.append(" empty");
}
plugin().log(Level.INFO, sb.toString());
}
@Override
public void registerListeners() {
if (config.isEnabled()) {
plugin().log("Registering CrazyCrates listener");
plugin().registerListener(this);
}
}
@Override
public void registerCommands() {
}
@Override
public void dataBootstrap() {
}
@Override
public void unregisterListeners() {
}
@Override
public void unregisterCommands() {
}
@Override
public void dataCleanup() {
}
@Override
public String status() {
if (config != null && config.isEnabled()) {
return "CrazyCrates.PlayerPrizeEvent monitoring active";
} else {
return "CrazyCrates.PlayerPrizeEvent monitoring not active";
}
}
public static CrazyCratesAnnounceConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new CrazyCratesAnnounceConfig(plugin, config);
}
}
@@ -15,12 +15,14 @@ import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.entity.Player;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import java.util.UUID;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.CTAnnounceConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.ExperimentalConfig;
public class Experimental extends SimpleHack<ExperimentalConfig> implements Listener, CommandExecutor {
@@ -221,5 +223,9 @@ public class Experimental extends SimpleHack<ExperimentalConfig> implements List
StringBuffer sb = new StringBuffer("[HI]: ");
logCombat(event, sb);
}
public static ExperimentalConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new ExperimentalConfig(plugin, config);
}
}
@@ -0,0 +1,232 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import java.util.Date;
import java.util.logging.Level;
import java.util.List;
import org.apache.commons.lang.time.DateUtils;
import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.*;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.FurnaceExtractEvent;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.GameFeaturesConfig;
import org.bukkit.event.player.PlayerInteractEntityEvent;
/**
* This is a grab-bag class to hold any _features_ related configurations that impact the
* game, server-wide. Mostly focused on turning things on or off.
*
* It's part of a series of focused hacks.
*
* {@link GameFixes} is focused on things that are broken or don't work, and attempts to fix them.
* {@link GameFeatures} focuses on enabling and disabling features, like elytra, various potion states.
* {@link GameTuning} neither fixes nor disables, but rather adjusts and reconfigures.
*
* Currently you can control the following:
* - Disable Potato XP
*/
public class GameFeatures extends SimpleHack<GameFeaturesConfig> implements Listener {
public static final String NAME = "GameFeatures";
public GameFeatures(SimpleAdminHacks plugin, GameFeaturesConfig config) {
super(plugin, config);
}
@Override
public void registerListeners() {
if (config != null && config.isEnabled()) {
plugin().log("Registering GameFeatures listeners");
plugin().registerListener(this);
}
}
@Override
public void unregisterListeners() {
// Bukkit does this for us.
}
@Override
public void registerCommands() {
}
@Override
public void unregisterCommands() {
}
@Override
public void dataBootstrap() {
}
@Override
public void dataCleanup() {
// NO-OP
}
@Override
public String status() {
StringBuilder genStatus = new StringBuilder();
genStatus.append("GameFeatures is ");
if (config != null && config.isEnabled()) {
genStatus.append("active\n");
if (config.isPotatoXPEnabled()) {
genStatus.append(" Potato XP is enabled\n");
} else {
genStatus.append(" Potato XP is disabled\n");
}
genStatus.append(" Villager Trading is ");
if (config.isVillagerTrading()) {
genStatus.append("enabled\n");
} else {
genStatus.append("disabled\n");
}
genStatus.append(" Wither Spawning is ");
if (config.isWitherSpawning()) {
genStatus.append("enabled\n");
} else {
genStatus.append("disabled\n");
}
genStatus.append(" Ender Chest placement is ");
if (config.isEnderChestPlacement()) {
genStatus.append("enabled\n");
} else {
genStatus.append("disabled\n");
}
genStatus.append(" WeepAngel is ");
if (config.isWeepingAngel()) {
genStatus.append("enabled\n");
} else {
genStatus.append("disabled\n");
}
// more?
} else {
genStatus.append("inactive");
}
return genStatus.toString();
}
public static GameFeaturesConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new GameFeaturesConfig(plugin, config);
}
/* From here on, the actual meat of the hack. Above is basically boilerplate for micro-plugins.*/
/**
* Perhaps eventually generalize?
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void potatoXP(FurnaceExtractEvent event) {
if (!config.isEnabled() || config.isPotatoXPEnabled()) return;
try {
Material mat = event.getItemType();
if (mat == null) return;
if (Material.BAKED_POTATO.equals(mat)) {
event.setExpToDrop(0);
}
} catch (Exception e) {
plugin().log(Level.WARNING, "Failed to stop potato XP", e);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void disableVillagerTrading(PlayerInteractEntityEvent event) {
if (!config.isVillagerTrading()) {
Entity npc = event.getRightClicked();
if (npc != null) {
if (npc.getType().equals(EntityType.VILLAGER)) {
event.setCancelled(true);
}
}
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void disableWitherSpawning(CreatureSpawnEvent event) {
if (!config.isWitherSpawning()) {
if (event.getEntityType().equals(EntityType.WITHER)) {
event.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void disableEnderChestPlacement(BlockPlaceEvent event) {
if (!config.isEnderChestPlacement()) {
if (event.getBlock().getType().equals(Material.ENDER_CHEST)) {
event.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void weepingAngelListener(PlayerDeathEvent event) {
if (!config.isWeepingAngel()) {
return;
}
if (event.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent) {
EntityDamageByEntityEvent evt = ((EntityDamageByEntityEvent) event.getEntity().getLastDamageCause());
LivingEntity killer = null;
if (evt.getDamager() instanceof LivingEntity) {
killer = (LivingEntity) evt.getDamager();
} else if (evt.getDamager() instanceof Projectile) {
Projectile projectile = (Projectile) evt.getDamager();
if (projectile.getShooter() instanceof LivingEntity) {
killer = (LivingEntity) projectile.getShooter();
}
}
if (killer != null) {
if (killer instanceof Player) {
banPlayer(event.getEntity().getPlayer(), config.getWeepingAngelPlayer());
} else {
banPlayer(event.getEntity().getPlayer(), config.getWeepingAngelEnv());
}
}
} else {
banPlayer(event.getEntity().getPlayer(), config.getWeepingAngelEnv());
}
}
private void banPlayer(Player p, int minutes) {
if (!config.isWeepingAngel()) {
return;
}
Date exp = DateUtils.addMinutes(new Date(), minutes);
Bukkit.getServer().getBanList(BanList.Type.NAME).addBan(p.getName(), "You've been banned for " + minutes +
" minutes due to your death.", exp, "weepingAngel");
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(SimpleAdminHacks.instance(), new Runnable() {
public void run() {
p.kickPlayer("You've been banned for " + minutes + " minutes due to your death.");
}
}, 2L);
}
}
@@ -0,0 +1,211 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import org.bukkit.Material;
import org.bukkit.ChatColor;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.entity.EntityTeleportEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.material.Dispenser;
import org.bukkit.material.Hopper;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.GameFixesConfig;
public class GameFixes extends SimpleHack<GameFixesConfig> implements Listener {
public static final String NAME = "GameFixes";
public GameFixes(SimpleAdminHacks plugin, GameFixesConfig config) {
super(plugin, config);
}
@Override
public void registerListeners() {
if (config != null && config.isEnabled()) {
plugin().log("Registering GameFixes listeners");
plugin().registerListener(this);
}
}
@Override
public void registerCommands() {
}
@Override
public void dataBootstrap() {
}
@Override
public void unregisterListeners() {
//Bukkit does this for us (why is this a method then?)
// incase your listener semantics include things beyond the default
// like maybe alerting bungee of a connected listener to disable, etc.
}
@Override
public void unregisterCommands() {
}
@Override
public void dataCleanup() {
}
@Override
public String status() {
StringBuilder genStatus = new StringBuilder();
genStatus.append("GameFixes is ");
if (config != null && config.isEnabled()) {
genStatus.append(ChatColor.GREEN).append("active\n").append(ChatColor.RESET);
if (config.isBlockElytraBreakBug()) {
genStatus.append(" Block elytra break bug is ").append(ChatColor.GREEN).append("enabled\n")
.append(ChatColor.RESET);
genStatus.append(" Will deal " + config.getDamageOnElytraBreakBug() + " damage to players\n");
} else {
genStatus.append(" Block elytra break bug is ").append(ChatColor.RED).append("disabled\n")
.append(ChatColor.RESET);
}
if (!config.canStorageTeleport()) {
genStatus.append(" Block storage entities from teleporting to prevents exploits ")
.append(ChatColor.GREEN).append("enabled\n").append(ChatColor.RESET);
} else {
genStatus.append(" Block storage entities from teleporting to prevents exploits ")
.append(ChatColor.RED).append("disabled\n").append(ChatColor.RESET);
}
if (config.isStopHopperDupe()) {
genStatus.append(" Hopper self-feed duplication exploit fix ")
.append(ChatColor.GREEN).append("enabled\n").append(ChatColor.RESET);
} else {
genStatus.append(" Hopper self-feed duplication exploit fix ")
.append(ChatColor.RED).append("disabled\n").append(ChatColor.RESET);
}
} else {
genStatus.append(ChatColor.RED).append("inactive").append(ChatColor.RESET);
}
return genStatus.toString();
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent event) {
if (!config.isEnabled() || !config.isBlockElytraBreakBug()) return;
Block block = event.getBlock();
Player player = event.getPlayer();
if (block == null || player == null) return;
if (!player.getLocation().equals(block.getLocation())
&& player.getEyeLocation().getBlock().getType() != Material.AIR) {
event.setCancelled(true);
player.damage(config.getDamageOnElytraBreakBug());
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onEntityTeleport(EntityTeleportEvent event) {
if (!config.isEnabled() || config.canStorageTeleport()) return;
if (event.getEntity() instanceof InventoryHolder) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onEntityPortal(EntityPortalEvent event) {
if (!config.isEnabled() || config.canStorageTeleport()) return;
if (event.getEntity() instanceof InventoryHolder) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onInventoryMoveItem(InventoryMoveItemEvent event) {
if (!config.isEnabled() || !config.isStopHopperDupe()) return;
if ((event.getDestination() == null) || (event.getSource() == null) ||
!(InventoryType.HOPPER.equals(event.getDestination().getType())) ||
!(InventoryType.HOPPER.equals(event.getSource().getType())) ||
!(Material.HOPPER.equals(event.getDestination().getLocation().getBlock().getType())) ||
!(Material.HOPPER.equals(event.getSource().getLocation().getBlock().getType()))) return;
Hopper source = (Hopper) event.getSource().getLocation().getBlock().getState().getData();
Hopper dest = (Hopper) event.getDestination().getLocation().getBlock().getState().getData();
if (source.getFacing().getOppositeFace() == dest.getFacing()) {
//They're pointing into each other and will eventually dupe
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPistonPushRail(BlockPistonExtendEvent event) {
if (config.isStopRailDupe()) {
for (Block block : event.getBlocks()) {
Material type = block.getType();
if (config.getRailArray().contains(type)) {
event.setCancelled(true);
return;
}
}
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onRailPlace(BlockPlaceEvent event) {
if (config.isStopRailDupe()) {
Block block = event.getBlock();
Material type = block.getType();
if (config.getRailArray().contains(type)) {
for (BlockFace face : config.getBfArray()) {
type = block.getRelative(face).getType();
if (config.getPistonArray().contains(type)) {
event.setCancelled(true);
return;
}
}
}
}
}
//Trying to stop players from deleting end portals
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) {
if (config.isStopEndPortalDeletion()) {
Block block = event.getBlockClicked().getRelative(event.getBlockFace());
if (block.getType() == Material.ENDER_PORTAL) {
event.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onDispenseEvent(BlockDispenseEvent event) {
if (config.isStopEndPortalDeletion()) {
if (event.getBlock().getType() == Material.DISPENSER) {
Dispenser disp = (Dispenser) event.getBlock().getState().getData();
Material type = event.getBlock().getRelative(disp.getFacing()).getType();
if (type == Material.ENDER_PORTAL) {
event.setCancelled(true);
}
}
}
}
public static GameFixesConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new GameFixesConfig(plugin, config);
}
}
@@ -0,0 +1,286 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import java.util.logging.Level;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.minecart.HopperMinecart;
import org.bukkit.entity.minecart.StorageMinecart;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.entity.Player;
import org.bukkit.Material;
import org.bukkit.Bukkit;
import org.bukkit.block.BlockState;
import org.bukkit.block.Block;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.configuration.ConfigurationSection;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.GameTuningConfig;
import org.bukkit.event.world.PortalCreateEvent;
import org.bukkit.inventory.InventoryHolder;
/**
* This is a grab-bag class to hold any _tuning_ related configurations that impact the
* game, server-wide.
*
* It's part of a series of focused hacks.
*
* {@link GameFixes} is focused on things that are broken or don't work, and attempts to fix them.
* {@link GameFeatures} focuses on enabling and disabling features, like elytra, various potion states.
* {@link GameTuning} neither fixes nor disables, but rather adjusts and reconfigures.
*
* Currently you can control the following:
* - BlockEntity limits per chunk
* - Setting bed during the day instead of just at night
*/
public class GameTuning extends SimpleHack<GameTuningConfig> implements Listener {
public static final String NAME = "GameTuning";
public GameTuning(SimpleAdminHacks plugin, GameTuningConfig config) {
super(plugin, config);
}
@Override
public void registerListeners() {
if (config != null && config.isEnabled()) {
plugin().log("Registering GameTuning listeners");
plugin().registerListener(this);
}
}
@Override
public void unregisterListeners() {
// Bukkit does this for us.
}
@Override
public void registerCommands() {
}
@Override
public void unregisterCommands() {
}
@Override
public void dataBootstrap() {
}
@Override
public void dataCleanup() {
// NO-OP
}
@Override
public String status() {
StringBuilder genStatus = new StringBuilder();
genStatus.append("GameTuning is ");
if (config != null && config.isEnabled()) {
genStatus.append("active\n");
if (config.areChunkLimitsEnabled()) {
genStatus.append(" Chunk Limits are enabled\n");
} else {
genStatus.append(" Chunk Limits are disabled\n");
}
if (config.areDaytimeBedsEnabled()) {
genStatus.append(" Daytime Beds are enabled\n");
} else {
genStatus.append(" Daytime Beds are disabled\n");
}
genStatus.append(" One To One Nether is ");
if (config.isOneToOneNether()) {
genStatus.append("enabled\n");
} else {
genStatus.append("disabled\n");
}
// more?
} else {
genStatus.append("inactive");
}
return genStatus.toString();
}
public static GameTuningConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new GameTuningConfig(plugin, config);
}
/* From here on, the actual meat of the hack. Above is basically boilerplate for micro-plugins.*/
/**
* Many thanks to BlackXNT for his work on this event in Humbug, which I have largely copied and expanded.
*
* This tracks block placements, and if a limit is configured and the block is a TileEntity w/ state,
* will reject the placement if otherwise it would exceed limits for the Chunk.
*
* @param event the Placement event.
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void chunkLimits(BlockPlaceEvent event) {
if (!config.isEnabled() || !config.areChunkLimitsEnabled()) return;
try {
Player player = event.getPlayer();
if (player == null) return;
Block block = event.getBlock();
if (block == null) return;
if (!config.applyChunkLimits(player.getUniqueId())) return;
Material mat = block.getType();
if (mat == null) return;
Integer limit = config.getChunkLimit(mat);
if (limit == null) return;
if (block.getChunk().getTileEntities() == null) return;
int current = 0;
for (BlockState state : block.getChunk().getTileEntities()) {
if (state != null && mat.equals(state.getType())) {
if (++current > limit) {
event.setCancelled(true);
player.sendMessage(config.getChunkLimitsExceededMessage());
return;
}
}
}
} catch (Exception e) {
plugin().log(Level.WARNING, "Failed to measure chunk limit", e);
}
}
// If any limit at all, cancel the piston event.
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void chunkLimitsExploitExtend(BlockPistonExtendEvent event) {
if (!config.isEnabled() || !config.areChunkLimitsEnabled()) return;
List<Block> blocks = event.getBlocks();
if (blocks != null && blocks.size() > 0) {
for (Block b : blocks) {
if (b != null && b.getType() != null && config.getChunkLimit(b.getType()) != null) {
event.setCancelled(true);
return; // TODO send message to nearby player warning of reason for stopping.
}
}
}
}
// Yes, this is identical ...
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void chunkLimitsExploitRetract(BlockPistonRetractEvent event) {
if (!config.isEnabled() || !config.areChunkLimitsEnabled()) return;
List<Block> blocks = event.getBlocks();
if (blocks != null && blocks.size() > 0) {
for (Block b : blocks) {
if (b != null && b.getType() != null && config.getChunkLimit(b.getType()) != null) {
event.setCancelled(true);
return; // TODO send message to nearby player warning of reason for stopping.
}
}
}
}
@SuppressWarnings("deprecation")
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void bedRClickToSetSpawn(PlayerInteractEvent event) {
if (!config.isEnabled() || !config.areDaytimeBedsEnabled() || event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getClickedBlock().getType() != Material.BED_BLOCK) {
return;
}
if (event.getPlayer() == null) return;
if (event.getClickedBlock() == null) return;
// Let plugins that already watch for and cancel this event have a turn, like ExilePearl
PlayerBedEnterEvent pbee = new PlayerBedEnterEvent(event.getPlayer(), event.getClickedBlock());
Bukkit.getServer().getPluginManager().callEvent(pbee);
if (pbee.isCancelled()) {
return;
}
event.getPlayer().setBedSpawnLocation(event.getClickedBlock().getLocation(), false);
Location loc = event.getClickedBlock().getLocation();
event.getPlayer().sendTitle("", config.getDaytimeBedSpawnSetMessage());
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPortalTravel(EntityPortalEvent event) {
if (config.isOneToOneNether()) {
Location newLoc = event.getFrom();
newLoc.setWorld(event.getTo().getWorld());
event.setTo(newLoc);
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerPortalTravel(PlayerPortalEvent event) {
if (config.isOneToOneNether() && event.getCause() == PlayerTeleportEvent.TeleportCause.NETHER_PORTAL) {
Location newLoc = event.getFrom();
newLoc.setWorld(event.getTo().getWorld());
event.setTo(newLoc);
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPortalCreate(PortalCreateEvent event) {
if (!config.isReturnNetherPortal()) {
if (event.getReason() == PortalCreateEvent.CreateReason.FIRE && event.getWorld().getName().equals("world_nether")) {
event.setCancelled(true);
}
}
}
//Trying to stop dupe bugs via minecart inventories
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onEntityRightClick(PlayerInteractEntityEvent event) {
if (!config.isChestedMinecartInventories() || !config.isHopperMinecartInventories()) {
Entity target = event.getRightClicked();
if (target.getType().equals(EntityType.MINECART_CHEST) && !config.isChestedMinecartInventories()) {
event.setCancelled(true);
}
if (target.getType().equals(EntityType.MINECART_HOPPER) && !config.isHopperMinecartInventories()) {
event.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onInventoryMoveItemEvent(InventoryMoveItemEvent event) {
if (!config.isChestedMinecartInventories() || !config.isHopperMinecartInventories()) {
InventoryHolder holder = event.getDestination().getHolder();
if (holder instanceof StorageMinecart && !config.isChestedMinecartInventories()) {
event.setCancelled(true);
}
if (holder instanceof HopperMinecart && !config.isHopperMinecartInventories()) {
event.setCancelled(true);
}
}
}
}
@@ -26,6 +26,7 @@ import com.comphenix.protocol.ProtocolManager;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.bots.Bot;
import com.programmerdan.minecraft.simpleadminhacks.configs.CTAnnounceConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.HackBotConfig;
import de.inventivegames.npc.NPC;
@@ -395,5 +396,9 @@ public class HackBot extends SimpleHack<HackBotConfig> implements Listener, Comm
sb.append(ChatColor.RESET).append("; details: ").append(abot.status());
}
return sb.toString();
}
}
public static HackBotConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new HackBotConfig(plugin, config);
}
}
@@ -0,0 +1,452 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.IllegalPluginAccessException;
import org.bukkit.plugin.RegisteredListener;
import org.bukkit.scheduler.BukkitTask;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.InsightConfig;
/**
* Config example:
* <code>
* Insight:
* enabled: true
* into:
* - org.bukkit.event.player
* - org.bukkit.event.block
* - org.bukkit.event.inventory
*
* This "into" list defines event classpaths. Events found in that classpath are
* instrumented; listeners are reordered to put _this_ hack's listeners as first and last to run.
*
* These listeners bypass safe bukkit convention, Timings, and can handle
* async or sync events.
*
* Can monitor not only bukkit events but also plugin / custom events; just give the
* valid classpath.
*
* This bypasses the "safe" bukkit way of registering listeners for events. So it
* also bypasses Timings.
*
* This is the only warning I'll give. Be careful with this and prefer to leave it off.
*
* Wireups only happen on restart, I'm dubious about injecting live for now.
*
* @author ProgrammerDan
*/
public class Insight extends SimpleHack<InsightConfig> implements CommandExecutor, Listener {
public static final String NAME = "Insight";
private Map<String, InsightStat> tracking;
private List<Class<? extends Event>> rebounders;
public Insight(SimpleAdminHacks plugin, InsightConfig config) {
super(plugin, config);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length < 1) { // show help
return false;
}
return true;
}
@SuppressWarnings("unchecked")
@Override
public void registerListeners() {
if (config.isEnabled()) {
plugin().log("Registering Insight reordering hack");
plugin().registerListener(this);
plugin().log("Registering Insight event instrumentations");
if (config.getInsightOn() == null) {
plugin().log("No instrumentations defined, disabling.");
this.softDisable();
return;
}
try {
ClassPath getSamplersPath = ClassPath.from(plugin().exposeClassLoader());
for (String clsPackage : config.getInsightOn()) {
ImmutableSet<ClassPath.ClassInfo> clsInfoSet = getSamplersPath.getTopLevelClasses(clsPackage);
if (clsInfoSet == null || clsInfoSet.isEmpty()) {
plugin().log(Level.INFO, "Package {0} doesn't exist or is empty.", clsPackage);
continue;
}
plugin().log(Level.INFO, "Scanning package {0} for events.", clsPackage);
for (ClassPath.ClassInfo clsInfo : clsInfoSet) {
try {
Class<?> clazz = clsInfo.load();
if (clazz != null && Event.class.isAssignableFrom(clazz)) {
plugin().log(Level.INFO, "...Found event {0}, registering", clazz.getName());
// This is some advanced shit. It also bypasses the timings, so, be careful, ok?
plugin().getServer().getPluginManager().registerEvent((Class<? extends Event>) clazz, this, EventPriority.LOWEST, new EventExecutor() {
@Override
public void execute(Listener arg0, Event arg1) throws EventException {
try {
((Insight) arg0).start(arg1);
} catch (Exception e) {
// no-op, we were never here
}
}
}, plugin(), true);
plugin().getServer().getPluginManager().registerEvent((Class<? extends Event>) clazz, this, EventPriority.MONITOR, new EventExecutor() {
@Override
public void execute(Listener arg0, Event arg1) throws EventException {
try {
((Insight) arg0).end(arg1);
} catch (Exception e) {
// no-op, we were never here
}
}
}, plugin(), true);
rebounders.add((Class<? extends Event>) clazz);
}
} catch (NoClassDefFoundError e) {
plugin().log(Level.INFO, "...Unable to register event {0} due to dependency failure", clsInfo.getName());
} catch (IllegalPluginAccessException ipae) {
plugin().log(Level.INFO, "...Unable to register event {0}; abstract or uninstanced event", clsInfo.getName());
} catch (Exception e) {
plugin().log(Level.WARNING, "...Failed to complete event registration for " + clsInfo.getName(), e);
}
}
}
} catch (IOException ioe) {
plugin().log(Level.WARNING, "Failed to register event classes", ioe);
}
}
}
private BukkitTask remap = null;
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void postPluginLoad(PluginEnableEvent e) {
if (!config.isEnabled()) {
return;
}
if (remap != null) {
remap.cancel();
}
remap = Bukkit.getScheduler().runTaskLater(plugin(), new Runnable(){
@Override
public void run() {
//event.block
rebounders.forEach(e -> reboundHandlers(e));
}
}, 60l); // 3 seconds
}
/**
* For tracked events we hijack priority ordering insertions
* We to grab the registeredlistener array, remove all listeners
* then readd, forcing _this_ plugin for _that_ event to be first(s) and last(s) in the list.
*
* @param clazz The Event class whose handlers we want to reorder
*/
private <T extends Event> void reboundHandlers(Class<T> clazz) {
try {
plugin().log(Level.INFO, "Rebound handlers for {0}", clazz.getName());
Method method = clazz.getDeclaredMethod("getHandlerList");
HandlerList handler = (HandlerList) method.invoke(null);
RegisteredListener[] listeners = handler.getRegisteredListeners();
ArrayList<RegisteredListener> newOrdering = new ArrayList<RegisteredListener>(listeners.length);
ArrayList<RegisteredListener> thisLow = new ArrayList<RegisteredListener>();
ArrayList<RegisteredListener> thisHigh = new ArrayList<RegisteredListener>();
for (RegisteredListener l : listeners) {
handler.unregister(l);
if (l.getPriority().equals(EventPriority.LOWEST)) {
if (l.getListener().equals(this)) {
thisLow.add(l);
continue;
}
} else if (l.getPriority().equals(EventPriority.MONITOR)) {
if (l.getListener().equals(this)) {
thisHigh.add(l);
continue;
}
}
newOrdering.add(l);
}
if (thisLow.isEmpty()) {
plugin().log("Failed to find low-order handler!");
}
thisLow.addAll(newOrdering);
if (thisHigh.isEmpty()) {
plugin().log("Failed to find high-order handler!");
} else {
thisLow.addAll(thisHigh);
}
try {
handler.registerAll(thisLow);
} catch (Exception e) {
plugin().log(Level.SEVERE, "reboundHandler failure to reassert listeners for " + clazz.getName(), e);
handler.registerAll(Arrays.asList(listeners));
}
} catch (NoSuchMethodException nsme) {
plugin().log(Level.WARNING, "This event does not have independent handlers, skipping " + clazz.getName());
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException m) {
plugin().log(Level.SEVERE, "Unable to retrieve handlers for " + clazz.getName(), m);
}
}
@Override
public void registerCommands() {
if (config.isEnabled()) {
plugin().log("Registering Insight's insight command");
plugin().registerCommand("insight", this);
}
}
@Override
public void dataBootstrap() {
if (config.isEnabled()) {
this.tracking = new ConcurrentHashMap<String, InsightStat>();
this.rebounders = new ArrayList<Class<? extends Event>>();
}
}
@Override
public void unregisterListeners() {
}
@Override
public void unregisterCommands() {
}
@Override
public void dataCleanup() {
if (config.isEnabled() || this.tracking != null) {
this.tracking.clear();
this.tracking = null;
this.rebounders = null;
}
}
@Override
public String status() {
if (config.isEnabled()) {
StringBuffer sb = new StringBuffer( "Passively providing insight into events:\n");
if (this.tracking != null) {
sb.append(ChatColor.WHITE).append(String.format(" %10s %10s %10s %7s %7s %7s %7s Event\n", "Count", "Canceled", "Handlers", "Min", "Avg", "Max", "StDev"));
this.tracking.forEach((k, v) -> {
sb.append(String.format(" %s%10d %10d %10d %s%7d %s%7.0f %s%7d %s%7.2f ",
ChatColor.WHITE, v.getSamples(), v.getCancels(), v.getHandlers(),
color(v.getMin()), v.getMin(),
color(v.getAverage()), v.getAverage(),
color(v.getMax()), v.getMax(),
ChatColor.WHITE, v.getStdev()));
sb.append(ChatColor.AQUA).append(k).append("\n");
});
}
return sb.toString();
} else {
return "Insights fully disabled.";
}
}
/**
* 10% of tick? Green.
* 15% of tick? Dark Green.
* 20% of tick? Yellow
* 30% of tick? Orange
* 50% of tick? Dark Red
* Above that? Red
*
* @param microseconds
* @return
*/
private ChatColor color(long microseconds) {
if (microseconds <= 5000) {
return ChatColor.GREEN;
} else if (microseconds <= 7500) {
return ChatColor.DARK_GREEN;
} else if (microseconds <= 10000) {
return ChatColor.YELLOW;
} else if (microseconds <= 15000) {
return ChatColor.GOLD;
} else if (microseconds <= 25000) {
return ChatColor.DARK_RED;
} else {
return ChatColor.RED;
}
}
private ChatColor color(double microseconds) {
return color((long) microseconds);
}
// -- SUPPORT
public void start(Event event) {
if (config.isEnabled()) {
InsightStat insight = this.tracking.compute(event.getEventName(), (key, stat) ->
(stat == null) ? new InsightStat() : stat
);
insight.begin(event.getHandlers().getRegisteredListeners().length);
}
}
public void end(Event event) {
if (config.isEnabled()) {
InsightStat insight = this.tracking.get(event.getEventName());
if (insight != null) {
if (event instanceof Cancellable) {
insight.end(((Cancellable) event).isCancelled());
} else {
insight.end(false);
}
}
}
}
public static InsightConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new InsightConfig(plugin, config);
}
private static class InsightStat {
// count of events
private long count;
// count of cancels
private long cancels;
// avg length of event (based on LOWEST vs. MONITOR)
private double avg;
// max length of event (based on LOWEST vs. MONITOR)
private long max;
// min length of event
private long min;
// variance of lengths
private double varsum;
private double var;
// rolling stdev of lengths
private double std;
// last execution handler list size
private long handlers;
private long lastStart;
public InsightStat() {
count = 0l;
cancels = 0l;
avg = 0l;
max = 0l;
min = 0l;
var = 0l;
std = 0l;
handlers = 0l;
lastStart = 0l;
}
/**
* Starts a specific capture for this; assumes runs in LOWEST for same-event.
* Will safely skip events that had no end() called.
*
* @param handlers
*/
public synchronized void begin(long handlers) {
this.lastStart = System.nanoTime() / 1000l;
this.handlers = handlers;
}
/**
* Ends a specific capture for this; assumes runs in MONITOR for same-event.
*/
public synchronized void end(boolean cancelled) {
long len = (System.nanoTime() / 1000l) - this.lastStart;
if (cancelled) {
cancels ++;
}
if (count == 0) {
avg = len;
max = len;
min = len;
varsum = 0d;
var = 0d;
std = 0d;
} else {
double oavg = avg;
avg = avg + ( len - avg )/ (double) (count + 1);
// estimate...
varsum += Math.pow(len - avg, 2);
var = varsum / count; // (pre-inc, see Bessel's correction)
std = Math.sqrt(var);
if (len > max) {
max = len;
}
if (len < min) {
min = len;
}
}
count++; // n+1
}
public long getHandlers() {
return handlers;
}
public double getAverage() {
return avg;
}
public long getSamples() {
return count;
}
public double getStdev() {
return std;
}
public long getMin() {
return min;
}
public long getMax() {
return max;
}
public long getCancels() {
return cancels;
}
}
}
@@ -10,6 +10,7 @@ import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -24,6 +25,7 @@ import org.bukkit.inventory.meta.BookMeta;
import com.programmerdan.minecraft.simpleadminhacks.CommandListener;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.HackBotConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.IntrobookConfig;
/**
@@ -114,7 +116,7 @@ public class Introbook extends SimpleHack<IntrobookConfig> implements Listener,
if (this.hasBook.remove(puid)) {
Inventory inv = dead.getInventory();
inv.addItem(config.getIntroBook());
inv.addItem(config.getIntroBook(dead));
}
}
@@ -131,7 +133,7 @@ public class Introbook extends SimpleHack<IntrobookConfig> implements Listener,
if (!alive.hasPlayedBefore() || this.hasBook.contains(puid)) {
this.hasBook.remove(puid);
Inventory inv = alive.getInventory();
inv.addItem(config.getIntroBook());
inv.addItem(config.getIntroBook(alive));
Introbook.bookGiftCount ++;
plugin().debug("Gave {0} an introbook", alive.getName());
}
@@ -149,7 +151,7 @@ public class Introbook extends SimpleHack<IntrobookConfig> implements Listener,
sb.append(ChatColor.WHITE).append("\n Introbooks given out: ");
sb.append(ChatColor.GREEN).append(Introbook.bookGiftCount);
sb.append("\n Current Introbook:");
ItemStack book = config.getIntroBook();
ItemStack book = config.getIntroBook(null);
if (book != null) {
BookMeta meta = (BookMeta) book.getItemMeta();
if (meta != null) {
@@ -194,9 +196,13 @@ public class Introbook extends SimpleHack<IntrobookConfig> implements Listener,
plugin().log(Level.INFO, "Sent introbook to {0}", args[0]);
p.sendMessage(ChatColor.GREEN + "You've been given an introductory book!");
Inventory inv = p.getInventory();
inv.addItem(config.getIntroBook());
inv.addItem(config.getIntroBook(p));
}
return true;
}
public static IntrobookConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new IntrobookConfig(plugin, config);
}
}
@@ -15,6 +15,7 @@ import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.craftbukkit.v1_10_R1.CraftServer;
import org.bukkit.craftbukkit.v1_10_R1.inventory.CraftInventoryPlayer;
import org.bukkit.craftbukkit.v1_10_R1.inventory.CraftItemStack;
@@ -30,6 +31,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.HackBotConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.InvControlConfig;
public class InvControl extends SimpleHack<InvControlConfig> implements CommandExecutor, Listener {
@@ -144,7 +146,7 @@ public class InvControl extends SimpleHack<InvControlConfig> implements CommandE
sb.append("\n Feet: ").append(pl_inv.getBoots());
for (int slot = 0; slot < 36; slot++) {
final ItemStack it = pl_inv.getItem(slot);
sb.append("\n ").append(slot).append(":").append(it);
sb.append("\n ").append(slot).append(":").append(it).append(ChatColor.RESET);
}
sender.sendMessage(sb.toString());
} else {
@@ -222,4 +224,7 @@ public class InvControl extends SimpleHack<InvControlConfig> implements CommandE
}
}
public static InvControlConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new InvControlConfig(plugin, config);
}
}
@@ -0,0 +1,185 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.InvisibleFixConfig;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.UUID;
/**
* Sometimes when joining, a player is invisible due to some kind of spigot bug that's been
* known since 2015 but unfixed.
*
* This is widely reported as a fix -- hide then show the player to the other players online.
* To do this safely w/o crashing all the things, I schedule the hide and show delayed
* so if we get a huge influx of joins this doesn't take forever.
*
* @author ProgrammerDan
*/
public class InvisibleFix extends SimpleHack<InvisibleFixConfig> implements Listener {
public static final String NAME = "InvisibleFix";
private AtomicInteger activeJoins = new AtomicInteger(0);
private LinkedTransferQueue<UUID> ltq = new LinkedTransferQueue<UUID>();
private BukkitTask recheckTask = null;
public InvisibleFix(SimpleAdminHacks plugin, InvisibleFixConfig config) {
super(plugin, config);
}
@EventHandler(priority=EventPriority.MONITOR, ignoreCancelled = true)
public void JoinEvent(PlayerJoinEvent event) {
if (!config.isEnabled()) return; // ignore if off
Player p = event.getPlayer();
if (p == null) return;
final UUID uuid = p.getUniqueId();
ltq.offer(uuid);
if (p.isOp() && config.getIgnoreOps()) return;
String tPerm = config.getIgnorePermission();
if (tPerm != null && p.hasPermission(tPerm)) return;
Bukkit.getScheduler().runTaskLater(plugin(), new Runnable() {
@Override
public void run() {
plugin().debug("Hopefully fixing any invisibility for {0}", uuid);
try {
if (uuid == null) return;
Player p = Bukkit.getPlayer(uuid);
if (p == null) return;
for (Player online : Bukkit.getOnlinePlayers()) {
if (online != p) {
online.hidePlayer(p);
p.hidePlayer(online);
online.showPlayer(p);
p.showPlayer(online);
}
}
} finally {
if (activeJoins.decrementAndGet() < 0) {
activeJoins.set(0);
}
}
}
}, activeJoins.incrementAndGet() * 20);
}
@Override
public void registerListeners() {
if (config.isEnabled()) {
plugin().log("Registering InvisibleFix listener");
plugin().registerListener(this);
plugin().log("Starting recheck task");
this.recheckTask = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin(),
new Runnable() {
@Override
public void run() {
if (!config.isEnabled()) return;
doRecheck();
}
},
config.getRecheckInterval(), config.getRecheckInterval());
}
}
private void doRecheck() {
// unspool, then recheck.
plugin().debug("Refixing invisibles...");
HashSet<Player> refix = new HashSet<Player>(config.getMaxPlayersPerRecheck());
LinkedList<UUID> skiplist = new LinkedList<UUID>();
int cnt = 0;
// accumulate
while (this.ltq.peek() != null && cnt < config.getMaxPlayersPerRecheck()) {
UUID fixerUUID = ltq.poll();
if (fixerUUID == null) break;
Player fixer = Bukkit.getPlayer(fixerUUID);
if (fixer != null) {
if (fixer.isOp() && config.getIgnoreOps()) {
skiplist.add(fixerUUID);
continue;
}
String tPerm = config.getIgnorePermission();
if (tPerm != null && fixer.hasPermission(tPerm)) {
skiplist.add(fixerUUID);
continue;
}
refix.add(fixer);
cnt++;
}
}
if (cnt > 0) {
plugin().debug("Found {0} players to refix", cnt);
// unblind
for (Player online : Bukkit.getOnlinePlayers()) {
for (Player fix : refix) {
if (!refix.contains(fix)) {
online.hidePlayer(fix);
fix.hidePlayer(online);
online.showPlayer(fix);
fix.hidePlayer(online);
}
}
}
// add them back.
for (Player fix : refix) {
this.ltq.offer(fix.getUniqueId());
}
}
for (UUID skip : skiplist) {
this.ltq.offer(skip);
}
}
@Override
public void registerCommands() {
}
@Override
public void dataBootstrap() {
activeJoins.set(0);
ltq.clear();
}
@Override
public void unregisterListeners() {
if (this.recheckTask != null) {
plugin().debug("Stopping InvisibleFix recheck task");
this.recheckTask.cancel();
}
}
@Override
public void unregisterCommands() {
}
@Override
public void dataCleanup() {
ltq.clear();
}
@Override
public String status() {
if (config != null && config.isEnabled()) {
return "InvisibleFix active";
} else {
return "InvisibleFix not active";
}
}
public static InvisibleFixConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new InvisibleFixConfig(plugin, config);
}
}
@@ -0,0 +1,179 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.server.v1_10_R1.EntityTracker;
import net.minecraft.server.v1_10_R1.EntityTrackerEntry;
import net.minecraft.server.v1_10_R1.WorldServer;
import net.minecraft.server.v1_10_R1.PacketPlayOutEntityTeleport;
import org.bukkit.craftbukkit.v1_10_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_10_R1.entity.CraftEntity;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleMoveEvent;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.InvisibleFixTwoConfig;
/**
* DISABLE WITH 1.11 -- This is for 1.10 only. Basically doing what Mojang was too
* busy / lazy to do before 1.11. Note this is not a perfect fix, but at least
* seems to deal with the largest areas of issue, namely, boats and horses,
* by forcing a location packet to be broadcast to other players that are tracking.
*
* Portions adapted and heavily modified from code by aadnk: https://gist.github.com/aadnk/3773860
*
* @author ProgrammerDan
*/
public class InvisibleFixTwo extends SimpleHack<InvisibleFixTwoConfig> implements Listener {
public static final String NAME = "InvisibleFixTwo";
private Server server;
private Map<Integer, Long> updateMap;
public InvisibleFixTwo(SimpleAdminHacks plugin, InvisibleFixTwoConfig config) {
super(plugin, config);
this.server = plugin.getServer();
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent event) {
if (!config.isEnabled()) return; // ignore if off
final Player player = event.getPlayer();
if (player.isOp() && config.getIgnoreOps()) return;
String tPerm = config.getIgnorePermission();
if (tPerm != null && player.hasPermission(tPerm)) return;
server.getScheduler().runTaskLater(plugin(), new Runnable() {
@Override
public void run() {
try {
forceUpdate(player);
} catch (NullPointerException npe) {
plugin().debug("Player offline, no forcefix");
}
}
}, config.getTeleportFixDelay());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerVehicleMove(VehicleMoveEvent move) {
if (!config.isEnabled()) return; // ignore if off
Vehicle vehicle = move.getVehicle();
if (vehicle == null) return;
Entity e = vehicle.getPassenger();
if (e == null) return;
if (e.isOp() && config.getIgnoreOps()) return;
String tPerm = config.getIgnorePermission();
if (tPerm != null && e.hasPermission(tPerm)) return;
forceUpdate(e);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMoveInVehicle(PlayerMoveEvent move) {
if (!config.isEnabled()) return; // ignore if off
Player p = move.getPlayer();
if (p == null) return;
if (p.isOp() && config.getIgnoreOps()) return;
String tPerm = config.getIgnorePermission();
if (tPerm != null && p.hasPermission(tPerm)) return;
Entity vehicle = p.getVehicle();
if (vehicle == null) return;
forceUpdate(p);
}
private void forceUpdate(Entity entity) {
Long last = updateMap.get(entity.getEntityId());
long now = System.currentTimeMillis();
if (last == null || last < now - config.getFixInterval()) {
updateEntities(entity);
updateMap.put(entity.getEntityId(), now);
//plugin().debug("Forcing packet-based update for {0}", entity);
}
}
private void updateEntities(final Entity entity) {
World world = entity.getWorld();
WorldServer worldServer = ((CraftWorld) world).getHandle();
EntityTracker tracker = worldServer.tracker;
EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities
.get(entity.getEntityId());
net.minecraft.server.v1_10_R1.Entity eEntity = getNmsEntity(entity);
entry.broadcast(new PacketPlayOutEntityTeleport(eEntity));
}
private net.minecraft.server.v1_10_R1.Entity getNmsEntity(final Entity entity) {
CraftEntity craftEntity = (CraftEntity) entity;
return craftEntity.getHandle();
}
@Override
public void registerListeners() {
if (config.isEnabled()) {
plugin().log("Registering InvisibleFixTwo listener");
plugin().registerListener(this);
}
}
@Override
public void registerCommands() {
}
@Override
public void dataBootstrap() {
updateMap = new HashMap<Integer, Long>();
}
@Override
public void unregisterListeners() {
}
@Override
public void unregisterCommands() {
}
@Override
public void dataCleanup() {
if (updateMap != null) {
updateMap.clear();
}
}
@Override
public String status() {
if (config != null && config.isEnabled()) {
return "InvisibleFixTwo active";
} else {
return "InvisibleFixTwo not active";
}
}
public static InvisibleFixTwoConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new InvisibleFixTwoConfig(plugin, config);
}
}
@@ -7,6 +7,7 @@ import java.util.logging.Level;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -21,6 +22,7 @@ import org.bukkit.ChatColor;
import com.programmerdan.minecraft.simpleadminhacks.BroadcastLevel;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.HackBotConfig;
import com.programmerdan.minecraft.simpleadminhacks.configs.NewfriendAssistConfig;
/**
@@ -252,5 +254,8 @@ public class NewfriendAssist extends SimpleHack<NewfriendAssistConfig> implement
return true;
}
}
public static NewfriendAssistConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new NewfriendAssistConfig(plugin, config);
}
}
@@ -0,0 +1,132 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.ReinforcedChestBreakConfig;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import vg.civcraft.mc.citadel.Citadel;
import vg.civcraft.mc.citadel.ReinforcementManager;
import java.util.*;
import java.util.logging.Level;
/**
* Sends every 3 minutes a message to the admins if a chest is broken
*/
public class ReinforcedChestBreak extends SimpleHack<ReinforcedChestBreakConfig> implements Listener {
public static String NAME = "ReinforcedChestBreak";
private ReinforcementManager manager;
private Messenger messenger;
private Set<String> messages;
public ReinforcedChestBreak(SimpleAdminHacks plugin, ReinforcedChestBreakConfig config) {
super(plugin, config);
}
public static ReinforcedChestBreakConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new ReinforcedChestBreakConfig(plugin, config);
}
@Override
public void registerListeners() {
if (!config.isEnabled()) return;
Bukkit.getPluginManager().registerEvents(this, plugin());
}
@Override
public void registerCommands() {}
@Override
public void dataBootstrap() {
if (!config.isEnabled()) return;
messages = new HashSet<>();
manager = Citadel.getReinforcementManager();
messenger = new Messenger();
Bukkit.getScheduler().runTaskTimer(plugin(), messenger, 0, config.getDelay() * 20);
}
@Override
public void unregisterListeners() {}
@Override
public void unregisterCommands() {}
@Override
public void dataCleanup() {}
@Override
public String status() {
return "Delay: " + config.getDelay();
}
/**
* Gets fired by the BLockBreakEvent and checks if the block is reinforced
* @param eve BLockBreakEvent
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true)
public void onBlockBreak(BlockBreakEvent eve) {
if (!config.isEnabled()) return;
if (eve.getPlayer() == null) return;
if (eve.getBlock() == null) return;
Material bbe = eve.getBlock().getType();
if (bbe == null) return;
if (manager == null) manager = Citadel.getReinforcementManager();
if (Material.CHEST.equals(bbe) || Material.TRAPPED_CHEST.equals(bbe)
|| Material.ENDER_CHEST.equals(bbe) || Material.FURNACE.equals(bbe)
|| Material.BURNING_FURNACE.equals(bbe) || Material.DISPENSER.equals(bbe)
|| Material.DROPPER.equals(bbe) || Material.HOPPER.equals(bbe)) {
if(manager.isReinforced(eve.getBlock())) {
String name = eve.getPlayer().getDisplayName();
Location loc = eve.getBlock().getLocation();
String msg = setVars(name,
String.valueOf(loc.getBlockX()),
String.valueOf(loc.getBlockY()),
String.valueOf(loc.getBlockZ()));
if(messages.add(msg)) {
plugin().log(Level.INFO, msg);
}
}
}
}
/**
* Builds the message
* @param name the player name
* @param x block x
* @param y block y
* @param z block z
* @return returns the builded String
*/
private String setVars(String name, String x, String y, String z) {
return ChatColor.translateAlternateColorCodes('&', config.getMessage()
.replace("%player%", name)
.replace("%x%", x)
.replace("%y%", y)
.replace("%z%", z));
}
private class Messenger implements Runnable {
@Override
public void run() {
for (String message: messages) {
plugin().serverOperatorBroadcast(message);
}
messages.clear();
}
}
}
@@ -0,0 +1,111 @@
package com.programmerdan.minecraft.simpleadminhacks.hacks;
import java.util.logging.Level;
import org.bukkit.block.Block;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks;
import com.programmerdan.minecraft.simpleadminhacks.SimpleHack;
import com.programmerdan.minecraft.simpleadminhacks.configs.SanityHackConfig;
public class SanityHack extends SimpleHack<SanityHackConfig> implements Listener {
public static final String NAME = "SanityHack";
private final int trackingLevel;
private int caughtPlayers; // Just to give a useful message in status tbh
public SanityHack(SimpleAdminHacks plugin, SanityHackConfig config) {
super(plugin, config);
this.trackingLevel = config.getTrackingLevel();
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled=true)
public void onBlockBreak(BlockBreakEvent event){
if(!config.isEnabled()) return;
if(!config.isTrackingBreak()) return;
Block block = event.getBlock();
Player player = event.getPlayer();
if (player == null) return;
if (block == null) return;
if(block.getY() < trackingLevel){
plugin().log(Level.INFO, "Player({0}, {1}) caught breaking a block({2}) below Y {3} at {4}", player.getName(), player.getUniqueId().toString(),
block.getType().toString(), trackingLevel, block.getLocation());
caughtPlayers++;
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled=true)
public void onBlockPlace(BlockPlaceEvent event){
if(!config.isEnabled()) return;
if(!config.isTrackingPlace()) return;
Block block = event.getBlock();
Player player = event.getPlayer();
if (player == null) return;
if (block == null) return;
if(block.getY() < trackingLevel && player != null){
plugin().log(Level.INFO, "Player({0}, {1}) caught placing a block({2}) below Y {3} at {4}", player.getName(), player.getUniqueId().toString(),
block.getType().toString(), trackingLevel, block.getLocation());
caughtPlayers++;
}
}
@Override
public void registerListeners() {
if (config.isEnabled()) {
plugin().log("Registering SanityHack listeners");
plugin().registerListener(this);
}
}
@Override
public void registerCommands() {
}
@Override
public void dataBootstrap() {
caughtPlayers = 0;
}
@Override
public void unregisterListeners() {
}
@Override
public void unregisterCommands() {
}
@Override
public void dataCleanup() {
caughtPlayers = 0;
}
@Override
public String status() {
if (!config.isEnabled()) {
return "Sanity Hack listening disabled.";
}
return caughtPlayers + " caught below Y " + trackingLevel;
}
public static SanityHackConfig generate(SimpleAdminHacks plugin, ConfigurationSection config) {
return new SanityHackConfig(plugin, config);
}
}
File diff suppressed because it is too large. Load diff
+123 -34
View File
@@ -1,4 +1,4 @@
# Effectively Modern Humbug. For 1.9 ground up.
# Effectively Modern Humbug. For 1.10 ground up.
configuration_file_version: 1
@@ -6,40 +6,75 @@ debug: true
broadcast_permission: simpleadmin.broadcast
hacks:
experimental:
name: Experimental
Experimental:
enabled: true
combatspy: true
teleportspy: true
combatspy: false
teleportspy: false
postteleport:
spy: true
spy: false
count: 10
game_features:
name: GameFeatures
enabled: false
GameFeatures:
enabled: true
disablePotatoXP: false
# Villager trading is not like above and true = able to trade and false = not able
villagerTrading: false
# Wither spawn is also true = enabled and false = disabled
witherSpawning: false
# Enderchests is also true = enabled and false = disabled
enderChestPlacement: false
elytra: off
chorus_fruit: off
game_fixes:
name: GameFixes
enabled: false
nether_bed_placement: off
game_tuning:
name: GameTuning
enabled: false
str2: replace_str1ext
pearl_gravity: 0.3
combattag_announce:
name: CombatTagAnnounce
# WeepingAngel bans people when they die, the numbers are in minutes.
weepingAngel:
enabled: false
environment: 1
playerKill: 5
GameFixes:
enabled: true
# Does not fully work atm
blockElytraBreakBug: false
damageOnElytraBreakBug: 1.0
canStorageTeleport: false
stopHopperDupe: true
stopRailDupe: true
stopEndPortalDeletion: true
GameTuning:
enabled: true
chunkLimits:
enabled: true
exceededMessage: '&9Limit reached for this chunk, you cannot place more of this type of block.'
tileEntities:
HOPPER: 4
PISTON_BASE: 4
PISTON_STICKY_BASE: 4
DISPENSER: 4
DROPPER: 4
REDSTONE_COMPARATOR: 4
exempt:
- Steve
- Alex
daytimeBed:
enabled: true
spawnSetMessage: '&7Your spawn has been set.'
# Makes nether ratio 1 to 1
oneToOneNether: false
# Return nether portal, true = enabled false = disabled
returnNetherPortal: true
# Disables minecart inventories (trying to fix dupe issues)
# enabled = able to right click disabled = not able to right click
chestedMinecartInventories: true
hopperMinecartInventories: true
CTAnnounce:
enabled: false
delay: 10000
message: '%Victim% was combat tagged by %Attacker%'
broadcast: [OP, CONSOLE]
newfriend:
name: NewfriendAssist
CrazyCratesAnnounce:
enabled: false
NewfriendAssist:
enabled: true
announce:
message: '%Player% is brand new!'
broadcast: [PERM, CONSOLE]
announce: '&f%Player% is brand new!'
broadcast: [PERM, CONSOLE]
helptips: on
helptips_end: 20m
introkit:
@@ -65,17 +100,17 @@ hacks:
- "This world is unforgiving."
- "Be sure to get a good night's"
- "rest soon."
introbook:
name: Introbook
Introbook:
enabled: true
follow: true
contents:
title: Welcome to Devoted
title: Welcome, ${player}
author: Bonkill
pages:
- " &oWelcome to Devoted, ${player}!"
- " &oWhat is Devoted?\n\"A new type of Minecraft server built towards providing a competitive, high stakes, immersive experience controlled by the players.\"&r\n-ShadedJon, server owner"
- " &oWhat is Devoted?&r\n&lNOT just survival&r\nAdmins are hands-off and only handle glitches and hackers\n&lNOT just chaos&r\nPlugins allow players and towns to enforce their own rules\n&lNOT just roleplay&r\nNobody pretends -- conflict is genuine and heated"
- " &oBasic Mechanics&r\n-The world has many continents\n-They're separated by ocean.\n-Limited chat range\n-Respawn in random area unless you sleep\n-Mobs spawn sparsely\n-Vanilla Nether\n-Ore veins and vanilla ore spawning."
- " &oBasic Mechanics&r\n-The world is custom generated\n-Many custom biomes\n-Limited chat range\n-Respawn in random area unless you sleep\n-Mobs spawn sparsely\n-Vanilla Nether\n-Ore veins and custom ore spawning."
- " &oDevoted Mods&r\n &lCitadel&r:\n Reinforces things so it takes numerous breaks to destroy them. Locks some things too.\n&lMore Info:&r &oVisual Guide:&r imgur.com/BnlL2 &oWiki Page:&r tinyurl.com/citadelmod\n&oType \"/help citadel\" in chat to get commands"
- " &lPrison Pearl&r:\n Ender Pearls trap players in the end. Others can steal back your pearl and free you -- you always know where it is so they cannot hide it\n&lMore Info:&r &oVisual Guide:&r imgur.com/XbhkK &oWiki Page:&r tinyurl.com/prisperl"
- " &lJuke Alert&r:\n Creates 'Juke' blocks that record player activity in radius. If you steal, grief, or trespass -- people will know about it & put a bounty for you to be pearled.\n&lMore Info:&r &oWiki Page:&r tinyurl.com/snitchblock"
@@ -87,9 +122,8 @@ hacks:
- " &lBrewery&r:\n Custom brewing of booze and more.\n&lMore Info:&r &oBukkit Page:&r tinyurl.com/bukkitbrewp"
- " &oFurther Info&r\nVisit our subreddit at: &oreddit.com/r/Devoted&r"
- "We extend our appreciation to the devs, admins, and players at: &oreddit.com/r/Civcraft"
hackbot:
name: HackBot
enabled: true
HackBot:
enabled: false
bots:
Rando:
name: Rando4800xXx
@@ -111,6 +145,61 @@ hacks:
y: 40.0
world: world
x: 50.0
invctrl:
name: InvControl
InvControl:
enabled: true
InvisibleFixTwo:
enabled: true
# in ticks -- ticks to wait after teleport to force update
teleportDelay: 5
# in milliseconds -- min time between forced updates if active
minInterval: 100
ignoreOps: true
ignorePermission: sah.allowInvisible
InvisibleFix:
enabled: true
ignoreOps: true
ignorePermission: sah.allowInvisible
# in ticks
recheckInterval: 500
ReinforcedChestBreak:
enabled: true
# in seconds
delay: 180
message: "&4%player% is raiding a chest at %x% %y% %z%."
SanityHack:
enabled: true
trackPlace: true
trackBreak: true
belowYLevel: 7
TimingsHack:
enabled: true
BadBoyWatch:
enabled: true
trackingDepth: 16
clearOnMatch: true
watchedMaterials:
- CHEST
- FURNACE
- DROPPER
- DISPENSER
- JUKEBOX
- NOTE_BLOCK
- BEACON
- ENDER_CHEST
- SPONGE
- HOPPER
- PISTON_BASE
- PISTON_STICKY_BASE
Insight:
enabled: false
into:
- org.bukkit.event.player
- org.bukkit.event.entity
- org.bukkit.event.block
- org.bukkit.event.inventory
- org.bukkit.event.enchantment
- org.bukkit.event.hanging
- org.bukkit.event.painting
- org.bukkit.event.vehicle
- org.bukkit.event.weather
- org.bukkit.event.world
+37 -1
View File
@@ -3,7 +3,7 @@ main: com.programmerdan.minecraft.simpleadminhacks.SimpleAdminHacks
author: ProgrammerDan
authors: []
version: ${project.version}
softdepend: [CombatTagPlus, ProtocolLib, NameLayer, NPCLib]
softdepend: [CombatTagPlus, ProtocolLib, NameLayer, NPCLib, CrazyCrates]
commands:
hacks:
description: Shows the various hacks and fixes supported by this plugin and controls them
@@ -47,6 +47,38 @@ commands:
/<command> all spawn
/<command> [status|list]
permission: simpleadmin.bots
showtimings:
description: Get an in-game map that shows tick length and distribution over time (2 mins, updated continuously)
usage: /<command>
permission: simpleadmin.timings
bindtimings:
description: Get an in-game map that shows relative tick usage by a targetted class
usage: /<command> classname
permission: simpleadmin.timings
thresholdtimings:
description: Get periodic reports on misbehaving classes using a lot of tick time, based on a threshold (as a factor * avg tick)
usage: /<command> threshold
permission: simpleadmin.timings
listtimings:
description: Show all Classes that can be used with bindtimings; this is a progressive report function that continues until nothing new is seen
usage: /<command>
permission: simpleadmin.timings
stoptimings:
description: Stop any advanced timing tracking. Does not stop /showtimings reults.
usage: /<command>
permission: simpleadmin.timings
insight:
description: Event insights. Passively tracks stuff, or actively represents events for players.
usage: |
/<command> on
/<command> active - Activates active monitoring of events. For continuous events, pings you with a summary update every 5 seconds. For initiated events, pings you immediately.
/<command> off
/<command> inactive - Deactivates active monitoring.
/<command> list - Shows all monitored events and some stats about each
/<command> list <event> - Shows all monitored events that match the passed param <event>
/<command> periodic <time> - Shows a color differentiated summary of monitored events and counts that have changed, with <time> as ticks inbetween display
/<command> periodic <event> <time> - Shows a color differentiated summary of monitored evenst that match the passed param <event>, with <time> as ticks inbetween display
/<command> - Just shows this description
permissions:
simpleadmin.*:
description: Gives access to all SimpleAdminHacks commands
@@ -61,6 +93,7 @@ permissions:
simpleadmin.book: true
simpleadmin.broadcast: true
simpleadmin.bots: true
simpleadmin.timings: true
simpleadmin.hacks:
description: Allows control of the hacks and settings which simpleadminhacks supports
default: op
@@ -88,3 +121,6 @@ permissions:
simpleadmin.bots:
description: Gives the ability to create NPCs (bots)
default: op
simpleadmin.timings:
description: Gives the ability to view and manage /...timings commands
default: op