Asset - Extension Execute Shell (for Windows, macOS, and Ubuntu)

S

Sam (Deleted User)

Guest
A user told me they can't get the mac port of this extension working. I don't have any problems with it on my end, is there anyone else who can verify this is an issue? If so, I'lll need a sample project reproducing the issue.
 
Hi. I am using this to get past what I assume is the sandboxing, so an included file can be copied, have its contents edited and then be directly saved back into the original by way of Notepad. For whatever reason I couldn't do this any other way within GMS, but I wondered if you are aware of whether this is misusing it?

I ask because this is something being considered for a tutorial on how to make your own text editor (although it's mainly about me making a much needed utility to tidy up my messy scripts) and I don't want to promote bad practices. Not really bothered about side effects personally, since its working for me, but I'd rather be 100% clear this is something I can suggest to others. And you might not want crediting for something that, I dunno, blows up peoples computers or some such :)
 
S

Sam (Deleted User)

Guest
Hi. I am using this to get past what I assume is the sandboxing, so an included file can be copied, have its contents edited and then be directly saved back into the original by way of Notepad. For whatever reason I couldn't do this any other way within GMS, but I wondered if you are aware of whether this is misusing it?

I ask because this is something being considered for a tutorial on how to make your own text editor (although it's mainly about me making a much needed utility to tidy up my messy scripts) and I don't want to promote bad practices. Not really bothered about side effects personally, since its working for me, but I'd rather be 100% clear this is something I can suggest to others. And you might not want crediting for something that, I dunno, blows up peoples computers or some such :)
This isn't any less dangerous than any other form of executable code. It's what people do with it that can cause trouble. Same could be said about visual studio, microsoft's official IDE for C++ development; people program viruses using that. I don't know why people consider this any more dangerous. You could swap out any dll and have an exe run malicious code. These vulnerabilities exist with nearly all software on the planet. No need to single this one out or call it a bad practice, because believe me, it's nothing different from the ordinary. GameMaker used to have this feature built-in. They took it out along with a ton of other features claiming them to be not cross-platform even though we all know the truth behind that. They wanted less work to do. Less code to have to port to multiple platforms. At least, that's my impression.
 
This isn't any less dangerous than any other form of executable code. It's what people do with it that can cause trouble. Same could be said about visual studio, microsoft's official IDE for C++ development; people program viruses using that. I don't know why people consider this any more dangerous. You could swap out any dll and have an exe run malicious code. These vulnerabilities exist with nearly all software on the planet. No need to single this one out or call it a bad practice, because believe me, it's nothing different from the ordinary. GameMaker used to have this feature built-in. They took it out along with a ton of other features claiming them to be not cross-platform even though we all know the truth behind that. They wanted less work to do. Less code to have to port to multiple platforms. At least, that's my impression.
My apologies if you thought I was attacking you for being able to do this. "Bad practices" was a poor choice of words, but there might be consequences I'm unaware of in terms of system integrity / code integrity. I don't know :) That's what I meant by bad practices.

It's for a text editor built from GML (I have lots of unused variables floating around in scripts to remove, and commented out unused code), and I might make a tutorial of it. Which would be a bit crappy if it involved shifting files around outside of GMS, and a ton of concessions to being able to do it in GML in the first place.

Including the text files it will read is just a matter of convenience for the user, as is being able to save the edited file back in. It's a very specific purpose, and one where the developer intends for the user to be an active participant - but as far as I can tell even giving system permissions doesn't allow the sand box to be this direct, and the sand box options in GMS 1.4 won't work (for me at least - though that could be down to being a crap programmer)

Assuming I actually need to do it this way, then I just wanted to make sure it wouldn't be an issue. Since it uses your code, and that would need crediting if I do make this tutorial.
 
Last edited:
S

Sam (Deleted User)

Guest
I knew you weren't attacking, I just wanted to make it clear you have nothing to worry about. :) credit me only if you want to, I dont require it.
 
Could you use this asset to produce with GM:S a program called a "game booter", a program that contains a listing of other GMS games that execute shell will run from the sub-directory of the game booter's sandbox ( As long as you have the physical RAM to run them )?

The game booter is used to prevent the user from running the other games outside of the game booter. The other games are password locked, and only the game booter writes a password via execute shell, that the other games will acknowledge as a password, run the game, then delete the password file using a destroy command sent to the OS via execute shell from the game, while the game is still running.

The destroy command is a small program I wrote in C, which simply, truncates the file to 0 bytes to prevent file recovery and then deletes it.
 
S

Sam (Deleted User)

Guest
Could you use this asset to produce with GM:S a program called a "game booter", a program that contains a listing of other GMS games that execute shell will run from the sub-directory of the game booter's sandbox ( As long as you have the physical RAM to run them )?

The game booter is used to prevent the user from running the other games outside of the game booter. The other games are password locked, and only the game booter writes a password via execute shell, that the other games will acknowledge as a password, run the game, then delete the password file using a destroy command sent to the OS via execute shell from the game, while the game is still running.

The destroy command is a small program I wrote in C, which simply, truncates the file to 0 bytes to prevent file recovery and then deletes it.
You can use command line parameters instead of a file, that would be more secure. Although it might be desirable to read program output in some cases, in which case the extension you would want would be Evaluate Shell.

You could read from the game's password parameter with the gml functions parameter_count() (only if you need it) and more importantly parameter_string(). On a Linux machine, you could even tell the application to boot on startup of your computer, and disallow any unrelated apps from running and hide the desktop. But this will take extra configuring on your end manually apart from the extension and GMS.
 
You can use command line parameters instead of a file, that would be more secure. Although it might be desirable to read program output in some cases, in which case the extension you would want would be Evaluate Shell.

You could read from the game's password parameter with the gml functions parameter_count() (only if you need it) and more importantly parameter_string(). On a Linux machine, you could even tell the application to boot on startup of your computer, and disallow any unrelated apps from running and hide the desktop. But this will take extra configuring on your end manually apart from the extension and GMS.
Im only using Windows.
 
S

Sam (Deleted User)

Guest
Sorry, I assumed you were using linux, because people do this kind of thing a lot with that OS. All of this should be possible on Windows too, you would just need to prevent the runner from closing on alt+f4 and write an extension that would keep the fullscreen windows that you want to always be visible to not lose focus on alt+tab. Which if you already know C, these things are reasonably easy to do. Another approach would be to not ever have a keyboard plugged in and use a gamepad instead.

Mac users need to use the open command on the app bundle, which doesn't allow for reading output, but at least you can still use parameter_string/count() this way. :)

Edit: to be more correct, Russell informed me of this:
to read the output you can use the same trick that we do which is to redirect the runner output to a file (use the -output and -debugoutput command line flags to the same file) and then read the file as it is written to.
So while you can't read output through a pipe directly, you can however send the output to a temporary text file and thus allowing you to read from that and then quickly delete the temp file for security purposes.
 
Last edited by a moderator:
I just wanted to make it clear you have nothing to worry about. :) credit me only if you want to, I dont require it.
Heh! You say that....After I set up this project GMS stopped working, and my PC then crashed. Which did worry me for a bit ;)

