/*******************************************************************************
* Copyright (C) 2014 Travis Ralston (turt2live)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package com.turt2live.antishare.bukkit;
import com.turt2live.antishare.ASGameMode;
import com.turt2live.antishare.bukkit.abstraction.VersionSelector;
import com.turt2live.antishare.bukkit.commands.CommandHandler;
import com.turt2live.antishare.bukkit.commands.command.ReloadCommand;
import com.turt2live.antishare.bukkit.commands.command.ToolsCommand;
import com.turt2live.antishare.bukkit.configuration.BukkitConfiguration;
import com.turt2live.antishare.bukkit.groups.BukkitGroupManager;
import com.turt2live.antishare.bukkit.impl.BukkitWorldProvider;
import com.turt2live.antishare.bukkit.impl.pattern.BukkitIronGolemMobPattern;
import com.turt2live.antishare.bukkit.impl.pattern.BukkitSnowmanPattern;
import com.turt2live.antishare.bukkit.impl.pattern.BukkitWitherPattern;
import com.turt2live.antishare.bukkit.lang.Lang;
import com.turt2live.antishare.bukkit.listener.EngineListener;
import com.turt2live.antishare.bukkit.listener.ToolListener;
import com.turt2live.antishare.engine.DevEngine;
import com.turt2live.antishare.engine.Engine;
import com.turt2live.antishare.engine.WorldEngine;
import com.turt2live.antishare.engine.WorldSplit;
import com.turt2live.antishare.events.EventDispatcher;
import com.turt2live.antishare.events.EventListener;
import com.turt2live.antishare.events.worldengine.WorldEngineCreateEvent;
import com.turt2live.antishare.io.EntityManager;
import com.turt2live.antishare.io.flatfile.FileBlockManager;
import com.turt2live.antishare.io.flatfile.FileEntityManager;
import com.turt2live.antishare.io.flatfile.FileInventoryManager;
import com.turt2live.antishare.object.pattern.PatternManager;
import com.turt2live.antishare.uuid.UuidService;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
/**
* The AntiShare Bukkit Plugin main class
*
* @author turt2live
*/
public class AntiShare extends JavaPlugin {
/**
* Color char identifier
*/
public static final char COLOR_REPLACE_CHAR = '&';
// The number of reloads recorded
private static int RELOADS = 0;
private static AntiShare instance;
private File dataFolder;
private int blockSize;
private MaterialProvider materialProvider = new MaterialProvider();
private YamlConfiguration worldsplits;
private File worldsplitsFile;
@Override
public void onLoad() {
instance = this;
dataFolder = new File(getDataFolder(), "data");
if (!dataFolder.exists()) dataFolder.mkdirs();
// Load material defaults
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(getResource("res-bukkit/item_aliases.csv")));
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("#")) continue;
String[] parts = line.split(",");
if (parts.length != 2) continue;
String name = parts[0].trim();
String material = parts[1].trim();
Material mat = Material.matchMaterial(material);
if (mat == null) continue;
materialProvider.insertAlias(name, mat);
}
reader.close();
} catch (IOException e) {
getLogger().warning("Could not load internal item_aliases.csv, you may have weird errors");
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(getResource("res-bukkit/item_lang.csv")));
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("#")) continue;
String[] parts = line.split(",", 2);
String idCombo = parts[0];
String playerName = parts[1];
parts = idCombo.split(":", 2);
String materialName = parts[0];
String dataName = parts[1];
try {
short data = Short.parseShort(dataName);
Material material = Material.matchMaterial(materialName);
if (material == null) continue;
materialProvider.insertPlayerFriendly(material, data, playerName);
if (data == 0) materialProvider.insertPlayerFriendly(material, (short) -1, playerName);
} catch (NumberFormatException ignored) {
}
}
reader.close();
} catch (IOException e) {
getLogger().warning("Could not load internal item_lang.csv, you may have weird errors");
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(getResource("res-bukkit/item_similars.txt")));
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("#")) continue;
materialProvider.insertExtra(line);
}
reader.close();
} catch (IOException e) {
getLogger().warning("Could not load internal item_similars.txt, you may have weird errors");
}
}
@Override
public void onDisable() {
// Save everything
Engine.getInstance().prepareShutdown();
// Cleanup
getServer().getScheduler().cancelTasks(this);
EventDispatcher.deregister(this);
// Shutdown DevEngine
if (DevEngine.isEnabled()) {
getLogger().info("DevEngine shutdown");
DevEngine.setEnabled(false);
}
}
@Override
public void onEnable() {
instance = this; // For reload support
// Check for developer tools
if (getServer().getPluginManager().getPlugin("AntiShare-DevTools") != null) {
getLogger().warning("============= ANTISHARE =============");
getLogger().warning(" -- DEVELOPMENT TOOLS FOUND --");
getLogger().warning(" **** Enabling Debug Support ****");
getLogger().warning("============= ANTISHARE =============");
DevEngine.setEnabled(true);
DevEngine.setLogDirectory(new File(getDataFolder(), "devlogs"));
}
// Setup configuration
FileConfiguration configuration = YamlConfiguration.loadConfiguration(getResource("res-bukkit/config.yml"));
getConfig().setDefaults(configuration);
saveDefaultConfig();
// Setup world splits
worldsplitsFile = new File(getDataFolder(), "worldsplits.yml");
worldsplits = YamlConfiguration.loadConfiguration(worldsplitsFile);
worldsplits.setDefaults(YamlConfiguration.loadConfiguration(getResource("res-bukkit/worldsplits.yml")));
worldsplits.options().copyDefaults(true);
worldsplits.options().copyHeader(true);
try {
worldsplits.save(worldsplitsFile);
} catch (IOException e) {
e.printStackTrace();
}
// Setup AntiShare events
EventDispatcher.register(this);
// Start lang
Lang.getInstance();
// Start the engines
initEngine();
// Setup listeners
getServer().getPluginManager().registerEvents(new EngineListener(), this);
getServer().getPluginManager().registerEvents(new ToolListener(), this);
// Setup commands
CommandHandler handler = new CommandHandler();
getCommand("antishare").setExecutor(handler);
// Register commands
handler.registerCommand(new ToolsCommand());
handler.registerCommand(new ReloadCommand());
}
/**
* Initializes the engine
*/
protected void initEngine() {
// Complain about offline mode
if (!getServer().getOnlineMode()) {
DevEngine.log("[Bukkit Plugin] OFFLINE MODE SERVER DETECTED");
getLogger().warning("******************************************");
getLogger().warning("* OFFLINE MODE SERVER *");
getLogger().warning("* -------------------------------------- *");
getLogger().warning("* Your server is in offline mode. Due to *");
getLogger().warning("* how AntiShare operates, this means *");
getLogger().warning("* that AntiShare may take extra time to *");
getLogger().warning("* load/save data because it has to find *");
getLogger().warning("* player UUIDs/names from a web service. *");
getLogger().warning("* *");
getLogger().warning("* To correct the problem, enable online *");
getLogger().warning("* mode from the server.properties. *");
getLogger().warning("* *");
getLogger().warning("* This message cannot be disabled. *");
getLogger().warning("******************************************");
}
// Reset UUID cache service
UuidService.getInstance().removeSources();
VersionSelector.getMinecraft().initialize();
// Load engine variables
blockSize = getConfig().getInt("caching.block-size", 256);
long cacheMax = getConfig().getLong("caching.cache-expiration", 120000);
long cacheInterval = getConfig().getLong("caching.cache-timer-interval", 60000);
long periodicSave = getConfig().getLong("caching.periodic-save", 0);
// Validate engine variables
if (blockSize <= 0) blockSize = 256;
if (cacheMax <= 0) cacheMax = 120000;
if (cacheInterval <= 0) cacheInterval = 60000;
// File/Folder setup
File inventoryFolder = new File(getDataFolder(), "data" + File.separator + "inventories");
if (!inventoryFolder.exists()) inventoryFolder.mkdirs();
// Setup engine
Engine.getInstance().setLogger(this.getLogger());
Engine.getInstance().setCacheMaximum(cacheMax);
Engine.getInstance().setCacheIncrement(cacheInterval);
Engine.getInstance().setSaveInterval(periodicSave);
Engine.getInstance().setGroupManager(new BukkitGroupManager());
Engine.getInstance().setWorldProvider(new BukkitWorldProvider());
Engine.getInstance().setConfiguration(new BukkitConfiguration(new File(getDataFolder(), "config.yml")));
Engine.getInstance().loadItemProvider();
Engine.getInstance().setInventoryManager(new FileInventoryManager(inventoryFolder));
// Setup mob patterns
PatternManager patterns = Engine.getInstance().getPatterns();
patterns.clear();
patterns.add(new BukkitIronGolemMobPattern());
patterns.add(new BukkitSnowmanPattern());
patterns.add(new BukkitWitherPattern());
// Probe all currently loaded worlds
for (World world : getServer().getWorlds()) {
Engine.getInstance().createWorldEngine(world.getName());
}
}
/**
* Reloads the AntiShare plugin, saving all data before re-enabling all services.
*
* @return returns the number of counted reloads
*/
public int reloadPlugin() {
getLogger().info("Reloading plugin...");
DevEngine.log("[Bukkit Plugin] Reload issued. Reloads until now: " + RELOADS);
// Reload the world splits
worldsplits = YamlConfiguration.loadConfiguration(worldsplitsFile);
// Restart the engine
DevEngine.log("[Bukkit Plugin] Reloading engine...");
Engine.getInstance().prepareShutdown();
initEngine();
// Restart language
DevEngine.log("[Bukkit Plugin] Reloading language...");
Lang.getInstance().reload();
RELOADS++;
DevEngine.log("[Bukkit Plugin] Reload completed. Reloads until now: " + RELOADS);
return RELOADS;
}
@EventListener
public void onWorldEngineCreate(WorldEngineCreateEvent event) {
WorldEngine engine = event.getEngine();
// Prepare world directories
File storeLocation = new File(dataFolder, engine.getWorldName());
File blockStore = new File(storeLocation, "blockdata");
File entityStore = new File(storeLocation, "entities.dat");
if (!storeLocation.exists()) storeLocation.mkdirs();
if (!blockStore.exists()) blockStore.mkdirs();
// Set block manager
getLogger().info("Indexing '" + engine.getWorldName() + "'...");
engine.setBlockManager(new FileBlockManager(blockSize, blockStore));
// Set entity manager
EntityManager entityManager = new FileEntityManager(entityStore);
entityManager.load();
engine.setEntityManager(entityManager);
// Set world split, if required
List<Map<?, ?>> splits = worldsplits.getMapList("worldsplits");
for (Map<?, ?> split : splits) {
try {
if (((String) split.get("world")).equalsIgnoreCase(engine.getWorldName())) {
// We found a world split
String positive = (String) split.get("positive");
String negative = (String) split.get("negative");
String axis = (String) split.get("axis");
int value = (Integer) split.get("value");
ASGameMode p = ASGameMode.fromString(positive);
ASGameMode n = ASGameMode.fromString(negative);
WorldSplit.Axis a = WorldSplit.Axis.valueOf(axis.toUpperCase());
if (p == null || n == null || p == n) {
getLogger().warning("[WorldSplit: " + engine.getWorldName() + "] Gamemodes must be dis-similar and real: " + p + " vs " + n);
continue;
}
WorldSplit worldSplit = new WorldSplit(a, value, p, n);
engine.setWorldSplit(worldSplit);
}
} catch (Exception e) {
getLogger().warning("Invalid world split configuration, see below for more information.");
e.printStackTrace();
}
}
}
/**
* Gets the active material provider for this plugin instance
*
* @return the material provider
*/
public MaterialProvider getMaterialProvider() {
return materialProvider;
}
/**
* Gets the AntiShare instance
*
* @return the AntiShare instance
*/
public static AntiShare getInstance() {
return instance;
}
}