-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from Ceikry/master
Plugin system, plugin API
- Loading branch information
Showing
51 changed files
with
2,104 additions
and
377 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package plugin; | ||
|
||
import plugin.api.MiniMenuEntry; | ||
import rt4.Component; | ||
import rt4.Npc; | ||
import rt4.Player; | ||
import rt4.Tile; | ||
|
||
/** | ||
* The base plugin class which is meant to be extended by plugins. | ||
* Contains callbacks to many parts of the internal client code. | ||
* @author ceikry | ||
*/ | ||
public abstract class Plugin { | ||
long timeOfLastDraw; | ||
|
||
void _init() { | ||
Init(); | ||
} | ||
|
||
void _draw() { | ||
long nowTime = System.currentTimeMillis(); | ||
Draw(nowTime - timeOfLastDraw); | ||
timeOfLastDraw = nowTime; | ||
} | ||
|
||
/** | ||
* Draw() is called by the client rendering loop so that plugins can draw information onto the screen. | ||
* This will be called once per frame, meaning it is framerate bound. | ||
* @param timeDelta the time (ms) elapsed since the last draw call. | ||
*/ | ||
public void Draw(long timeDelta) {} | ||
|
||
/** | ||
* Init() is called when the plugin is first loaded | ||
*/ | ||
public void Init() {} | ||
|
||
/** | ||
* OnXPUpdate() is called when the client receives an XP update packet. This includes at login. | ||
* @param skill - the skill ID being updated | ||
* @param xp - the new total XP for the skill. | ||
*/ | ||
public void OnXPUpdate(int skill, int xp) {} | ||
|
||
/** | ||
* Update() is called once per tick, aka once every 600ms. | ||
*/ | ||
public void Update() {} | ||
|
||
/** | ||
* PlayerOverheadDraw() is called once per frame, for every player on the screen. :) Expensive. | ||
* @param screenX the X coordinate on the screen for overhead drawing | ||
* @param screenY the Y coordinate on the screen for overhead drawing | ||
*/ | ||
public void PlayerOverheadDraw(Player player, int screenX, int screenY) {} | ||
|
||
/** | ||
* NPCOverheadDraw() is called once per frame, for every NPC on the screen. :) Expensive. | ||
* @param screenX the X coordinate on the screen for overhead drawing | ||
* @param screenY the Y coordinate on the screen for overhead drawing | ||
*/ | ||
public void NPCOverheadDraw(Npc npc, int screenX, int screenY) {} | ||
|
||
/** | ||
* ProcessCommand is called when a user types and sends a message prefixed with :: | ||
* @param commandStr the command the user used - should include :: in comparisons, eg <pre>commandStr.equals("::command")</pre> | ||
* @param args any other tokens included with the initial message. Tokens are determined by spaces. | ||
*/ | ||
public void ProcessCommand(String commandStr, String[] args) {} | ||
|
||
/** | ||
* ComponentDraw is called when an interface component is being rendered by the client. | ||
* @param componentIndex the index of the component in its parent interface. | ||
* @param component the component itself | ||
* @param screenX the screen X coordinate of this component | ||
* @param screenY the screen Y coordinate of this component | ||
*/ | ||
public void ComponentDraw(int componentIndex, Component component, int screenX, int screenY) {} | ||
|
||
/** | ||
* OnVarpUpdate is called when varps are updated by the server sending packets. | ||
* @param id the ID of the varp | ||
* @param value the value the varp is being set to. | ||
*/ | ||
public void OnVarpUpdate(int id, int value) {} | ||
|
||
/** | ||
* OnLogout is called when the client logs out. This should be used to clear player-relevant plugin state. | ||
*/ | ||
public void OnLogout() {} | ||
|
||
/** | ||
* DrawMiniMenu is called when a MiniMenu entry has been created. | ||
* @param entry the entry | ||
*/ | ||
public void DrawMiniMenu(MiniMenuEntry entry) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package plugin; | ||
|
||
import plugin.annotations.PluginMeta; | ||
|
||
import java.io.File; | ||
import java.io.FileNotFoundException; | ||
import java.io.FileReader; | ||
import java.io.IOException; | ||
import java.util.Objects; | ||
import java.util.Properties; | ||
|
||
/** | ||
* A data class for storing information about plugins. | ||
* @author ceikry | ||
*/ | ||
class PluginInfo { | ||
double version; | ||
String author; | ||
String description; | ||
|
||
public PluginInfo(String author, String description, double version) { | ||
this.version = version; | ||
this.author = author; | ||
this.description = description; | ||
} | ||
|
||
public static PluginInfo loadFromFile(File file) { | ||
Properties prop = new Properties(); | ||
|
||
try { | ||
prop.load(new FileReader(file)); | ||
} catch (FileNotFoundException e) { | ||
System.err.println("File does not exist! - " + file.getAbsolutePath()); | ||
return new PluginInfo("", "", 0.0); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
return new PluginInfo("", "", 0.0); | ||
} | ||
|
||
return new PluginInfo( | ||
prop.get("AUTHOR").toString(), | ||
prop.get("DESCRIPTION").toString(), | ||
Double.parseDouble(prop.get("VERSION").toString()) | ||
); | ||
} | ||
|
||
public static PluginInfo loadFromClass(Class<?> clazz) { | ||
PluginMeta info = clazz.getAnnotation(PluginMeta.class); | ||
|
||
if (info == null) { | ||
return null; | ||
} | ||
|
||
return new PluginInfo( | ||
info.author(), | ||
info.description(), | ||
info.version() | ||
); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
PluginInfo that = (PluginInfo) o; | ||
return Double.compare(that.version, version) == 0 && Objects.equals(author, that.author) && Objects.equals(description, that.description); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(version, author, description); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package plugin; | ||
|
||
import plugin.api.MiniMenuEntry; | ||
import plugin.api.MiniMenuType; | ||
import rt4.*; | ||
|
||
import java.io.File; | ||
import java.net.URL; | ||
import java.net.URLClassLoader; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Responsible for loading and broadcasting methods to all plugins. | ||
* @author ceikry | ||
*/ | ||
public class PluginRepository { | ||
static HashMap<PluginInfo, Plugin> loadedPlugins = new HashMap<>(); | ||
|
||
public static void registerPlugin(PluginInfo info, Plugin plugin) { | ||
loadedPlugins.put(info, plugin); | ||
} | ||
|
||
public static void reloadPlugins() { | ||
loadedPlugins.clear(); | ||
Init(); | ||
} | ||
|
||
public static void Init() { | ||
File pluginsDirectory = new File(GlobalJsonConfig.instance.pluginsFolder); | ||
|
||
if (!pluginsDirectory.exists()) { | ||
System.out.println("Skipping plugin initialization - " + pluginsDirectory.getAbsolutePath() + " does not exist."); | ||
return; | ||
} | ||
|
||
try { | ||
URL[] classPath = {pluginsDirectory.toURI().toURL()}; | ||
URLClassLoader loader = new URLClassLoader(classPath); | ||
|
||
for(File file : Objects.requireNonNull(pluginsDirectory.listFiles())) { | ||
if(!file.isDirectory()) continue; | ||
if(file.getName().equals("META-INF")) continue; | ||
|
||
File infoFile = new File(file.getAbsoluteFile() + File.separator + "plugin.properties"); | ||
File pluginRoot = new File(file.getAbsoluteFile() + File.separator + "plugin.class"); | ||
|
||
if (!pluginRoot.exists()) { | ||
System.err.println("Unable to load plugin " + file.getName() + " because plugin.class is absent!"); | ||
continue; | ||
} | ||
|
||
Class<?> clazz = loader.loadClass(file.getName() + ".plugin"); | ||
|
||
PluginInfo info; | ||
if (infoFile.exists()) | ||
info = PluginInfo.loadFromFile(infoFile); | ||
else | ||
info = PluginInfo.loadFromClass(clazz); | ||
|
||
if (info == null) { | ||
System.err.println("Unable to load plugin " + file.getName() + " because it contains no information about author, version, etc!"); | ||
continue; | ||
} | ||
|
||
try { | ||
Plugin thisPlugin = (Plugin) clazz.newInstance(); | ||
thisPlugin._init(); | ||
registerPlugin(info, thisPlugin); | ||
} catch (Exception e) { | ||
System.err.println("Error loading plugin " + file.getName() + ":"); | ||
e.printStackTrace(); | ||
return; | ||
} | ||
|
||
List<File> otherClasses = Arrays.stream(Objects.requireNonNull(file.listFiles())) | ||
.filter((f) -> | ||
!f.getName().equals("plugin.class") && f.getName().contains(".class")) | ||
.collect(Collectors.toList()); | ||
|
||
for (File f : otherClasses) { | ||
loader.loadClass(file.getName() + "." + f.getName().replace(".class","")); | ||
} | ||
|
||
System.out.println("Successfully loaded plugin " + file.getName() + ", version " + info.version); | ||
} | ||
} catch (Exception e) { | ||
System.err.println("Unexpected exception during plugin initialization:"); | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
public static void Update() { | ||
loadedPlugins.values().forEach(Plugin::Update); | ||
} | ||
|
||
public static void Draw() { | ||
loadedPlugins.values().forEach(Plugin::_draw); | ||
} | ||
|
||
public static void NPCOverheadDraw(Npc npc, int screenX, int screenY) { | ||
loadedPlugins.values().forEach((plugin) -> plugin.NPCOverheadDraw(npc, screenX, screenY)); | ||
} | ||
|
||
public static void PlayerOverheadDraw(Player player, int screenX, int screenY) { | ||
loadedPlugins.values().forEach((plugin) -> plugin.PlayerOverheadDraw(player, screenX, screenY)); | ||
} | ||
|
||
public static void ProcessCommand(JagString commandStr) { | ||
String[] tokens = commandStr.toString().split(" "); | ||
String[] args = Arrays.copyOfRange(tokens, 1, tokens.length); | ||
loadedPlugins.values().forEach((plugin) -> plugin.ProcessCommand(tokens[0], args)); | ||
} | ||
|
||
public static void ComponentDraw(int componentIndex, Component component, int screenX, int screenY) { | ||
loadedPlugins.values().forEach((plugin) -> plugin.ComponentDraw(componentIndex, component, screenX, screenY)); | ||
} | ||
|
||
public static void OnVarpUpdate(int id, int value) { | ||
loadedPlugins.values().forEach((plugin) -> plugin.OnVarpUpdate(id, value)); | ||
} | ||
|
||
public static void OnXPUpdate(int skill, int xp) { | ||
loadedPlugins.values().forEach((plugin) -> plugin.OnXPUpdate(skill, xp)); | ||
} | ||
|
||
public static void OnLogout() { | ||
loadedPlugins.values().forEach(Plugin::OnLogout); | ||
} | ||
|
||
public static void DrawMiniMenu(MiniMenuEntry entry) { | ||
loadedPlugins.values().forEach((plugin) -> plugin.DrawMiniMenu(entry)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package plugin.annotations; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface PluginMeta { | ||
String author(); | ||
String description(); | ||
double version(); | ||
} |
Oops, something went wrong.