mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-10-06 10:27:26 +02:00
- Some code cleanup
- measure serverside lag in a seperate thread, once per second - (Server) lag resistant "more packets" check - minor adjustments to default config settings - fixed a bug that would deactivate the durability check if the morepackets check got deactivated
This commit is contained in:
parent
c0a124ac61
commit
5b56fc7302
@ -3,7 +3,7 @@ name: NoCheat
|
||||
author: Evenprime
|
||||
|
||||
main: cc.co.evenprime.bukkit.nocheat.NoCheat
|
||||
version: 2.01a
|
||||
version: 2.01b
|
||||
|
||||
permissions:
|
||||
|
||||
|
@ -117,8 +117,8 @@ public class DefaultConfiguration {
|
||||
morePacketsNode.add(actions);
|
||||
|
||||
actions.add(0, "morepacketsLow moveCancel");
|
||||
actions.add(15, "morepacketsMed moveCancel");
|
||||
actions.add(30, "morepacketsHigh moveCancel");
|
||||
actions.add(30, "morepacketsMed moveCancel");
|
||||
actions.add(60, "morepacketsHigh moveCancel");
|
||||
}
|
||||
|
||||
/**** MOVING.NOCLIP ****/
|
||||
|
@ -36,6 +36,12 @@ public class NoCheat extends JavaPlugin {
|
||||
private BlockPlaceEventManager eventBlockPlaceManager;
|
||||
private PlayerInteractEventManager eventPlayerInteractManager;
|
||||
|
||||
private int taskId = -1;
|
||||
private int ingameseconds = 0;
|
||||
private long lastIngamesecondTime = 0L;
|
||||
private long lastIngamesecondDuration = 0L;
|
||||
private boolean skipCheck = false;
|
||||
|
||||
private ActionManager action;
|
||||
|
||||
public NoCheat() {
|
||||
@ -44,6 +50,10 @@ public class NoCheat extends JavaPlugin {
|
||||
|
||||
public void onDisable() {
|
||||
|
||||
if(taskId != -1) {
|
||||
this.getServer().getScheduler().cancelTask(taskId);
|
||||
taskId = -1;
|
||||
}
|
||||
PluginDescriptionFile pdfFile = this.getDescription();
|
||||
|
||||
if(conf != null)
|
||||
@ -59,7 +69,7 @@ public class NoCheat extends JavaPlugin {
|
||||
this.data = new DataManager();
|
||||
|
||||
this.action = new ActionManager(log);
|
||||
|
||||
|
||||
// parse the nocheat.yml config file
|
||||
this.conf = new ConfigurationManager(this.getDataFolder().getPath(), action);
|
||||
|
||||
@ -71,6 +81,24 @@ public class NoCheat extends JavaPlugin {
|
||||
|
||||
PluginDescriptionFile pdfFile = this.getDescription();
|
||||
|
||||
if(taskId == -1) {
|
||||
taskId = this.getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// If the previous second took to long, skip checks during this second
|
||||
skipCheck = lastIngamesecondDuration > 1500;
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
lastIngamesecondDuration = time - lastIngamesecondTime;
|
||||
if(lastIngamesecondDuration < 1000) lastIngamesecondDuration = 1000;
|
||||
lastIngamesecondTime = time;
|
||||
ingameseconds++;
|
||||
}
|
||||
}, 0, 20);
|
||||
}
|
||||
|
||||
log.logToConsole(LogLevel.LOW, "[NoCheat] version [" + pdfFile.getVersion() + "] is enabled.");
|
||||
}
|
||||
|
||||
@ -89,4 +117,16 @@ public class NoCheat extends JavaPlugin {
|
||||
public ActionManager getActionManager() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public int getIngameSeconds() {
|
||||
return ingameseconds;
|
||||
}
|
||||
|
||||
public long getIngameSecondDuration() {
|
||||
return lastIngamesecondDuration;
|
||||
}
|
||||
|
||||
public boolean skipCheck() {
|
||||
return skipCheck;
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,12 @@ package cc.co.evenprime.bukkit.nocheat;
|
||||
public class Permissions {
|
||||
|
||||
private final static String _NOCHEAT = "nocheat";
|
||||
private final static String _ADMIN = _NOCHEAT + ".admin";
|
||||
private final static String _CHECKS = _NOCHEAT + ".checks";
|
||||
private final static String _MOVE = _CHECKS + ".moving";
|
||||
private final static String _BLOCKBREAK = _CHECKS + ".blockbreak";
|
||||
public static final String _BLOCKPLACE = _CHECKS + ".blockplace";
|
||||
public final static String _INTERACT = _CHECKS + ".interact";
|
||||
private final static String _BLOCKPLACE = _CHECKS + ".blockplace";
|
||||
private final static String _INTERACT = _CHECKS + ".interact";
|
||||
|
||||
public final static String MOVE = _CHECKS + ".moving.*";
|
||||
public final static String MOVE_FLY = _MOVE + ".flying";
|
||||
@ -34,7 +35,6 @@ public class Permissions {
|
||||
public final static String BLOCKPLACE_ONLIQUID = _BLOCKPLACE + ".onliquid";
|
||||
public static final String BLOCKPLACE_REACH = _BLOCKPLACE + ".reach";
|
||||
|
||||
private final static String _ADMIN = _NOCHEAT + ".admin";
|
||||
|
||||
public final static String ADMIN_CHATLOG = _ADMIN + ".chatlog";
|
||||
|
||||
|
@ -19,7 +19,7 @@ import org.bukkit.plugin.Plugin;
|
||||
*/
|
||||
public class ConsoleCommandSender implements CommandSender {
|
||||
|
||||
private Server server;
|
||||
private final Server server;
|
||||
private final PermissibleBase perm = new PermissibleBase(this);
|
||||
|
||||
public ConsoleCommandSender(Server server) {
|
||||
|
@ -21,7 +21,7 @@ import cc.co.evenprime.bukkit.nocheat.data.BlockBreakData;
|
||||
*/
|
||||
public class ReachCheck {
|
||||
|
||||
private ActionExecutor action;
|
||||
private final ActionExecutor action;
|
||||
|
||||
public ReachCheck(NoCheat plugin) {
|
||||
this.action = new ActionExecutorWithHistory(plugin);
|
||||
|
@ -19,7 +19,7 @@ import cc.co.evenprime.bukkit.nocheat.data.BlockPlaceData;
|
||||
*/
|
||||
public class OnLiquidCheck {
|
||||
|
||||
private ActionExecutor action;
|
||||
private final ActionExecutor action;
|
||||
|
||||
public OnLiquidCheck(NoCheat plugin) {
|
||||
action = new ActionExecutorWithHistory(plugin);
|
||||
|
@ -24,7 +24,7 @@ import cc.co.evenprime.bukkit.nocheat.data.BlockPlaceData;
|
||||
*/
|
||||
public class ReachCheck {
|
||||
|
||||
private ActionExecutor action;
|
||||
private final ActionExecutor action;
|
||||
|
||||
public ReachCheck(NoCheat plugin) {
|
||||
this.action = new ActionExecutorWithHistory(plugin);
|
||||
|
@ -29,7 +29,7 @@ public class InteractCheck {
|
||||
|
||||
boolean cancel = false;
|
||||
|
||||
final boolean durability = cc.moving.morePacketsCheck && !player.hasPermission(Permissions.INTERACT_DURABILITY);
|
||||
final boolean durability = cc.interact.durabilityCheck && !player.hasPermission(Permissions.INTERACT_DURABILITY);
|
||||
|
||||
if(durability) {
|
||||
// It's so simple, I'll just do the check in place
|
||||
|
@ -6,7 +6,6 @@ import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import cc.co.evenprime.bukkit.nocheat.NoCheat;
|
||||
import cc.co.evenprime.bukkit.nocheat.Permissions;
|
||||
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutor;
|
||||
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutorWithHistory;
|
||||
import cc.co.evenprime.bukkit.nocheat.actions.types.LogAction;
|
||||
@ -29,21 +28,71 @@ public class MorePacketsCheck {
|
||||
|
||||
private final ActionExecutor action;
|
||||
|
||||
private final long timeframe = 1000;
|
||||
private final double packetsPerTimeframe = 22;
|
||||
|
||||
private final double lowLimit = -20;
|
||||
private final long packetsPerTimeframe = 22;
|
||||
private final double bufferLimit = 30;
|
||||
private final NoCheat plugin;
|
||||
|
||||
public MorePacketsCheck(NoCheat plugin) {
|
||||
this.action = new ActionExecutorWithHistory(plugin);
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ok, lets write down the internal logic, to not forget what I wanted to
|
||||
* do when I started it:
|
||||
*
|
||||
* A second on the server has 20 ticks
|
||||
* A second on the client has 20 ticks
|
||||
* A unmodified client with no lag will send 1 packet per tick = 20 packets
|
||||
* Ideally checking for 20 packets/second on the server would work
|
||||
*
|
||||
* But a client may send 10 packets in second 1, and 30 packets in second 2
|
||||
* due to lag. This should still be allowed, therefore we give a "buffer".
|
||||
* If a player doesn't use all of his 20 move packets in one second, he may
|
||||
* carry over unused packets to the next (the buffer is limited in size for
|
||||
* obvious reasons).
|
||||
*
|
||||
* But the server may not be able to process all packets in time, e.g.
|
||||
* because it is busy saving the world.
|
||||
*
|
||||
* Well that sounded strange...
|
||||
* Admin: "Hey server, what is taking you so long?"
|
||||
* Server: "I'm saving the world!"
|
||||
* Admin: "o_O"
|
||||
*
|
||||
* Contrary to client lag, serverside lag could be measured. So let's do
|
||||
* that. A task will be executed every 20 server ticks, storing the time it
|
||||
* took the server to process those ticks. If it's much more than 1 second,
|
||||
* the server was busy, and packets may have piled up during that time. So a
|
||||
* good idea would be to ignore the following second completely, as it will
|
||||
* be used to process the stacked-up packets, getting the server back in
|
||||
* sync with the timeline.
|
||||
*
|
||||
* Server identified as being busy -> ignore the second that follows. If
|
||||
* that second the server is still busy -> ignore the second after that too.
|
||||
*
|
||||
* What's with the second during which the server is busy? Can there be more
|
||||
* packets during that time? Sure. But should we care? No, this initial lag
|
||||
* can be mitigated by using the time it took to do the 20 ticks and factor
|
||||
* it with the limit for packets. Problem solved. The only real problem are
|
||||
* packets that stack up in one second to get processed in the next, which
|
||||
* is what the "ignoring" is for.
|
||||
*
|
||||
* So the general course of action would be:
|
||||
*
|
||||
* 1. Collect packets processed within 20 server ticks = packetCounter
|
||||
* 2. Measure time taken for those 20 server ticks = elapsedTime
|
||||
* 3. elapsedTime >> 1 second -> ignore next check
|
||||
* 4. limit = 22 x elapsedTime
|
||||
* 5. difference = limit - packetCounter
|
||||
* 6. buffer = buffer + difference; if(buffer > 20) buffer = 20;
|
||||
* 7. if(buffer < 0) -> violation of size "buffer".
|
||||
* 8. reset packetCounter, wait for next 20 ticks to pass by.
|
||||
*
|
||||
*/
|
||||
public Location check(Player player, ConfigurationCache cc, MovingData data) {
|
||||
|
||||
if(!cc.moving.morePacketsCheck || player.hasPermission(Permissions.MOVE_MOREPACKETS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Location newToLocation = null;
|
||||
|
||||
data.morePacketsCounter++;
|
||||
@ -51,46 +100,58 @@ public class MorePacketsCheck {
|
||||
data.morePacketsSetbackPoint = player.getLocation();
|
||||
}
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
// Is at least half a second gone by?
|
||||
if(currentTime - timeframe > data.morePacketsLastTime) {
|
||||
int ingameSeconds = plugin.getIngameSeconds();
|
||||
// Is at least a second gone by and has the server at least processed 20
|
||||
// ticks since last time
|
||||
if(ingameSeconds != data.lastElapsedIngameSeconds) {
|
||||
|
||||
// Are we over the 10 event limit for that time frame now?
|
||||
final double change = data.morePacketsCounter - packetsPerTimeframe * ((double) (currentTime - data.morePacketsLastTime)) / ((double) timeframe);
|
||||
long limit = (packetsPerTimeframe * plugin.getIngameSecondDuration()) / 1000L;
|
||||
|
||||
if(change > 0) {
|
||||
data.morePacketsOverLimit += change;
|
||||
} else if(data.morePacketsOverLimit + change > lowLimit) {
|
||||
data.morePacketsOverLimit += change;
|
||||
} else if(data.morePacketsOverLimit > lowLimit) {
|
||||
data.morePacketsOverLimit = lowLimit;
|
||||
}
|
||||
long difference = limit - data.morePacketsCounter;
|
||||
|
||||
if(data.morePacketsOverLimit > 0 && data.morePacketsCounter > packetsPerTimeframe) {
|
||||
data.morePacketsBuffer = data.morePacketsBuffer + difference;
|
||||
if(data.morePacketsBuffer > bufferLimit)
|
||||
data.morePacketsBuffer = bufferLimit;
|
||||
// Are we over the 22 event limit for that time frame now? (limit
|
||||
// increases with time)
|
||||
|
||||
int packetsAboveLimit = (int) -data.morePacketsBuffer;
|
||||
|
||||
if(data.morePacketsBuffer < 0)
|
||||
data.morePacketsBuffer = 0;
|
||||
|
||||
// Should we react? Only if the check doesn't get skipped and we
|
||||
// went over the limit
|
||||
if(!plugin.skipCheck() && packetsAboveLimit > 0) {
|
||||
data.morePacketsViolationLevel += packetsAboveLimit;
|
||||
|
||||
HashMap<String, String> params = new HashMap<String, String>();
|
||||
|
||||
params.put(LogAction.PACKETS, String.valueOf(data.morePacketsCounter - packetsPerTimeframe));
|
||||
// Packets above limit
|
||||
params.put(LogAction.PACKETS, String.valueOf(data.morePacketsCounter - limit));
|
||||
params.put(LogAction.CHECK, "morepackets");
|
||||
|
||||
boolean cancel = false;
|
||||
cancel = action.executeActions(player, cc.moving.morePacketsActions, (int) data.morePacketsOverLimit, params, cc);
|
||||
cancel = action.executeActions(player, cc.moving.morePacketsActions, (int) data.morePacketsViolationLevel, params, cc);
|
||||
|
||||
if(cancel) {
|
||||
newToLocation = data.morePacketsSetbackPoint != null ? data.morePacketsSetbackPoint : player.getLocation();
|
||||
// Only do the cancel if the player didn't change worlds
|
||||
// inbetween
|
||||
if(cancel && player.getWorld().equals(data.morePacketsSetbackPoint.getWorld())) {
|
||||
newToLocation = data.morePacketsSetbackPoint;
|
||||
}
|
||||
}
|
||||
|
||||
// No new setbackLocation was chosen
|
||||
if(newToLocation == null) {
|
||||
data.morePacketsSetbackPoint = player.getLocation();
|
||||
}
|
||||
|
||||
if(data.morePacketsOverLimit > 0)
|
||||
data.morePacketsOverLimit *= 0.8; // Shrink the "over limit"
|
||||
// value by 20 % every second
|
||||
data.morePacketsLastTime = currentTime;
|
||||
data.morePacketsCounter = 0;
|
||||
if(data.morePacketsViolationLevel > 0)
|
||||
// Shrink the "over limit" value by 20 % every second
|
||||
data.morePacketsViolationLevel *= 0.8;
|
||||
|
||||
data.morePacketsCounter = 0; // Count from zero again
|
||||
data.lastElapsedIngameSeconds = ingameSeconds;
|
||||
}
|
||||
|
||||
return newToLocation;
|
||||
|
@ -165,7 +165,7 @@ public class MovingEventHelper {
|
||||
return((value & NONSOLID) == NONSOLID);
|
||||
}
|
||||
|
||||
public final boolean isLadder(int value) {
|
||||
private final boolean isLadder(int value) {
|
||||
return((value & LADDER) == LADDER);
|
||||
}
|
||||
|
||||
|
@ -43,8 +43,6 @@ public class ConfigurationManager {
|
||||
private final static String defaultActionFileName = "default_actions.txt";
|
||||
private final static String descriptionsFileName = "descriptions.txt";
|
||||
|
||||
public final static String rootConfigFolder = "plugins/NoCheat/"; // default
|
||||
|
||||
private final Map<String, ConfigurationCache> worldnameToConfigCacheMap = new HashMap<String, ConfigurationCache>();
|
||||
|
||||
// Only use one filehandler per file, therefore keep open filehandlers in a
|
||||
@ -96,7 +94,7 @@ public class ConfigurationManager {
|
||||
|
||||
}
|
||||
|
||||
public void initializeActions(String rootConfigFolder, ActionManager action) {
|
||||
private void initializeActions(String rootConfigFolder, ActionManager action) {
|
||||
|
||||
FlatActionParser parser = new FlatActionParser();
|
||||
|
||||
|
@ -8,8 +8,8 @@ package cc.co.evenprime.bukkit.nocheat.config.tree;
|
||||
*/
|
||||
public class ActionOption extends ChildOption implements Comparable<ActionOption> {
|
||||
|
||||
private int treshold;
|
||||
private String value;
|
||||
private final int treshold;
|
||||
private String value;
|
||||
|
||||
public ActionOption(Integer treshold, String value) {
|
||||
|
||||
@ -30,23 +30,6 @@ public class ActionOption extends ChildOption implements Comparable<ActionOption
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* It is recommended to take further action if the treshold
|
||||
* gets changed: If successful, "clone()" this object and
|
||||
* replace the original with it.
|
||||
*
|
||||
* @param treshold
|
||||
* @return
|
||||
*/
|
||||
public boolean setTreshold(String treshold) {
|
||||
try {
|
||||
this.treshold = Integer.parseInt(treshold);
|
||||
return true;
|
||||
} catch(NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getTreshold() {
|
||||
return treshold;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public class DataManager {
|
||||
private final Map<Player, MovingData> movingData = new HashMap<Player, MovingData>();
|
||||
private final Map<Player, BlockBreakData> blockbreakData = new HashMap<Player, BlockBreakData>();
|
||||
private final Map<Player, InteractData> interactData = new HashMap<Player, InteractData>();
|
||||
private Map<Player, BlockPlaceData> blockPlaceData = new HashMap<Player, BlockPlaceData>();
|
||||
private final Map<Player, BlockPlaceData> blockPlaceData = new HashMap<Player, BlockPlaceData>();
|
||||
|
||||
public DataManager() {
|
||||
|
||||
|
@ -29,12 +29,12 @@ public class MovingData {
|
||||
public double horizontalBuffer;
|
||||
|
||||
public int morePacketsCounter;
|
||||
public long morePacketsLastTime;
|
||||
|
||||
public double morePacketsOverLimit = -50;
|
||||
|
||||
public double morePacketsBuffer = 50;
|
||||
public Location morePacketsSetbackPoint;
|
||||
public double morePacketsViolationLevel = 0;
|
||||
|
||||
public Location teleportTo;
|
||||
|
||||
public int lastElapsedIngameSeconds = 0;
|
||||
|
||||
}
|
||||
|
@ -58,7 +58,6 @@ public class PlayerTeleportEventManager extends PlayerListener {
|
||||
|
||||
@Override
|
||||
public void onPlayerTeleport(PlayerTeleportEvent event) {
|
||||
|
||||
if(!event.isCancelled())
|
||||
handleTeleportation(event.getPlayer(), event.getTo());
|
||||
|
||||
@ -73,7 +72,7 @@ public class PlayerTeleportEventManager extends PlayerListener {
|
||||
handleTeleportation(event.getPlayer(), event.getRespawnLocation());
|
||||
}
|
||||
|
||||
public void handleTeleportation(Player player, Location newLocation) {
|
||||
private void handleTeleportation(Player player, Location newLocation) {
|
||||
|
||||
/********* Moving check ************/
|
||||
final MovingData data = this.data.getMovingData(player);
|
||||
|
@ -10,6 +10,7 @@ import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import cc.co.evenprime.bukkit.nocheat.DefaultConfiguration;
|
||||
import cc.co.evenprime.bukkit.nocheat.actions.ActionManager;
|
||||
|
||||
/**
|
||||
@ -35,7 +36,7 @@ public class FlatActionParser {
|
||||
List<String[]> lines = new LinkedList<String[]>();
|
||||
|
||||
if(!file.exists()) {
|
||||
createActionFile(file);
|
||||
DefaultConfiguration.writeActionFile(file);
|
||||
return lines;
|
||||
}
|
||||
|
||||
@ -69,22 +70,4 @@ public class FlatActionParser {
|
||||
return lines;
|
||||
|
||||
}
|
||||
|
||||
public void createActionFile(File file) {
|
||||
if(!file.exists()) {
|
||||
try {
|
||||
file.createNewFile();
|
||||
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
|
||||
writer.write("# Use this file to define your own actions.");
|
||||
writer.newLine();
|
||||
writer.write("# Look at \"default_actions.txt\" for inspiration.");
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
writer.close();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
package cc.co.evenprime.bukkit.nocheat.file;
|
||||
|
||||
import cc.co.evenprime.bukkit.nocheat.config.tree.ActionListOption;
|
||||
import cc.co.evenprime.bukkit.nocheat.config.tree.ActionOption;
|
||||
import cc.co.evenprime.bukkit.nocheat.config.tree.ChildOption;
|
||||
import cc.co.evenprime.bukkit.nocheat.config.tree.ConfigurationTree;
|
||||
import cc.co.evenprime.bukkit.nocheat.config.tree.Option;
|
||||
import cc.co.evenprime.bukkit.nocheat.config.tree.ParentOption;
|
||||
|
||||
/**
|
||||
* An extremely simple YAML configuration generator
|
||||
*
|
||||
* @author Evenprime
|
||||
*
|
||||
*/
|
||||
public class YamlConfigGenerator {
|
||||
|
||||
private final static String spaces = " ";
|
||||
|
||||
public static String treeToYaml(ConfigurationTree tree) {
|
||||
|
||||
ParentOption o = (ParentOption) tree.getOption("");
|
||||
|
||||
String s = "";
|
||||
|
||||
for(Option option : o.getChildOptions()) {
|
||||
s += optionToYamlString(option, "");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static String optionToYamlString(Option option, String prefix) {
|
||||
|
||||
String s = "";
|
||||
|
||||
if(option instanceof ParentOption) {
|
||||
|
||||
s += prefix + option.getIdentifier() + ":\r\n";
|
||||
|
||||
prefix += spaces;
|
||||
|
||||
for(Option o : ((ParentOption) option).getChildOptions()) {
|
||||
s += optionToYamlString(o, prefix);
|
||||
}
|
||||
} else if(option instanceof ActionListOption && option.isActive()) {
|
||||
s += prefix + option.getIdentifier() + ":\r\n";
|
||||
|
||||
for(ActionOption o : ((ActionListOption) option).getChildOptions()) {
|
||||
s += prefix + spaces + o.getIdentifier() + ": \"" + o.getStringValue() + "\"\r\n";
|
||||
}
|
||||
} else if(option instanceof ChildOption && option.isActive()) {
|
||||
s += prefix + option.getIdentifier() + ": \"" + ((ChildOption) option).getStringValue() + "\"\r\n";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
package cc.co.evenprime.bukkit.nocheat.file;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An extremely simple YAML config parser
|
||||
*
|
||||
* @author Evenprime
|
||||
*
|
||||
*/
|
||||
public class YamlConfigParser {
|
||||
|
||||
private final String prefix = " ";
|
||||
|
||||
private final Map<String, Object> root;
|
||||
|
||||
public YamlConfigParser() {
|
||||
|
||||
root = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
public void read(File file) throws IOException {
|
||||
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
|
||||
|
||||
LinkedList<String> lines = new LinkedList<String>();
|
||||
|
||||
String line = null;
|
||||
|
||||
while((line = r.readLine()) != null) {
|
||||
lines.add(line);
|
||||
}
|
||||
|
||||
r.close();
|
||||
|
||||
parse(root, lines, "");
|
||||
|
||||
}
|
||||
|
||||
private void parse(Map<String, Object> root, LinkedList<String> lines, String prefix) throws IOException {
|
||||
|
||||
String line = null;
|
||||
|
||||
while(!lines.isEmpty()) {
|
||||
line = lines.getFirst();
|
||||
if(line.trim().startsWith("#")) {
|
||||
lines.removeFirst();
|
||||
} else if(line.trim().isEmpty()) {
|
||||
lines.removeFirst();
|
||||
} else if(line.startsWith(prefix)) {
|
||||
lines.removeFirst();
|
||||
if(line.contains(":")) {
|
||||
String pair[] = line.split(":", 2);
|
||||
if(pair[1].trim().isEmpty()) {
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
parse(m, lines, prefix + this.prefix);
|
||||
root.put(pair[0].trim(), m);
|
||||
} else {
|
||||
root.put(pair[0].trim(), removeQuotationMarks(pair[1].trim()));
|
||||
}
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static String removeQuotationMarks(String s) {
|
||||
if(s.startsWith("\"") && s.endsWith("\"")) {
|
||||
return s.substring(1, s.length() - 1);
|
||||
} else if(s.startsWith("\'") && s.endsWith("\'")) {
|
||||
return s.substring(1, s.length() - 1);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
/* Convenience methods for retrieving values start here */
|
||||
|
||||
public Object getProperty(String path) {
|
||||
return getProperty(path, root);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object getProperty(String path, Map<String, Object> node) {
|
||||
|
||||
if(node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!path.contains(".")) {
|
||||
return node.get(path);
|
||||
} else {
|
||||
String[] parts = path.split("\\.", 2);
|
||||
return getProperty(parts[1], (Map<String, Object>) node.get(parts[0]));
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(String path, String defaultValue) {
|
||||
return getString(path, defaultValue, root);
|
||||
}
|
||||
|
||||
private static String getString(String path, String defaultValue, Map<String, Object> node) {
|
||||
try {
|
||||
String result = (String) getProperty(path, node);
|
||||
if(result == null)
|
||||
return defaultValue;
|
||||
return result;
|
||||
} catch(Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user