public class PyrceptionInstaller : EditorWindow
{
//This files stores entries as ProjectDataPath,PythonPID,Port,PyrceptionPID
//It keeps a record of the instances of pyrception opened so that we don't open a new one everytime
private static readonly string _filename_streamlit_instances = "streamlit_instances.csv" ;
private static string pathToStreamlitInstances
{
= "" ;
#endif
/// <summary>
/// Runs pyrception instance in default browser
/// </summary>
static int LaunchPyrception ( )
{
string path = Path . GetFullPath ( Application . dataPath . Replace ( "/Assets" , "" ) ) ;
#if UNITY_EDITOR_WIN
string packagesPath = Path . GetFullPath ( Application . dataPath . Replace ( "/Assets" , "/Library/PythonInstall/Scripts" ) ) ;
#elif UNITY_EDITOR_OSX
string packagesPath = Application . dataPath . Replace ( "/Assets" , "/Library/PythonInstall/bin" ) ;
#endif
string pathToData = PlayerPrefs . GetString ( SimulationState . latestOutputDirectoryKey ) ;
#if UNITY_EDITOR_WIN
path = path . Replace ( "/" , "\\" ) ;
packagesPath = packagesPath . Replace ( "/" , "\\" ) ;
pathToData = pathToData . Replace ( "/" , "\\" ) ;
#endif
string command = "" ;
#if UNITY_EDITOR_WIN
command = $"cd \" { pathToData } \ \ . . \ " && \"{packagesPath}\\pyrception-utils.exe\" preview --data=\".\"" ;
#elif UNITY_EDITOR_OSX
command = $"cd \'{packagesPath}\' ;./python3.7 ./pyrception-utils.py preview --data=\'{pathToData}/..\'" ;
#endif
int ExitCode = 0 ;
int PID = ExecuteCMD ( command , ref ExitCode , waitForExit : false , displayWindow : true ) ;
if ( ExitCode ! = 0 )
{
UnityEngine . Debug . LogError ( "Problem occured when launching pyrception-utils - Exit Code: " + ExitCode ) ;
return - 1 ;
}
return PID ;
}
/// <summary>
/// Install pyrception (Assumes python3 and pip3 are already installed)
return - 1 ;
}
/// <summary>
/// If an instance is already running for this project it opens the browser at the correct port
/// If no instance is found it launches a new process
/// </summary>
//The dataPath is used as a unique identifier for the project
//If there is a python instance for this project AND it is alive then just run browser
//Otherwise delete any previous entry for this project and launch a new process
else
{
DeleteEntry ( project ) ;
}
Process [ ] after = null ;
//Poll for new processes until the pyrception process is launched
int newPyrceptionPID = - 1 ;
while ( newPyrceptionPID = = - 1 )
{
}
//Poll for new processes until the streamlit python script is launched
int newPythonPID = - 1 ;
while ( newPythonPID = = - 1 )
{
}
//Poll until the python script starts using the port
int newPort = - 1 ;
while ( newPort = = - 1 )
{
//Save this into the streamlit_instances.csv file
//When launching the process it will try to open a new tab in the default browser, however if a tab for it already exists it will not
//For convinience if the user wants to force a new one to open they can press on "manually open"
if ( EditorUtility . DisplayDialog ( "Opening Visualizer Tool" ,
$"The visualizer tool should open shortly in your default browser at http://localhost:{newPort}.\n\nIf this is not the case after a few seconds you may open it manually" ,
"Manually Open" ,
}
}
/// <summary>
/// Runs pyrception instance (streamlit) from the python for unity install
/// </summary>
static int LaunchPyrception ( )
{
string path = Path . GetFullPath ( Application . dataPath . Replace ( "/Assets" , "" ) ) ;
#if UNITY_EDITOR_WIN
string packagesPath = Path . GetFullPath ( Application . dataPath . Replace ( "/Assets" , "/Library/PythonInstall/Scripts" ) ) ;
#elif UNITY_EDITOR_OSX
string packagesPath = Application . dataPath . Replace ( "/Assets" , "/Library/PythonInstall/bin" ) ;
#endif
string pathToData = PlayerPrefs . GetString ( SimulationState . latestOutputDirectoryKey ) ;
#if UNITY_EDITOR_WIN
path = path . Replace ( "/" , "\\" ) ;
packagesPath = packagesPath . Replace ( "/" , "\\" ) ;
pathToData = pathToData . Replace ( "/" , "\\" ) ;
#endif
string command = "" ;
#if UNITY_EDITOR_WIN
command = $"cd \" { pathToData } \ \ . . \ " && \"{packagesPath}\\pyrception-utils.exe\" preview --data=\".\"" ;
#elif UNITY_EDITOR_OSX
command = $"cd \'{packagesPath}\' ;./python3.7 ./pyrception-utils.py preview --data=\'{pathToData}/..\'" ;
#endif
int ExitCode = 0 ;
int PID = ExecuteCMD ( command , ref ExitCode , waitForExit : false , displayWindow : true ) ;
if ( ExitCode ! = 0 )
{
UnityEngine . Debug . LogError ( "Problem occured when launching pyrception-utils - Exit Code: " + ExitCode ) ;
return - 1 ;
}
return PID ;
}
private static ( int pythonPID , int port , int pyrceptionPID ) ReadEntry ( string project )
{
string path = pathToStreamlitInstances ;
string [ ] entry = line . TrimEnd ( ) . Split ( ',' ) ;
if ( entry [ 0 ] = = project )
{
//The -1 on ports is because the System.Diagnosis.Process API starts at 0 where as the PID in Windows and Mac start at 1
return ( int . Parse ( entry [ 1 ] ) - 1 , int . Parse ( entry [ 2 ] ) , int . Parse ( entry [ 3 ] ) - 1 ) ;
}
}
}
}
/// <summary>
/// Finds the process id of the first process that is different between the before and after array
/// and that contains name
/// </summary>
/// <param name="before"></param>
/// <param name="after"></param>
/// <param name="name"></param>
/// <returns></returns>
private static int GetNewProcessID ( Process [ ] before , Process [ ] after , string name )
{
foreach ( Process p in after )
return - 1 ;
}
/// <summary>
/// Finds which port the process PID is using
/// </summary>
/// <param name="PID"></param>
/// <returns></returns>
private static int GetPortForPID ( int PID )
{
foreach ( ProcessPort p in ProcessPorts . ProcessPortMap )
return - 1 ;
}
/// <summary>
/// Launches browser at localhost:port
/// </summary>
/// <param name="port"></param>
/// <summary>
/// Check if streamlit process is alive
/// </summary>
/// <param name="pythonPID"></param>
/// <param name="port"></param>
/// <param name="pyrceptionPID"></param>
/// <returns></returns>
private static bool ProcessAlive ( int pythonPID , int port , int pyrceptionPID )
{
return PIDExists ( pythonPID ) & &
checkProcessName ( pyrceptionPID , nameOfPyrceptionProcess ) ;
}
/// <summary>
/// Check if a process with ProcessId = PID is alive
/// </summary>
/// <param name="PID"></param>
/// <returns></returns>
private static bool PIDExists ( int PID )
{
try
}
}
/// <summary>
/// Check if process with PID has a name that contains "name"
/// </summary>
/// <param name="PID"></param>
/// <param name="name"></param>
/// <returns></returns>
private static bool checkProcessName ( int PID , string name )
{
Process proc = Process . GetProcessById ( PID + 1 ) ;
/// <summary>
/// Check if the given PID listens to given port
/// </summary>
/// <param name="PID"></param>
/// <param name="port"></param>
/// <returns></returns>
private static bool ProcessListensToPort ( int PID , int port )
{
List < ProcessPort > processes = ProcessPorts . ProcessPortMap . FindAll (
Proc . StartInfo = StartInfo ;
Proc . Start ( ) ;
#if UNITY_EDITOR_OSX
#endif
StreamReader StandardOutput = Proc . StandardOutput ;
StreamReader StandardError = Proc . StandardError ;