Not sure what the culprit was, but it was after an Nvidia update, and a Microsoft one too. Just amusing timing given our conversation. Whatever caused it, things are working now.

I will credit you, since anyone doing this will need to go to the marketplace (via your links above) to download execute_shell anyway. Though I've found that its encoded as gmez and won't work with 1.4, even though the marketplace says it's the right download. I had to go way back to an old project that was made when it was first acquired.
 
S

Sam (Deleted User)

Guest
Heh! You say that....After I set up this project GMS stopped working, and my PC then crashed. Which did worry me for a bit ;)

Not sure what the culprit was, but it was after an Nvidia update, and a Microsoft one too. Just amusing timing given our conversation. Whatever caused it, things are working now.

I will credit you, since anyone doing this will need to go to the marketplace (via your links above) to download execute_shell anyway. Though I've found that its encoded as gmez and won't work with 1.4, even though the marketplace says it's the right download. I had to go way back to an old project that was made when it was first acquired.
That doesn't sound good. lol I'm glad everything turned out to be ok. GMS2 has been reported to crash abruptly at random by several users, so it wouldn't surprise me if it's just a bug with GMS2.
 

Mr Giff

Member
Hey!

Not sure what I'm doing wrong here, I'm on Mac OS 10.14.6 Mojave and I can't seem to get this to work at all.

I run the example project with no alterations and... well, nothing happens; no Calculator no Text Edit file.

I saw your update on the marketplace talking about required code signing, so I created a code sign certificate using Keychain Certificate Assistant and used Terminal to sign the DyLib in the sand box extension folder using

Code:
codesign -s [certificate] [DyLib Path]

