Catch non-standard output from .jar file - c#

I'm trying to make a GUI for hosting Minecraft CraftBukkit servers in C#. CraftBukkit servers are hosted with a .jar which's source code can be found here: https://github.com/Bukkit/CraftBukkit/.
So far I am able to receive output from it and give input to it like this:
var serverProcInfo = new ProcessStartInfo("javaw",
"-jar -Xms" + Ram + "M -Xmx" + Ram + "M \"" +
JarFileLocation + "\" -nojline " + AdditionalParams)
{
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
serverProc = new Process {StartInfo = serverProcInfo };
serverProc.OutputDataReceived += ServerOutputDataReceived;
serverProc.ErrorDataReceived += ServerOutputDataReceived;
serverProc.BeginOutputReadLine();
serverProc.BeginErrorReadLine();
serverProc.WaitForExit();
I want to create a list of all the players that are currently online.
When a player joins a message like this is outputed player < playername > has joined the server. I know that I could split this string and get the player name, but I think that it is not very good since somebody may say something like that in chat, and my program could interpret it as if somebody joined the server.
I saw other programs do this ("BukkitGUI"), but I'm not sure how I could do this. Does the .jar file output the player list? What should I look for in its source code to see what it outputs?
I am relatively new to C# and have never learned Java. Any help would be appreciated.
EDIT1:
I found that the CraftBukkit.jar has a class like this:
#SuppressWarnings("unchecked")
public Player[] getOnlinePlayers() {
List<EntityPlayer> online = playerList.players;
Player[] players = new Player[online.size()];
for (int i = 0; i < players.length; i++) {
players[i] = online.get(i).playerConnection.getPlayer();
}
return players;
}
Is there any way I can call this class?

Unfortunately cross-process communication between java and C# is not so simple. Your best bet is to either send the list command to the server and parse the output, or if you're willing to write some java you could create a plugin for the minecraft server that opens a socket and send information back and forth.

I find a solution to this. It is actually quite easy.
First I create a process that will start the .jar file. Then I do the following:
Process.OutputDataReceived += ServerOutputHandler.ServerOutputReceived;
Process.ErrorDataReceived += ServerOutputHandler.ServerOutputReceived;
and after I start the process (Process.Start):
Process.BeginOutputReadLine();
Process.BeginErrorReadLine();
If you want to know more about handling .jar files, feel free to take a look at my open source project (I gave up it a while ago) ServerCrafter. For the part of the source code where I do code I mention above, take a look here: Server Crafter source code: ServerCrafter\ServerCrafter.ClassLibrary\ClassLibrary\Server\Server.cs

Related

system.componentmodel.win32exception when trying to run stockfish binary file for android

I am creating a Chess Game in Unity. I want to build my game for Android. For AI I am using Stockfish Chess Engine and more particularly "Stockfish-9-arm64v8" file which is a binary file for Android. I have created a C# script which creates a process to run this binary file and communicate with it. When I try to Start my process the exception is raised ->
try{
mProcess.Start();
}
catch(Exception e){
Helper.PrintString(e.GetType().ToString()); // ----------------(1)
Helper.PrintString(e.Message); // --------------(2)
}
/*
(1) is printing :
system.componentmodel.win32exception
(2) is printing :
ApplicationName =
'/storage/emulated/0/Android/data/com.chessmania.chess/files/Stockfish-9- arm64v8', CommandLine = '', CurrentDirectory = ''
*/
Also my process info parameters are as follows:
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = System.IO.Path.Combine(Application.persistentDataPath, "Stockfish-9-arm64v8"),
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
Can anyone please help me with it. I have been trying to resolve this issue past few days but unable to. Does it have something to do with file permission?? I mean shall I include some arguments in my ProcessInfoParameters to forcefully run the binary file?? I really don't know if this is the problem or something else? Correct me from the start if I am wrong.
I just want to integrate Stockfish Chess Engine with with Unity Project and build it for Android Platform. If anyone has any ideas or suggestions or if anyone has dealt with similar problem before, please let me know how to solve this issue. I would be grateful. Thanks for bearing with me till here :)

Not able to communicate with "Stockfish-9-armv7" binary file

