Java tutorial
/* * Copyright (C) 2015 Chingo * * 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.chingo247.structureapi; import com.chingo247.structureapi.exeption.StructureAPIException; import com.chingo247.structureapi.model.structure.ConstructionStatus; import com.chingo247.structureapi.model.structure.StructureNode; import com.chingo247.menuapi.menu.CategoryMenu; import com.chingo247.menuapi.menu.MenuAPI; import com.chingo247.xplatform.core.APlatform; import com.chingo247.settlercraft.core.SettlerCraft; import com.chingo247.settlercraft.core.concurrent.ThreadPoolFactory; import com.chingo247.settlercraft.core.event.DefaultSubscriberExceptionHandler; import com.chingo247.settlercraft.core.event.EventDispatcher; import com.chingo247.settlercraft.core.exception.SettlerCraftException; import com.chingo247.structureapi.menus.plans.StructurePlanMenuFactory; import com.chingo247.structureapi.menus.plans.StructurePlanMenuReader; import com.chingo247.settlercraft.core.model.world.WorldNode; import com.chingo247.settlercraft.core.persistence.neo4j.Neo4jHelper; import com.chingo247.structureapi.construction.Contractor; import com.chingo247.structureapi.plan.IStructurePlan; import com.chingo247.structureapi.plan.StructurePlanManager; import com.chingo247.structureapi.event.structure.plan.StructurePlansLoadedEvent; import com.chingo247.structureapi.event.structure.plan.StructurePlansReloadEvent; import com.chingo247.structureapi.model.structure.Structure; import com.chingo247.structureapi.platform.services.AsyncEditSessionFactoryProvider; import com.chingo247.structureapi.model.zone.ConstructionZoneNode; import com.chingo247.structureapi.placement.block.SchematicPlacement; import com.chingo247.structureapi.schematic.Schematic; import com.chingo247.structureapi.schematic.SchematicManager; import com.chingo247.structureapi.platform.ConfigProvider; import com.chingo247.structureapi.exeption.StructureRestrictionException; import com.chingo247.xplatform.core.IColors; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.AsyncEventBus; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.common.util.concurrent.Monitor; import com.sk89q.worldedit.regions.CuboidRegion; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Map; import java.util.UUID; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import java.util.Set; import java.util.concurrent.ExecutorService; import org.dom4j.DocumentException; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; import org.primesoft.asyncworldedit.worldedit.AsyncEditSessionFactory; import com.chingo247.structureapi.construction.IContractor; import com.chingo247.structureapi.util.WorldEditHelper; import com.chingo247.structureapi.watchers.PhysicsWatch; import com.chingo247.xplatform.core.IPlugin; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.world.World; /** * * @author Chingo */ public class StructureAPI implements IStructureAPI { public static final String PLUGIN_NAME = "SettlerCraft"; public static final String PLANS_DIRECTORY = "plans"; private static final Logger LOG = Logger.getLogger(StructureAPI.class.getName()); private final ExecutorService executor; private final GraphDatabaseService graph; private final Lock loadLock = new ReentrantLock(); private final APlatform platform; private final IColors COLORS; private final Map<String, Monitor> monitors; private final Set<StructureRestriction> restrictions; private StructurePlacerFactory structurePlacerFactory; // private ConstructionZonePlacerFactory constructionZonePlacerFactory; private IContractor constructionExecutor; private IPlugin plugin; private ConfigProvider config; private StructurePlanMenuFactory planMenuFactory; private CategoryMenu menuTemplate; private boolean isLoadingPlans = false, initialized = false; private EventDispatcher eventDispatcher; private EventBus eventBus, asyncEventBus; private AsyncEditSessionFactoryProvider sessionFactoryProvider; private IAsyncWorldEditIntegration asyncWorldEditIntegration; private static StructureAPI instance; private final PhysicsWatch physicsWatch; private StructureAPI() { this.platform = SettlerCraft.getInstance().getPlatform(); this.graph = SettlerCraft.getInstance().getNeo4j(); this.executor = new ThreadPoolFactory().newCachedThreadPool(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors()); this.monitors = Maps.newHashMap(); this.COLORS = platform.getChatColors(); this.restrictions = Sets.newHashSet(); this.eventDispatcher = new EventDispatcher(); this.asyncEventBus = new AsyncEventBus(executor, new DefaultSubscriberExceptionHandler()); this.eventBus = new EventBus(new DefaultSubscriberExceptionHandler()); this.eventDispatcher.register(eventBus); this.eventDispatcher.register(asyncEventBus); this.asyncEventBus.register(new StructurePlanManagerHandler()); this.physicsWatch = new PhysicsWatch(); setupSchema(); } @Override public EventBus getAsyncEventBus() { return asyncEventBus; } @Override public EventBus getEventBus() { return eventBus; } @Override public Iterable<StructureRestriction> getRestrictions() { return new ArrayList<>(restrictions); } // @Override // public IConstructionZonePlacerFactory getConstructionZonePlacerFactory() { // return constructionZonePlacerFactory; // } @Override public GraphDatabaseService getGraphDatabase() { return SettlerCraft.getInstance().getNeo4j(); } @Override public EventDispatcher getEventDispatcher() { return eventDispatcher; } @Override public StructurePlacerFactory getStructurePlacerFactory() { return structurePlacerFactory; } public synchronized Monitor getMonitor(String world) { Monitor monitor = monitors.get(world); if (monitor == null) { monitor = new Monitor(); monitors.put(world, monitor); } return monitor; } private void setupSchema() { // Create indexes, each index creation needs to be executed in a seperate transaction! try (Transaction tx = graph.beginTx()) { Neo4jHelper.createIndexIfNotExist(graph, StructureNode.label(), StructureNode.DELETED_AT_PROPERTY); tx.success(); } try (Transaction tx = graph.beginTx()) { Neo4jHelper.createUniqueIndexIfNotExist(graph, StructureNode.label(), StructureNode.ID_PROPERTY); tx.success(); } try (Transaction tx = graph.beginTx()) { Neo4jHelper.createUniqueIndexIfNotExist(graph, ConstructionZoneNode.label(), ConstructionZoneNode.ID_PROPERTY); tx.success(); } setupIdGenerator("STRUCTURE_ID"); setupIdGenerator("CONSTRUCTIONZONE_ID"); } private void setupIdGenerator(String generatorName) { try (Transaction tx = graph.beginTx()) { Result r = graph.execute( "MATCH (sid:ID_GENERATOR {name:'" + generatorName + "'}) " + "RETURN sid " + "LIMIT 1"); if (!r.hasNext()) { graph.execute("CREATE (sid:ID_GENERATOR {name:'" + generatorName + "', nextId: 0})"); } tx.success(); } } @Override public void addRestriction(StructureRestriction structureRestriction) { synchronized (restrictions) { this.restrictions.add(structureRestriction); } } @Override public void removeRestriction(StructureRestriction structureRestriction) { synchronized (restrictions) { this.restrictions.remove(structureRestriction); } } /** * Gets the StructureAPI instance * * @return */ public static IStructureAPI getInstance() { if (instance == null) { instance = new StructureAPI(); } return instance; } /** * Fires all process that need to start at the startup of the StructureAPI * * @throws DocumentException * @throws SettlerCraftException */ public synchronized void initialize() throws DocumentException, SettlerCraftException { if (!initialized) { // Set states back to stopped... if not completed or removed resetStates(); // Load StructurePlans StructurePlanMenuReader reader = new StructurePlanMenuReader(); // Setup menu this.menuTemplate = reader.read(new File(getWorkingDirectory(), "menu.xml")); this.planMenuFactory = new StructurePlanMenuFactory(platform, menuTemplate); reload(); // this.constructionZonePlacerFactory = new ConstructionZonePlacerFactory(this); this.structurePlacerFactory = new StructurePlacerFactory(this.getPlatform()); this.constructionExecutor = Contractor.getInstance(); this.initialized = true; } } private void resetStates() { try (Transaction tx = graph.beginTx()) { Map<String, Object> params = Maps.newHashMap(); // Enforce integers params.put("completed", (Integer) ConstructionStatus.COMPLETED.getStatusId()); params.put("removed", (Integer) ConstructionStatus.REMOVED.getStatusId()); params.put("stopped", (Integer) ConstructionStatus.COMPLETED.getStatusId()); String query = "MATCH (s:" + StructureNode.LABEL + ") " + "WHERE NOT s." + StructureNode.CONSTRUCTION_STATUS_PROPERTY + " = {completed} " + "AND NOT s." + StructureNode.CONSTRUCTION_STATUS_PROPERTY + " = {removed}" + "SET s." + StructureNode.CONSTRUCTION_STATUS_PROPERTY + " = {stopped}"; graph.execute(query, params); tx.success(); } } /** * Reloads all plans from the StructurePlan directory */ @Override public void reload() { if (loadLock.tryLock()) { try { StructurePlanManager.getInstance().reload(); } finally { loadLock.unlock(); } } } @Override public boolean isLoadingPlans() { return isLoadingPlans; } @Override public IContractor getContractor() { return constructionExecutor; } @Override public StructurePlanManager getStructurePlanManager() { return StructurePlanManager.getInstance(); } @Override public final File getWorldDirectory(String world) { File f = new File(getWorkingDirectory().getAbsolutePath() + "//worlds//" + world); f.mkdirs(); return f; } @Override public final File getStructuresDirectory(String world) { File f = new File(getWorkingDirectory().getAbsolutePath() + "//worlds//" + world + "//structures"); f.mkdirs(); // creates if not exists.. return f; } @Override public final File getPlanDirectory() { return new File(getWorkingDirectory(), PLANS_DIRECTORY); } @Override public CategoryMenu createPlanMenu() { return planMenuFactory.createPlanMenu(); } public void registerStructureAPIPlugin(IPlugin plugin) throws StructureAPIException { if (this.plugin != null) { throw new StructureAPIException( "Already registered a Plugin for the StructureAPI, NOTE that this method should only be used by StructureAPI Plugin itself!"); } this.plugin = plugin; } @Override public APlatform getPlatform() { return platform; } @Override public File getWorkingDirectory() { return plugin.getDataFolder(); } public void registerConfigProvider(ConfigProvider configProvider) { this.config = configProvider; } public void registerAWE(IAsyncWorldEditIntegration asyncWorldEditIntegration) { if (this.asyncWorldEditIntegration != null) { throw new RuntimeException("Already registered AWE"); } this.asyncWorldEditIntegration = asyncWorldEditIntegration; } public IAsyncWorldEditIntegration getAsyncWorldEditIntegration() { return asyncWorldEditIntegration; } @Override public boolean isQueueLocked(UUID player) { return asyncWorldEditIntegration.isQueueLocked(player); } final File getDirectoryForStructure(WorldNode worldNode, Structure structureNode) { File structuresDirectory = getStructuresDirectory(worldNode.getName()); File structureDir = new File(structuresDirectory, String.valueOf(structureNode.getId())); return structureDir; } @Override public ConfigProvider getConfig() { return config; } @Override public File getGenerationDirectory() { return new File(getWorkingDirectory(), "generate"); } @Override public void checkRestrictions(UUID player, String world, CuboidRegion region) throws StructureRestrictionException { Player ply = WorldEditHelper.getPlayer(player); World w = WorldEditHelper.getWorld(world); for (StructureRestriction restriction : restrictions) { restriction.check(ply, w, region, null); } } @Override public ExecutorService getExecutor() { return executor; } // @Override // public IWorldConfig loadOrGetConfig(String world) { // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. // } @Override public IPlugin getPlugin() { return plugin; } @Override public PhysicsWatch getPhysicsWatcher() { return physicsWatch; } private class StructurePlanManagerHandler { @Subscribe @AllowConcurrentEvents public void onLoadingStructurePlans(StructurePlansReloadEvent event) { isLoadingPlans = true; platform.getServer().broadcast( COLORS.yellow() + "[SettlerCraft]: " + COLORS.reset() + "Plans are being reloaded..."); // platform.getConsole().printMessage(COLORS.yellow() + "[SettlerCraft]: " + COLORS.reset() + "Loading StructurePlans"); } @Subscribe @AllowConcurrentEvents public void onStructurePlansLoaded(StructurePlansLoadedEvent event) { planMenuFactory = new StructurePlanMenuFactory(platform, menuTemplate); MenuAPI.getInstance().closeMenusWithTag(StructurePlanMenuFactory.PLAN_MENU_TAG, "Server is reloading plans..."); planMenuFactory.clearAll(); for (IStructurePlan plan : StructurePlanManager.getInstance().getPlans()) { planMenuFactory.load(plan); } isLoadingPlans = false; platform.getServer() .broadcast(COLORS.yellow() + "[SettlerCraft]: " + COLORS.reset() + "Reload plans complete!"); // platform.getConsole().printMessage(COLORS.yellow() + "[SettlerCraft]: " + COLORS.reset() + "Plans are loaded!"); } } public void registerAsyncEditSesionFactoryProvider(AsyncEditSessionFactoryProvider provider) { Preconditions.checkNotNull(provider, "Provider was null"); Preconditions.checkArgument(sessionFactoryProvider == null, "Already registered a AsyncEditSessionFactoryProvider"); this.sessionFactoryProvider = provider; } @Override public AsyncEditSessionFactory getSessionFactory() { return sessionFactoryProvider.getFactory(); } @Override public SchematicPlacement loadSchematic(File schematicFile) throws IOException { Schematic schematic = SchematicManager.getInstance().getOrLoadSchematic(schematicFile); return new SchematicPlacement(schematic); } }