and that still didn't work :(

So I'm not sure what else I need to do, unless perhaps I'm signing it wrong.

I'd love to get this working! I used to use the windows version to launch Python Scripts :)
 

kroart

Member
Hi. Is it possible to run an another executable file build on GameMaker and pass command line arguments to that executable using this extension on Ubuntu? And of course read these arguments in game that was launched.
 
S

Sam (Deleted User)

Guest
yes, it is possible on all platforms this extension was built for.
 

kroart

Member
This extension has had over 600 downloads, so I assume a good number of users here have it. Is anyone ok with showing me your project and how it uses my extension? I'm just a little curious as to what it is being used for.
I'm using your extension for my project Battle for Hexagon:

I'm adding multiplayer right now so I'm implementing a game server that is running on some dedicated server. And in my case there is a lobby server app running on VDS. Users are connecting to lobby and waiting for a game session to be launched. When a new game is about to launch my lobby app launches an instance of gameplay server app with parameters such as a port number, players amount and others, and sending this port number to users. And those user connects to the gameplay server by this port.


The one thing I didn't get in your extension - the purpose of "wait" parameter. What it does?
 
S

Sam (Deleted User)

Guest
The one thing I didn't get in your extension - the purpose of "wait" parameter. What it does?
The wait parameter when set to true makes the calling application wait for the executed process to close before resuming code execution, otherwise code execution continues immediately.
 
S

Sam (Deleted User)

Guest
I was wondering if it would be possible to add support to run bat and ink files for windows, that would be great
As far as I know, that should already be possible; you'd do it the same as you would from command prompt.
 

Lioran

Member
As far as I know, that should already be possible; you'd do it the same as you would from command prompt.
Hm, I can't get anything to pop up for certain things, not sure if windows 7 matters or not but here is what I tested,
Code:
ExecuteShell("J:\Desktop\text.txt",0)
ExecuteShell("J:\Desktop\reg_gale.exe",0)
ExecuteShell("J:\Desktop\forest.png",0)
All Works

Code:
ExecuteShell("J:\Desktop\epic song.ogg",0)
ExecuteShell("J:\Desktop\epic song.mp3",0)
ExecuteShell("J:\Desktop\Obs Studio.lnk",0)
ExecuteShell("J:\Desktop\whats my local ip.bat",0)
None works
 
S

Sam (Deleted User)

Guest
If there is any path that has spaces you need to use quotes within the initial quotes otherwise the spaces will be treated as separate arguments.
 
S

Sam (Deleted User)

Guest
You can use this now, which has a lot more functionality: https://forum.yoyogames.com/index.p...formation-for-windows-macos-and-ubuntu.75321/

I intend to provide more/better documentation in the near future.

Basically, you may use process_execute(command) which is the same as execute_shell(command, wait=true)

And process_execute_async(command) which is the same but without blocking execution of the main thread, i.e. wait=false.

To return/evaluate output printed, you may use if (process_evaluate() != "") { output = process_evaluate(); } after executing a process, whether syncronously or asynchronously. (Step event if async)

To clear previously recorded output you may call process_clear_out(), this will make process_evaluate() return an empty string again after you have stored output to a variable.

To get the process id of the most previous process you executed that has died, you may use if (process_previous() != 0) { finished = true; process_clear_pid(); } procesa_clear_pid() resets the value returned by process_previous() to zero when you are done getting the exit state of a previously executed process id.
 
You can use this now, which has a lot more functionality: https://forum.yoyogames.com/index.p...formation-for-windows-macos-and-ubuntu.75321/

I intend to provide more/better documentation in the near future.

Basically, you may use process_execute(command) which is the same as execute_shell(command, wait=true)

And process_execute_async(command) which is the same but without blocking execution of the main thread, i.e. wait=false.

To return/evaluate output printed, you may use if (process_evaluate() != "") { output = process_evaluate(); } after executing a process, whether syncronously or asynchronously. (Step event if async)

To clear previously recorded output you may call process_clear_out(), this will make process_evaluate() return an empty string again after you have stored output to a variable.

To get the process id of the most previous process you executed that has died, you may use if (process_previous() != 0) { finished = true; process_clear_pid(); } procesa_clear_pid() resets the value returned by process_previous() to zero when you are done getting the exit state of a previously executed process id.
All right, I switched to this, but I'm having the problem that my launched process seems to hang indefinitely after a certain point. I'm using it to launch another GMS2 game, and shortly after it transitions scenes, it hangs indefinitely with no error. Launching the file without process_execute() or process_execute_async() does not reproduce the issue. Unsure whether it's hanging shortly after the scene transition, or shortly after it's done processing an async networking event -- the timing seems to vary.
 