I am developing a Chess Game in Unity3D. I want to develop it for the Android platform. For AI I am using the Stockfish Chess Engine. I downloaded the Stockfish binary for Android named "Stockfish-9-armv7". I placed this binary file in my StreamingAssets folder so that it correctly goes into the targeted platform during build step. Everything works fine till here i.e. when I build my Unity project the file is placed in the correct location and I can very well see it.
Now in order for my AI to work I have to communicate with this binary file using UCI protocols. And so I wrote a C# script in my Unity project that creates a process to run the binary file and communicate with it. But this is not working.
However when I do the exact same thing for Windows i.e. using the windows binary version of Stockfish named "stockfish_9_x64.exe" and building it as a standalone application, things work perfectly and I am able to communicate with the engine via my C# code.
I researched it online but unable to find much resources and guidance. I found a similar post and reading through it led me conclude that maybe it has something to do with file permissions. The guy who asked this question actually solved the issue by writing these two lines of code:
string[] cmd = { "chmod", "744", Path.Combine(strToFolder, fileName) };
Java.Lang.Runtime.GetRuntime().Exec(cmd);
However he was using Xamarin and had access to Java Runtime library. I am using Unity and C# and I really don't know how to change the execute/run permission of this binary file and run it. In fact I don't even know whether this is the problem or not.
I just want to integrate stockfish into my Unity project with Android as the targeted platform. If anyone has any ideas, suggestions or if anyone has done this before, please guide me. Even if I am wrong from the start and my approach is buggy let me know that as well along with the corrected approach.
Given below is my code:
public class CommunicateWithEngine {
public static Process mProcess;
public static void Communicate()
{
// since the apk file is archived this code retreives the stockfish binary data and
// creates a copy of it in the persistantdatapath location.
string filepath = Application.persistentDataPath + "/" + "Stockfish-9-armv7";
if (!File.Exists(filepath))
{
WWW executable = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "Stockfish-9-armv7");
while (!executable.isDone)
{
}
File.WriteAllBytes(filepath, executable.bytes);
}
// creating the process and communicating with the engine
mProcess = new Process();
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = System.IO.Path.Combine(Application.persistentDataPath, "Stockfish-9-armv7"),
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
mProcess.StartInfo = si;
mProcess.OutputDataReceived += new DataReceivedEventHandler(MProcess_OutputDataReceived);
mProcess.Start();
mProcess.BeginErrorReadLine();
mProcess.BeginOutputReadLine();
SendLine("uci");
SendLine("isready");
}
private static void SendLine(string command) {
mProcess.StandardInput.WriteLine(command);
mProcess.StandardInput.Flush();
}
private static void MProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string text = e.Data;
Test.PrintStringToTheConsole(text);
}
}
For anyone struggling with the same problem as OP and me. After 12 hours searching and googling here is a solution. You can use executable stockfish binary, but you have to change extension for binary to .so and import it in unity in folder /Assets/Plugins/Android. After build you can find it on device in folder /data/data//lib. Here you can execute it and get input and output.

How to send data from C# to Python

