Skip to content

Commit

Permalink
feat(tsl): implement OS_RUN advanced action
Browse files Browse the repository at this point in the history
  • Loading branch information
iGoodie committed Aug 28, 2019
1 parent 1a3a10c commit 8d62d2a
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 13 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Expand Up @@ -5,7 +5,7 @@ org.gradle.daemon=false

mod_id=twitchspawn
mod_group=net.programmer.igoodie
mod_version=1.2.3
mod_version=1.2.4

minecraft_version=1.14.4
forge_version=28.0.55
Expand Down
Expand Up @@ -287,7 +287,6 @@ public static int testModule(CommandContext<CommandSource> context, String strea

index++;
}

}

TwitchSpawn.LOGGER.info("Tests queued for {}", streamerNick);
Expand Down
@@ -1,14 +1,20 @@
package net.programmer.igoodie.twitchspawn.network;

import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;
import net.programmer.igoodie.twitchspawn.TwitchSpawn;
import net.programmer.igoodie.twitchspawn.network.packet.OsRunPacket;
import net.programmer.igoodie.twitchspawn.network.packet.StatusChangedPacket;

import java.lang.reflect.Method;
import java.util.function.Supplier;

public class NetworkManager {

private static final String PROTOCOL_VERSION = "0.0.1";
private static final String PROTOCOL_VERSION = "0.0.2";

public static final SimpleChannel CHANNEL = NetworkRegistry.ChannelBuilder
.named(new ResourceLocation(TwitchSpawn.MOD_ID, "network"))
Expand All @@ -22,15 +28,11 @@ public static void initialize() {
StatusChangedPacket::encode,
StatusChangedPacket::decode,
StatusChangedPacket::handle);
}

private static void registerPacket(int index, Class<?> packetType) {
// TODO: Automatize encode/decode/handle registration
// int index,
// Class<MSG> messageType,
// java.util.function.BiConsumer<MSG, net.minecraft.network.PacketBuffer> encoder,
// java.util.function.Function<net.minecraft.network.PacketBuffer, MSG> decoder,
// java.util.function.BiConsumer<MSG, java.util.function.Supplier<net.minecraftforge.fml.network.NetworkEvent.Context>> messageConsumer
CHANNEL.registerMessage(1, OsRunPacket.class,
OsRunPacket::encode,
OsRunPacket::decode,
OsRunPacket::handle);
}

}
@@ -0,0 +1,47 @@
package net.programmer.igoodie.twitchspawn.network.packet;

import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent;
import net.programmer.igoodie.twitchspawn.TwitchSpawn;
import net.programmer.igoodie.twitchspawn.tslanguage.action.OsRunAction;

import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;