S

Sam (Deleted User)

Guest
All right, I switched to this, but I'm having the problem that my launched process seems to hang indefinitely after a certain point. I'm using it to launch another GMS2 game, and shortly after it transitions scenes, it hangs indefinitely with no error. Launching the file without process_execute() or process_execute_async() does not reproduce the issue. Unsure whether it's hanging shortly after the scene transition, or shortly after it's done processing an async networking event -- the timing seems to vary.
Try running this project and loading a gamemaker game file and let me know if the problem happens with your game and if it does than we can debug the issue further: https://drive.google.com/file/d/1l7a5iIoaNfK5B2yWIwDvgxz3YnR-NPx_/view?usp=sharing

The source code for process_execute is nearly identical to execute_shell so im very puzzled by what might be making the difference.
 
Yep, I can confirm that the same issue occurs when opening the .win file with your app.

I never got to use execute_shell so I couldn't say that it wouldn't happen with that too.
 
S

Sam (Deleted User)

Guest
Yep, I can confirm that the same issue occurs when opening the .win file with your app.

I never got to use execute_shell so I couldn't say that it wouldn't happen with that too.
do you have minimum steps to reproduce this issue?

what runtime are you using?

Edit: I'll upload the ExecuteShell extension to see if that has the same issue to help narrow things more.

Alright - here's ExecuteShell for now, and if it has the same issue I'll remove it to save space:


We can talk about this on discord or pm's if you get tired of refreshing the page, just let me know Samuel Venable#5465

I've had this problem, myself, but can only reproduce it with a project i made which uses the extension to embed mpv media player with the --wid flag to embed video playback. I can't seem to get it to happen with anything else, even full game projects.
 
Last edited by a moderator:
do you have minimum steps to reproduce this issue?

what runtime are you using?

Edit: I'll upload the ExecuteShell extension to see if that has the same issue to help narrow things more.
I've been trying to figure out what's triggering it. It only seems to happen on a room transition, but not reliably, and all the room transitions in my game are triggered by networking messages, so it's unclear. It's possible that it's hanging on the end of the Asynchronous - Networking event.
 
S

Sam (Deleted User)

Guest
I've been trying to figure out what's triggering it. It only seems to happen on a room transition, but not reliably, and all the room transitions in my game are triggered by networking messages, so it's unclear. It's possible that it's hanging on the end of the Asynchronous - Networking event.
If you could make a project which removes all your sacred assets and code, of which is too holy for mortal eyes such as mine to see, and you can get it to happen with a minimal reproducible example, it just depends on how bad you need this functionality how much you are willing to put time into trying to figure it out. I'm sorry this has become the pain it has, but if you try to do this it will go a long way, just keep at it and I believe we'll make something of it.