How can i send this list to list in Python script? This is so big for sending as arguments. Thank you.
List<String> cefList= new List<String>();
for(int i=0; i<1000; i++){
cefList.Add("CEF:0|ArcSight|ArcSight|6.0.3.6664.0|agent:030|Agent [test] type [testalertng] started|Low|
eventId=1 mrt=1396328238973 categorySignificance=/Normal categoryBehavior=/Execute/Start
categoryDeviceGroup=/Application catdt=Security Mangement categoryOutcome=/Success
categoryObject=/Host/Application/Service art=1396328241038 cat=/Agent/Started
deviceSeverity=Warning rt=1396328238937 fileType=Agent
cs2=<Resource ID\="3DxKlG0UBABCAA0cXXAZIwA\=\="/> c6a4=fe80:0:0:0:495d:cc3c:db1a:de71
cs2Label=Configuration Resource c6a4Label=Agent
IPv6 Address ahost=SKEELES10 agt=888.99.100.1 agentZoneURI=/All Zones/ArcSight
System/Private Address Space
Zones/RFC1918: 888.99.0.0-888.200.255.255 av=6.0.3.6664.0 atz=Australia/Sydney
aid=3DxKlG0UBABCAA0cXXAZIwA\=\= at=testalertng dvchost=SKEELES10 dvc=888.99.100.1
deviceZoneURI=/All Zones/ArcSight System/Private Address Space Zones/RFC1918:
888.99.0.0-888.200.255.255 dtz=Australia/Sydney _cefVer=0.1");
}
Since your C# program runs the python script, I guess the easiest solution would be to redirect the standard input of the python process:
Process pyProc = Process.Start(
new ProcessStartInfo("python.exe", #"/path/to/the/script.py")
{
RedirectStandardInput = true,
UseShellExecute = false
}
);
for (int ii = 0; ii < 100; ++ii)
{
pyProc.StandardInput.WriteLine(string.Format("this is message # {0}", ii));
}
At the python script side, you just need to use built-in function raw_input like below (please note the function has been renamed to raw_input in 3.x):
while True:
data = raw_input()
print(data)
You need to serialize the data to a common format that is accessible from both C# and Python. For example - XML or JSON. I would recommend using JSON.
Then you have several options:
Use sockets to transfer the data.
Use http to transfer the data.
Write to a file from C# and read that file from Python
Sockets would probably be faster. Using http might be easier. With files, you will need to have some sort of scheduling or notification system to let your Python program know when you have written to the file.

Getting output from one executable in an other one

I'm currently trying to get the output of an executable console-app into an other one. To be exact, a little overview of what I'm trying to do:
I have one executable which I cannot edit and neither see it's code. It writes some (quite a bunch to be honest) lines into the console when executed.
Now I want to write another executable that starts the one above and reads the things it writes.
Seems simple to me, so I started coding but ended up with an error message saying that StandardOut has not been redirected or the process hasn't started yet.
I tried it using this kinda structure (C#):
Process MyApp = Process.Start(#"C:\some\dirs\foo.exe", "someargs");
MyApp.Start();
StreamReader _Out = MyApp.StandardOutput;
string _Line = "";
while ((_Line = _Out.ReadLine()) != null)
Console.WriteLine("Read: " + _Line);
MyApp.Close();
I can open the executable and it also does open the one inside, but as soon as it comes to reading the returned values, the app crashes.
What am I doing wrong?!
Take a look at the documentation for the Process.StandardOutput property. You will need to set a boolean indicating that you want the stream redirected as well as disabling shell execute.
Note from the documentation:
To use StandardOutput, you must set ProcessStartInfo..::.UseShellExecute to false, and you must set ProcessStartInfo..::.RedirectStandardOutput to true. Otherwise, reading from the StandardOutput stream throws an exception
You would need to change your code a little bit to adjust for the changes:
Process myApp = new Process(#"C:\some\dirs\foo.exe", "someargs");
myApp.StartInfo.UseShellExecute = false;
myApp.StartInfo.RedirectStandardOutput = false;
myApp.Start();
string output = myApp.StandardOutput.ReadToEnd();
p.WaitForExit();
you could try setting processStartInfo.RedirectStandardOutput = true;
As noted above, you can use RedirectStandardOutput as here.
Another, dirtier way is something like
using (Process child = Process.Start
("cmd", #"/c C:\some\dirs\foo.exe someargs > somefilename"))
{
exeProcess.WaitForExit();
}
And then read its output from somefilename

Perl Script Output Capture Problem using C#

I was following one of the thread to run perl scripts from my c# program.
My c# code is like this:
private void RunScript(ArrayList selectedScriptFileList)
{
foreach (var curScriptFileName in selectedScriptFileList)
{
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo("perl.exe");
myProcessStartInfo.Arguments = (string)(curScriptFileName);
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
myProcessStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myProcessStartInfo.CreateNoWindow = true;
myProcess.StartInfo = myProcessStartInfo;
myProcess.Start();
myProcess.WaitForExit();
string output = myProcess.StandardOutput.ReadToEnd();
this.ScriptTestResultTextBox.AppendText(output);
}
}
And my perl script requires XML parsing. I can read the print statement before the XML parsing, but not after the parsing starts. The script runs find on DoS shell.
Here is part of my script:
print("\n");
print("****************** test1.pl ***********************\n");
print("\n");
print("1");
print("2");
my $scriptName = 'test1.pl';
my $file = '../../ScriptParamLib.xml';
my $parser = XML::LibXML->new();
my $tree = $parser->parse_file($file);
my $root = $tree->getDocumentElement;
my #species = $root->getElementsByTagName('test_node');
print("Accessing XML Data Base...\n");
The c# testbox only shows the first three print statement but not the last one.
Does anybody knows why?
Thanks
You could add more debugging print statements (e.g. one between every other line of your code) to see how far the execution gets. However, I'm going to go on a hunch and suggest that adding these three lines to your script will either solve the problem outright or lead you closer to a solution:
use strict;
use warnings;
use XML::LibXML;
Please update your question indicating how far execution gets and what errors you see!
I figured I should roll my comments into an answer since they proved to be helpful:
Since using an absolute path for $file in the Perl script works, the issue most likely has something to do with the working directory of the process that gets spawned from the C# program. You can use the Cwd module in the Perl script to see what the working directory actually is. If it's not what you expect, try setting it via the WorkingDirectory property of ProcessStartInfo in your C# program. Relative paths should work just fine after that.

Categories