public class OsRunPacket {

public static void encode(OsRunPacket packet, PacketBuffer buffer) {
buffer.writeInt(packet.shell.ordinal());
buffer.writeString(packet.script);
}

public static OsRunPacket decode(PacketBuffer buffer) {
OsRunAction.Shell shell = OsRunAction.Shell.values()[buffer.readInt()];
String script = buffer.readString();

return new OsRunPacket(shell, script);
}

public static void handle(final OsRunPacket packet, Supplier<NetworkEvent.Context> context) {
context.get().enqueueWork(() -> {
OsRunAction.ProcessResult result = OsRunAction.runScript(packet.shell, packet.script);
if (result.exception != null)
TwitchSpawn.LOGGER.info("OS_RUN failed to run. ({})", result.exception.getMessage());
else
TwitchSpawn.LOGGER.info("OS_RUN done with exit code {}:\n{}", result.exitCode, result.output);
});
context.get().setPacketHandled(true);
}

/* ------------------------------------------------ */

private OsRunAction.Shell shell;
private String script;

public OsRunPacket(OsRunAction.Shell shell, String script) {
this.shell = shell;
this.script = script;
}

}
Expand Up @@ -25,7 +25,7 @@ public static void handle(final StatusChangedPacket packet, Supplier<NetworkEven

/* ---------------------------- */

boolean status;
private boolean status;

public StatusChangedPacket(boolean status) {
this.status = status;
Expand Down
Expand Up @@ -35,6 +35,7 @@ public ForAction(List<String> words) throws TSLSyntaxError {
try {
this.iterationCount = Integer.parseInt(actionWords.get(0));
this.action = TSLParser.parseAction(words.get(2), words.subList(3, words.size()));
this.action.silent = true;

} catch (NumberFormatException e) {
throw new TSLSyntaxError("Malformed number word -> %s", actionWords.get(0));
Expand Down
@@ -0,0 +1,157 @@
package net.programmer.igoodie.twitchspawn.tslanguage.action;

import com.google.common.base.Defaults;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraftforge.fml.network.NetworkDirection;
import net.programmer.igoodie.twitchspawn.TwitchSpawn;
import net.programmer.igoodie.twitchspawn.network.NetworkManager;
import net.programmer.igoodie.twitchspawn.network.packet.OsRunPacket;
import net.programmer.igoodie.twitchspawn.tslanguage.EventArguments;
import net.programmer.igoodie.twitchspawn.tslanguage.parser.TSLParser;
import net.programmer.igoodie.twitchspawn.tslanguage.parser.TSLSyntaxError;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class OsRunAction extends TSLAction {

public static class ProcessResult {
public Exception exception;
public String output = "";
public int exitCode = 0;
}

public static ProcessResult runScript(Shell shell, String script) {
try {
List<String> processScript = new LinkedList<>(shell.processPrefix);
processScript.add(script);

ProcessBuilder processBuilder = new ProcessBuilder(processScript);
Process process = processBuilder.start();
ProcessResult result = new ProcessResult();

BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

String delimiter = "";
String line;
while ((line = reader.readLine()) != null) {
result.output += (line + delimiter);
delimiter = "\n";
}

result.exitCode = process.waitFor();
return result;

} catch (Exception e) {
TwitchSpawn.LOGGER.info("Failed to run shell script -> {}", script);
ProcessResult result = new ProcessResult();
result.exception = e;
return result;
}
}

public enum ScriptLocation {LOCAL, REMOTE}

public enum Shell {
CMD("cmd", "/c"),
POWERSHELL("powershell", "/c"),
BASH();

public List<String> processPrefix;

Shell(String... processPrefix) {
this.processPrefix = Arrays.asList(processPrefix);
}
}

/* ---------------------------------- */

private ScriptLocation scriptLocation;
private Shell shell;
private boolean parameterized;
private String shellScript;

/*
* E.g usage:
* OS_RUN LOCAL POWERSHELL echo %"Hello world!"%
* OS_RUN LOCAL PARAMETERIZED BASH sh %some/path/to/script.sh%
*
* 0 - LOCAL/REMOTE
* 1 - [PARAMETERIZED]
* 1|2 - <shell_name>
* 2|3 - <action>
*/
public OsRunAction(List<String> words) throws TSLSyntaxError {
this.message = TSLParser.parseMessage(words);
List<String> actionWords = actionPart(words);

try {
try {
scriptLocation = ScriptLocation.valueOf(actionWords.get(0));

} catch (IllegalArgumentException e) {
throw new TSLSyntaxError("Unknown script location -> %s", actionWords.get(0));
}

parameterized = actionWords.get(1).equalsIgnoreCase("PARAMETERIZED");

try {
shell = Shell.valueOf(actionWords.get(parameterized ? 2 : 1));

} catch (IllegalArgumentException e) {
throw new TSLSyntaxError("Unknown shell name -> %s", actionWords.get(parameterized ? 2 : 1));
}

shellScript = parameterized
? actionWords.get(3)
: actionWords.get(2);

} catch (IndexOutOfBoundsException e) {
throw new TSLSyntaxError("Invalid length of words -> %s", actionWords);
}
}

@Override
protected void performAction(ServerPlayerEntity player, EventArguments args) {
String script = shellScript;

if (parameterized) {
try {
for (Field field : EventArguments.class.getFields()) {
Object value = field.get(args);
Object defaultValue = Defaults.defaultValue(field.getType());

if ((value instanceof String) && ((String) value).isEmpty())
value = null;

if (value != null && !value.equals(defaultValue)) {
script += (" -" + field.getName() + ":"
+ ((value instanceof String)
? "\"" + value + "\"" : value));
}
}

} catch (IllegalAccessException e) {
throw new InternalError("Error trying to parameterize OS_RUN");
}
}

if (scriptLocation == ScriptLocation.LOCAL) {
ProcessResult result = runScript(shell, script);
if (result.exception != null)
TwitchSpawn.LOGGER.info("OS_RUN failed to run. ({})", result.exception.getMessage());
else
TwitchSpawn.LOGGER.info("OS_RUN done with exit code {}:\n{}", result.exitCode, result.output);

} else if (scriptLocation == ScriptLocation.REMOTE) {
NetworkManager.CHANNEL.sendTo(new OsRunPacket(shell, script),
player.connection.netManager,
NetworkDirection.PLAY_TO_CLIENT);
}
}

}
Expand Up @@ -14,6 +14,8 @@ public enum TSLActionKeyword {
EITHER(false, EitherAction.class),
BOTH(false, BothAction.class),
FOR(false, ForAction.class),

OS_RUN(true, OsRunAction.class),
;

public static boolean exists(String actionName) {
Expand Down
Expand Up @@ -66,5 +66,15 @@
"text": " triggered no action!",
"color": "white"
}
],
"OS_RUN": [
{
"text": "${actor}",
"color": "aqua"
},
{
"text": " triggered a shell script!",
"color": "white"
}
]
}
Expand Up @@ -6,7 +6,6 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
Expand Down

0 comments on commit 8d62d2a

Please sign in to comment.