Edit: just saw your response
(And yes I'll make it available again in the meantime)
 
In the meantime, if you get restless, the basic logic is this:
  1. Player hits button on screen 1
  2. Game connects to server (a simple Python select script) and sends it a packet
  3. Script sends an identical packet back
  4. This packet transitions the player to a new room
  5. New room transition triggers another packet to the server
  6. Server responds with a bunch of packets containing information about the new room (objects & such inside it)
Step #6 seems to be where it hangs, and I assume it has something to do with the volume of the packets based on the circumstances. Transitioning to a new room with only 1 small packet works fine, but whenever there's a large networking dump it freezes.
 
S

Sam (Deleted User)

Guest
Oh yes -- and thank you very much for filling in this gaping hole in GMS2's functionality. Wouldn't be worth ten bucks, let alone a hundred, without people like you doing Yoyo's work for them. :)
I've concluded what caused the problem I believe, although I think it might need a bit more testing to make sure - if the output read by process_evaluate exceeds 4096 characters exactly, including the null terminator, it will then freeze. Don't ask me why it does this, because I am still trying to figure that much out lol I dont see any mention of a buffer limit in microsoft's docs on ReadFile. Even after making the buffer resize dynamically so it could fit past that amount, it still has the problem once that many characters have printed.

Edit: I fixed the problem. It no longer hangs.
 
Last edited by a moderator:
S

Sam (Deleted User)

Guest
GMS2.3 - Version 10.0.0. Published June 17, 2021
  • Combined the feature set of this extension with that of my former Evaluate Shell and Process Info extensions, thus, this is the only extension still available among the three. Note functions have been renamed and how to use them has changed as well as the argument count of various functions has also changed.
 
S

Sam (Deleted User)

Guest
Updated the description to have frequently asked questions and answers.
 

mX273

Member
@Samuel Venable : The Execute Shell extension crashes:

############################################################################################
ERROR in
action number 1
of Draw Event
for object obj_example:

Variable obj_example.procId(100007, -2147483648) not set before reading it.
at gml_Object_obj_example_Draw_64 (line 3) - if (!ProcIdExists(procId) || exe == "") e = "<undefined>"; else e = exe;
############################################################################################
gml_Object_obj_example_Draw_64 (line 3)



at room start event "size" is always = 0, thats why procId is not set.

Code:
widget_set_owner(string(int64(window_handle())));

process = 0; pause = false; i = 0; list = ProcListCreate();

size = ProcessIdLength(list);

if (size) {

  if (i < size) {

    procId = ProcessId(list, i);

    info = ProcInfoFromProcId(procId);

    ppid = ParentProcessId(info);

    exe = ExecutableImageFilePath(info);

    cwd = CurrentWorkingDirectory(info);

    ii = 0; cmd = "";

    cmdsize = CommandLineLength(info);

    if (cmdsize) {

      for (ii = 0; ii < cmdsize; ii += 1) {

        tmp = string_replace_all(CommandLine(info, ii), @'\', @'\\');

        cmd += @'"' + string_replace_all(tmp, @'"', @'\"') + @'" ';

      }

      cmd = string_copy(cmd, 0, string_length(cmd) - 1);

    }

    FreeProcInfo(info);

  }

  FreeProcList(list);

  alarm[0] = 10;

  alarm[1] = 10;

}
what can i do?
 
S

Sam (Deleted User)

Guest
The Execute Shell extension crashes:

############################################################################################
ERROR in
action number 1
of Draw Event
for object obj_example:

Variable obj_example.procId(100007, -2147483648) not set before reading it.
at gml_Object_obj_example_Draw_64 (line 3) - if (!ProcIdExists(procId) || exe == "") e = "<undefined>"; else e = exe;
############################################################################################
gml_Object_obj_example_Draw_64 (line 3)



at room start event "size" is always = 0, thats why procId is not set.

Code:
widget_set_owner(string(int64(window_handle())));

process = 0; pause = false; i = 0; list = ProcListCreate();

size = ProcessIdLength(list);

if (size) {

  if (i < size) {

    procId = ProcessId(list, i);

    info = ProcInfoFromProcId(procId);

    ppid = ParentProcessId(info);

    exe = ExecutableImageFilePath(info);

    cwd = CurrentWorkingDirectory(info);

    ii = 0; cmd = "";

    cmdsize = CommandLineLength(info);

    if (cmdsize) {

      for (ii = 0; ii < cmdsize; ii += 1) {

        tmp = string_replace_all(CommandLine(info, ii), @'\', @'\\');

        cmd += @'"' + string_replace_all(tmp, @'"', @'\"') + @'" ';

      }

      cmd = string_copy(cmd, 0, string_length(cmd) - 1);

    }

    FreeProcInfo(info);

  }

  FreeProcList(list);

  alarm[0] = 10;

  alarm[1] = 10;

}
what can i do?
Looking at the code you provided it would appear you are using an outdated version of the extension.

I recommend downloading the latest version from itch.io: GameMaker Studio 2.3.1+ Extension Collection by Samuel Venable (itch.io)

Also are you using the GMS beta with Feather enabled? That probably explains why that is happening. Code initialization is more strict since the introduction of Feather and so I will need to update my project to work with it. Also, what platform are you testing on?

Edit: I see, size always equaling zero is not expected behavior. I would strongly recommend updating as you are correct that is why it isn't being initialized.

If that doesn't fix it I am happy to discuss this via private messages or add me on discord as i prefer realtime chat and not having to press refresh repeatedly.

Samuel Venable#5465 is my discord
 

Kenshiro

Member
Hello Samuel, how are you doing?
Quick question: is this working on Linux? I'm having trouble with http_get (as noted here), and I was wondering if I could just run curl from the terminal to get a similar result. hehe

Thanks in advance.
 
Top