001/* 002 * Trident - A Multithreaded Server Alternative 003 * Copyright 2014 The TridentSDK Team 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package net.tridentsdk.plugin; 019 020import net.tridentsdk.Trident; 021import net.tridentsdk.config.Config; 022import net.tridentsdk.event.Listener; 023import net.tridentsdk.plugin.annotation.PluginDesc; 024import net.tridentsdk.plugin.cmd.Command; 025import net.tridentsdk.registry.Registered; 026import net.tridentsdk.util.TridentLogger; 027import org.slf4j.Logger; 028 029import javax.annotation.Nonnull; 030import javax.annotation.Nullable; 031import javax.annotation.concurrent.NotThreadSafe; 032import java.io.File; 033import java.io.IOException; 034import java.io.InputStream; 035import java.nio.file.Files; 036import java.util.Objects; 037import java.util.stream.Collectors; 038 039/** 040 * Must be extended by a non-inner class to represent a plugin's <em>main class</em> 041 * 042 * @author The TridentSDK Team 043 * @since 0.3-alpha-DP 044 */ 045@NotThreadSafe 046public class Plugin { 047 private File pluginFile; 048 private File configDirectory; 049 private PluginDesc description; 050 private Config defaultConfig; 051 public PluginLoader classLoader; 052 053 protected Plugin() { 054 } 055 056 public void init(File pluginFile, PluginDesc description, PluginLoader loader) { 057 this.pluginFile = pluginFile; 058 this.description = description; 059 this.configDirectory = new File("plugins" + File.separator + description.name() + File.separator); 060 this.defaultConfig = new Config(new File(this.configDirectory, "config.json")); 061 this.classLoader = loader; 062 } 063 064 /** 065 * Obtains the instance of the plugin which the caller class is in 066 * 067 * <p>Returns {@code null} if the plugin has not been loaded yet, or if the class is not a plugin loaded on the 068 * server.</p> 069 * 070 * @return the instance of the plugin 071 */ 072 @Nullable 073 public static Plugin instance() { 074 Class<?> caller = Trident.findCaller(3); 075 ClassLoader loader = caller.getClassLoader(); 076 for (Plugin plugin : Registered.plugins()) 077 if (plugin.classLoader.equals(loader)) 078 return plugin; 079 return null; 080 } 081 082 /** 083 * Obtains the instance of the plugin which has the specified class 084 * 085 * <p>Returns {@code null} if the plugin has not been loaded yet, or if the class is not a plugin loaded on the 086 * server.</p> 087 * 088 * @param c the main class of the plugin to obtain the instance of 089 * @return the instance of the plugin with the specified main class 090 */ 091 @Nullable 092 public static <T extends Plugin> T instance(Class<T> c) { 093 for (Plugin plugin : Registered.plugins()) 094 if (plugin.classLoader.loadedClasses().containsKey(c.getName())) { 095 return (T) plugin; 096 } 097 return null; 098 } 099 100 /** 101 * Called by the handler to indicate the plugin has been constructed 102 */ 103 public void load() { 104 // Method intentionally left blank 105 } 106 107 /** 108 * Called by the handler to indicate the enabling of this plugin 109 */ 110 public void enable() { 111 // Method intentionally left blank 112 } 113 114 /** 115 * Called by the handler to indicate the disabling of this plugin 116 */ 117 public void disable() { 118 // Method intentionally left blank 119 } 120 121 /** 122 * Obtains the listener instance with the class specified 123 * 124 * @param c the class to find the listener instance by 125 * @return the listener instance registered to the server 126 */ 127 public <T extends Listener> T listenerBy(Class<T> c) { 128 return (T) Registered.events() 129 .stream() 130 .filter(r -> r.listener().getClass().equals(c)) 131 .collect(Collectors.toList()) 132 .get(0); 133 } 134 135 /** 136 * Obtains the command instance with the class specified 137 * 138 * @param c the class to find the command instance by 139 * @return the command instance registered to the server 140 */ 141 public <T extends Command> T commandBy(Class<T> c) { 142 return (T) Registered.commands().stream() 143 .filter(cmd -> cmd.getClass().equals(c)) 144 .collect(Collectors.toList()) 145 .get(0); 146 } 147 148 /** 149 * Gets the logger for this plugin 150 * 151 * @return the logger 152 */ 153 public Logger logger() { 154 return TridentLogger.get(description.name()).internal(); 155 } 156 157 /** 158 * Saves the configuration inside the jar to the plugin directory 159 * 160 * <p>If the configuration is already saved, this does not overwrite it.</p> 161 */ 162 public void saveDefaultConfig() { 163 this.saveResource("config.json", false); 164 } 165 166 /** 167 * Saves a resource inside the jar to the plugin directory 168 * 169 * @param name the filename of the directory 170 * @param replace whether or not replace the old resource, if it exists 171 */ 172 public void saveResource(String name, boolean replace) { 173 try { 174 InputStream is = this.getClass().getResourceAsStream('/' + name); 175 File file = new File(this.configDirectory, name); 176 177 if (is == null) { 178 return; 179 } 180 181 if (replace && file.exists()) { 182 file.delete(); 183 } 184 185 Files.copy(is, file.getAbsoluteFile().toPath()); 186 } catch (IOException ex) { 187 TridentLogger.get().error(ex); 188 } 189 } 190 191 /** 192 * Obtains the file which this plugin was loaded from 193 * 194 * @return the file which the plugin is loaded from 195 */ 196 public final File pluginFile() { 197 return this.pluginFile; 198 } 199 200 /** 201 * The default configuration for this plugin, which may or may not exist physically 202 * 203 * @return the default configuration given to this plugin 204 */ 205 public Config defaultConfig() { 206 return this.defaultConfig; 207 } 208 209 /** 210 * The plugin directory 211 * 212 * <p>The returned file includes the trailing file separator</p> 213 * 214 * @return the plugin directory where resources like the default config are saved 215 */ 216 public File configDirectory() { 217 return this.configDirectory; 218 } 219 220 /** 221 * Obtains the annotation given by this plugin 222 * 223 * @return the plugin descriptor for this plugin 224 */ 225 @Nonnull 226 public final PluginDesc description() { 227 return this.description; 228 } 229 230 @Override 231 public boolean equals(Object other) { 232 if (other instanceof Plugin) { 233 Plugin otherPlugin = (Plugin) other; 234 if (otherPlugin.description().name().equals(this.description().name())) { 235 if (otherPlugin.description().author().equals(this.description().author())) { 236 return true; 237 } 238 } 239 } 240 return false; 241 } 242 243 @Override 244 public int hashCode() { 245 // Find constants 246 String name = this.description().name(); 247 String author = this.description().author(); 248 249 return Objects.hash(name, author); 250 } 251}