Skip to content

Commit

Permalink
Add ClientScripts command for dynamically sending/removing client scr…
Browse files Browse the repository at this point in the history
…ipts

Yay!
  • Loading branch information
Morphan1 committed Jan 27, 2017
1 parent 77e196a commit cf481dd
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 8 deletions.
Expand Up @@ -27,7 +27,10 @@ public class ClientRunCommand extends AbstractCommand {
// @Author Morphan1

// @Description
// TODO
// This command is used to run a script on the Minecraft client by utilizing the Denizen2 Forge mod
// implementation, Clientizen.
// The script must be sent beforehand, either automatically when the player joined or sent by
// the <@link command ClientScripts> command.

// @Tags
// None
Expand Down
@@ -0,0 +1,109 @@
package com.denizenscript.depenizen.bukkit.commands.clientizen;

import com.denizenscript.depenizen.bukkit.support.clientizen.ClientizenSupport;
import net.aufdemrand.denizen.BukkitScriptEntryData;
import net.aufdemrand.denizen.objects.dPlayer;
import net.aufdemrand.denizencore.exceptions.CommandExecutionException;
import net.aufdemrand.denizencore.exceptions.InvalidArgumentsException;
import net.aufdemrand.denizencore.objects.aH;
import net.aufdemrand.denizencore.objects.dList;
import net.aufdemrand.denizencore.scripts.ScriptEntry;
import net.aufdemrand.denizencore.scripts.commands.AbstractCommand;
import net.aufdemrand.denizencore.utilities.debugging.dB;

import java.util.Collections;
import java.util.List;

public class ClientScriptsCommand extends AbstractCommand {

// <--[command]
// @Name ClientRun
// @Syntax clientscripts [add/remove] [<file_name>|...] (players:<player>|...)
// @Group Depenizen
// @Plugin DepenizenBukkit
// @Required 2
// @Stable stable
// @Short Sends or removes client scripts, assuming they have the Forge mod 'Clientizen'.
// @Author Morphan1

// @Description
// This command allows dynamic control of loading and unloading client scripts.
// The file name will never include subfolders, as no two files should share names. For example,
// if you have a file at '/client_scripts/subfolder/myscript.yml', you would input simply
// it as 'myscript.yml'.

// @Tags
// None

// @Usage
// Loads the script file "myscript.yml" for all online players.
// - clientscripts add myscript.yml <server.list_online_players>

// -->

@Override
public void parseArgs(ScriptEntry scriptEntry) throws InvalidArgumentsException {

for (aH.Argument arg : aH.interpret(scriptEntry.getArguments())) {

if (!scriptEntry.hasObject("action")
&& arg.matchesEnum(Action.values())) {
scriptEntry.addObject("action", Action.valueOf(arg.getValue()));
}

else if (!scriptEntry.hasObject("players")
&& arg.matchesPrefix("players")) {
scriptEntry.addObject("players", arg.asType(dList.class).filter(dPlayer.class));
}

else if (!scriptEntry.hasObject("files")) {
scriptEntry.addObject("files", arg.asType(dList.class));
}

}

if (!scriptEntry.hasObject("action") || !scriptEntry.hasObject("files")) {
throw new InvalidArgumentsException("Must specify action and file names!");
}

if (!scriptEntry.hasObject("players")) {
scriptEntry.addObject("players", Collections.singletonList(((BukkitScriptEntryData) scriptEntry.entryData).getPlayer()));
}
}

@Override
public void execute(ScriptEntry scriptEntry) throws CommandExecutionException {

Action action = (Action) scriptEntry.getObject("action");
dList files = scriptEntry.getdObject("files");
List<dPlayer> players = (List<dPlayer>) scriptEntry.getObject("players");

dB.report(scriptEntry, getName(), aH.debugObj("action", action)
+ files.debug()
+ aH.debugList("players", players));

for (String file : files) {
if (!ClientizenSupport.isScriptLoaded(file)) {
dB.echoError("Failed to send scripts because '" + file + "' doesn't exist or isn't loaded!");
return;
}
}

switch (action) {
case ADD:
for (dPlayer player : players) {
ClientizenSupport.sendScripts(player.getPlayerEntity(), files);
}
break;
case REMOVE:
for (dPlayer player : players) {
ClientizenSupport.removeScripts(player.getPlayerEntity(), files);
}
break;
}
}

private enum Action {
ADD, REMOVE
}
}
Expand Up @@ -2,6 +2,7 @@

import com.denizenscript.depenizen.bukkit.DepenizenPlugin;
import com.denizenscript.depenizen.bukkit.commands.clientizen.ClientRunCommand;
import com.denizenscript.depenizen.bukkit.commands.clientizen.ClientScriptsCommand;
import com.denizenscript.depenizen.bukkit.support.Support;
import com.denizenscript.depenizen.common.socket.DataDeserializer;
import com.denizenscript.depenizen.common.socket.DataSerializer;
Expand Down Expand Up @@ -30,17 +31,35 @@
public class ClientizenSupport extends Support implements Listener, PluginMessageListener {

private static final Map<String, String> clientScripts = new HashMap<String, String>();
private static final List<String> autoScripts = new ArrayList<String>();
private static final List<UUID> playersWithMod = new ArrayList<UUID>();
private static final Map<UUID, List<String>> playerScripts = new HashMap<UUID, List<String>>();

public static final File clientScriptsFolder;
public static final String clientScriptsPath;
public static final String autoScriptsPath;

static {
clientScriptsFolder = new File(DepenizenPlugin.getCurrentInstance().getDataFolder(), "client_scripts");
clientScriptsFolder.mkdirs();
File autoScriptsFolder = new File(clientScriptsFolder, "auto");
autoScriptsFolder.mkdirs();
String scriptsPath = null;
String autoPath = null;
try {
scriptsPath = clientScriptsFolder.getCanonicalPath();
autoPath = autoScriptsFolder.getCanonicalPath();
}
catch (IOException e) {
DepenizenPlugin.getCurrentInstance().debugException(e);
}
clientScriptsPath = scriptsPath;
autoScriptsPath = autoPath;
}

public ClientizenSupport() {
new ClientRunCommand().activate().as("CLIENTRUN").withOptions("clientrun [<script_name>] (def:<name>|<value>|...)", 1);
new ClientScriptsCommand().activate().as("CLIENTSCRIPTS").withOptions("clientscripts [add/remove] [<file_name>|...] (players:<player>|...)", 2);
Bukkit.getMessenger().registerIncomingPluginChannel(DepenizenPlugin.getCurrentInstance(), "Clientizen", this);
Bukkit.getMessenger().registerOutgoingPluginChannel(DepenizenPlugin.getCurrentInstance(), "Clientizen");
Bukkit.getServer().getPluginManager().registerEvents(this, DenizenAPI.getCurrentInstance());
Expand All @@ -52,12 +71,25 @@ private static void reloadClientScripts() {
List<File> files = CoreUtilities.listDScriptFiles(clientScriptsFolder);
for (File file : files) {
try {
String path = file.getCanonicalPath();
String fileName = CoreUtilities.toLowerCase(file.getName());
if (clientScripts.containsKey(fileName)) {
DepenizenPlugin.depenizenLog("Multiple files named '" + fileName + "' found! " +
"Please remember Denizen is case-insensitive and client scripts do " +
"not support similarly named files, even across subfolders.");
continue;
}
FileInputStream fis = new FileInputStream(file);
String contents = ScriptHelper.convertStreamToString(fis);
fis.close();
String fileName = file.getName();
clientScripts.put(fileName, contents);
DepenizenPlugin.depenizenLog("Loaded client script file: " + fileName);
if (path.startsWith(autoScriptsPath)) {
autoScripts.add(fileName);
DepenizenPlugin.depenizenLog("Loaded auto-send client script file: " + fileName);
}
else {
DepenizenPlugin.depenizenLog("Loaded dynamic client script file: " + fileName);
}
}
catch (FileNotFoundException e) {
DepenizenPlugin.getCurrentInstance().debugException(e);
Expand All @@ -75,7 +107,7 @@ public void onScriptReload(ScriptReloadEvent event) {
int ignored = 0;
for (Player player : Bukkit.getOnlinePlayers()) {
if (playersWithMod.contains(player.getUniqueId())) {
sendAllScripts(player);
resendAllScripts(player);
count++;
}
else {
Expand All @@ -90,6 +122,7 @@ public void onPlayerQuit(PlayerQuitEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
if (playersWithMod.contains(uuid)) {
playersWithMod.remove(uuid);
playerScripts.remove(uuid);
}
}

Expand All @@ -107,14 +140,82 @@ public void run() {
}, 10);
}

public static void sendAllScripts(Player player) {
public static boolean isScriptLoaded(String scriptName) {
return clientScripts.containsKey(CoreUtilities.toLowerCase(scriptName));
}

public static void resendAllScripts(Player player) {
UUID uuid = player.getUniqueId();
if (!playersWithMod.contains(uuid)) {
return;
}
List<String> currentScripts = playerScripts.get(uuid);
Map<String, String> scripts = new HashMap<String, String>();
for (String scriptName : currentScripts) {
if (clientScripts.containsKey(scriptName)) {
scripts.put(scriptName, clientScripts.get(scriptName));
}
}
for (String autoScript : autoScripts) {
if (!scripts.containsKey(autoScript)) {
scripts.put(autoScript, clientScripts.get(autoScript));
}
}
currentScripts.clear();
for (String newScript : scripts.keySet()) {
currentScripts.add(newScript);
}
DataSerializer serializer = new DataSerializer();
serializer.writeString("ClearAndLoadScripts");
serializer.writeStringMap(scripts);
send(player, serializer);
}

public static void sendAutoScripts(Player player) {
sendScripts(player, autoScripts);
}

public static void sendScripts(Player player, List<String> scriptNames) {
UUID uuid = player.getUniqueId();
if (!playersWithMod.contains(uuid)) {
return;
}
List<String> currentScripts = playerScripts.get(uuid);
Map<String, String> scripts = new HashMap<String, String>();
for (String scriptName : scriptNames) {
if (clientScripts.containsKey(scriptName) && !currentScripts.contains(scriptName)) {
scripts.put(scriptName, clientScripts.get(scriptName));
currentScripts.add(scriptName);
}
}
DataSerializer serializer = new DataSerializer();
serializer.writeString("LoadAllScripts");
serializer.writeStringMap(clientScripts);
serializer.writeString("LoadScripts");
serializer.writeStringMap(scripts);
send(player, serializer);
}

public static void removeScripts(Player player, List<String> scriptNames) {
UUID uuid = player.getUniqueId();
if (!playersWithMod.contains(uuid)) {
return;
}
List<String> currentScripts = playerScripts.get(uuid);
for (String scriptName : scriptNames) {
if (currentScripts.contains(scriptName)) {
currentScripts.remove(scriptName);
}
}
DataSerializer serializer = new DataSerializer();
serializer.writeString("RemoveScripts");
serializer.writeStringList(scriptNames);
send(player, serializer);
}

public static void runScript(Player player, String script, Map<String, String> definitions) {
UUID uuid = player.getUniqueId();
if (!playersWithMod.contains(uuid)) {
return;
}
DataSerializer serializer = new DataSerializer();
serializer.writeString("RunScript");
serializer.writeString(script);
Expand All @@ -135,7 +236,8 @@ public void onPluginMessageReceived(String channel, Player player, byte[] bytes)
UUID uuid = player.getUniqueId();
if (!playersWithMod.contains(uuid)) {
playersWithMod.add(uuid);
sendAllScripts(player);
playerScripts.put(uuid, new ArrayList<String>());
sendAutoScripts(player);
}
}
}
Expand Down

0 comments on commit cf481dd

Please sign in to comment.