Jump to content

Expanding the capabilities of ACS scripting with python


Recommended Posts

Hi, I'm new to Doom modding, but I think I found a way to expand the things you can do with ACS scripts using python.

Perhaps this was done before, but to my knowledge this is the first time someone has done this.

This paragraph is about how I got the Idea to do this so if you just want the technical stuff you can skip it. I got into Doom modding through the myhouse analysis videos by DavidXNewton. It was really cool how myhouse used the relatively simple ACS scripting language to great effect. I wanted to make some more complex scripts that are beyond the capabilites of ACS so I started with Writing files (haven't figured out reading yet but I'm working on it).

To my knowledge ACS scripting does not support writing files to the users computer at all. So I had to get ACS to tell a python script to do so. ACS can't do this, however, there is one way I can get ACS to shout into the void, if you will, so that my Python script can listen for the right things and then execute its functions. It turns out I can make ACS log some text and using a commandline argument to write this log to a text file. My Python script then reads this file and when certain text is written there it executes a certain function. To run both Doom with the log argument and my Python script at the same time I used a batch file.

Here is a little demo that uses this algorithm to exteract a text file from a zip when the player picks up a chaingun:

ACS:

script 1 (void) {
    Log(s:"Call1");
}

Logic.py:

import zipfile

#Extract file from zip
def Pull(target):
    if getattr(Pull, 'has_run', False):
        return

    Pull.has_run = True
    with zipfile.ZipFile('Data.zip') as zf:
        zf.extract('Data/'+target, 'Files')
    print("Pulled: "+target)

def Call1():
    #do stuff
    Pull("Text.txt")

print("started")
while True:
    with open('log.txt') as log:
        if 'Call1' in log.read():
            print("Here")
            Pull("Text.txt")
print("done")

Batch:

start gzdoom.exe +logfile log.txt -file Bunker.wad
start py Logic.py



Here is a video demonstration: https://www.youtube.com/watch?v=-7AghV91gJk

Any questions, suggestions or criticism is very welcome.

(I posted this on the ZDoom forum under a different name so if you see that don't be confused)

Share this post


Link to post
1 hour ago, Joshua1974 said:

To my knowledge ACS scripting does not support writing files to the users computer at all.

 

Thank god it doesn't! What do you need it for? What's your goal?

Share this post


Link to post
29 minutes ago, boris said:

 

Thank god it doesn't! What do you need it for? What's your goal?

This is just an example. I really just wanted to make ACS call a python script, I don't really have a goal it just seems interesting.

Share this post


Link to post

I shudder to imagine the inhuman arts of terry wad-ing if you somehow got this to work. Maybe, like the makers of jurrassic park, you should stop and think wheter you should rather than if you could.

Share this post


Link to post
11 minutes ago, JacketEternal said:

I shudder to imagine the inhuman arts of terry wad-ing if you somehow got this to work. Maybe, like the makers of jurrassic park, you should stop and think wheter you should rather than if you could.

Well it already works so I guess it's to late for that. Although it is very obvious if a Wad uses this because you have to start it through a batch file or start the script yourself so it's not like this can be smuggled in with any wad.

Share this post


Link to post
1 hour ago, Joshua1974 said:

Well it already works so I guess it's to late for that. Although it is very obvious if a Wad uses this because you have to start it through a batch file or start the script yourself so it's not like this can be smuggled in with any wad.

But what if you could...

Then again, It may be a good thing culturally. I mean, can you imagine a world without terry wads?

 

In all seriousness, this is actually really cool and could be used for IMSCARED level fuckery. If myhouse popped off I'd love to see what you could do with this, just be careful.

Share this post


Link to post
3 hours ago, boris said:

 

Thank god it doesn't! What do you need it for? What's your goal?

 

(SOMEMONG texture flashing)

Share this post


Link to post

MyHouse.pk3 doesn't use ACS. There's a lot more to GZ modding than ACS.

Also I don't think having a second program running in the background is safe for a game.

Share this post


Link to post

Just to clarify, this looks very much like the python code is actively looking for an event logged in the log file rather than ACS calling some kind of python API. So it's more python reacting to what it finds in the doom log file than ACS actively calling python.

 

However as a PoC for extending what can be triggered by Doom it's pretty cool. The same methodology could of course be applied to any language that can parse the log file.

 

@Joshua1974 have you seen the Doom python libs? IIRC there is at least one that can manipulate maps? Might be mileage in investigating this - Maybe triggering changes? can it alter maps at runtime??? (No idea if this would even work, though it might be fun to try). Maybe a map that can make another map? There seems to be an Oblige python wrapper too. How about calling this?..

 

>>> EDIT <<<

FWIW, there might be mileage in using something from here (python implementations of the tail utility), which might be more efficient if the logfile gets very large - which it might if you want to look for many, and repeated, events. Your current loop looks like it reads the entire log each time, and will read just the first occurrence of 'Call1'. I don't know what you want, but you may want to react to 'Call1', then 'Call2', and them maybe 'Call1' again. Reading just the last logged line would allow this and effectively result in an event queue that allowed repeated, and non-sequential, triggers. Just a thought.

Edited by smeghammer

Share this post


Link to post

Creative, but something I would ultimately never allow to happen on my box.

Share this post


Link to post
On 6/4/2023 at 3:09 PM, Ausara said:

MyHouse.pk3 doesn't use ACS. There's a lot more to GZ modding than ACS.

Also I don't think having a second program running in the background is safe for a game.

 

pardon?

Share this post


Link to post

Make it send Fortnite porn to my Mum from my email address whenever a Revenant fireball deals 80 damage.

Share this post


Link to post
On 6/4/2023 at 11:44 AM, Joshua1974 said:

It turns out I can make ACS log some text and using a commandline argument to write this log to a text file. My Python script then reads this file and when certain text is written there it executes a certain function.

I'm a little concerned about this. How do you handle the case where your Python script tries to read the log file at the same time it is being written to by the game (or the game tries to write to the file while your Python script is reading it)? I'm assuming nothing catastrophic happens, but you should probably figure out some way of dealing with this. The way you have it now, and depending on the order things happen, your script might throw an exception and exit while the game keeps running, or the game may crash. The former is easily preventable by catching the exception (which I presume would be OSError, maybe IOError) in the Python script, but I'm not sure the latter is preventable at all.

 

Anyway I got a little to excited by this title lol. I thought you managed to get Python to do stuff within the game, sort of like a replacement to ACS, so you could write Python script that raises a floor when the player takes some action in-game or something. Eevee (from fuzzy notepad) managed to to something like this with Lua. Although it did require to mess with GZDoom's source code.

 

On 6/4/2023 at 12:56 PM, boris said:

Thank god it doesn't! What do you need it for? What's your goal?

Every game engine out there gives game developers the ability to write files with arbitrary data to the user's machine (this is required to have the ability to create save files, for instance). Why are you reacting like this is such a horrible thing? It certainly has the potential to be harmful, but that's true with all of coding.

Edited by QuotePilgrim

Share this post


Link to post
3 minutes ago, QuotePilgrim said:

Every game engine out there gives game developers the ability to write files with arbitrary data to the user's machine (this is required to have the ability to create save files, for instance). Why are you reacting like this is such a horrible thing? It certainly has the potential to be harmful, but that's true with all of coding.

 

That's really not the same thing. This is allowing an independent python interpreter to run, likely with permissions of a scope larger than that of the game engine itself.

Share this post


Link to post
4 minutes ago, dasho said:

That's really not the same thing. This is allowing an independent python interpreter to run, likely with permissions of a scope larger than that of the game engine itself.

Not really. You just expect GZDoom not to be able to do stuff like that because, well, it isn't really able to. But any game written in Unity or Unreal Engine or GameMaker or Godot, etc. might just delete all of your files or something if the developer wanted to. You trust the games you play not to do that, but there's nothing preventing them from doing it.

Edited by QuotePilgrim

Share this post


Link to post
Just now, QuotePilgrim said:

Not really. You just expect GZDoom not to be able to do stuff like that because, well, it isn't really able to.

 

Exactly. Now you're adding a standalone python interpreter to the mix.

Share this post


Link to post

And how is it any different from playing a game written in Unity, which gives the developer the power to make arbitrary file operations?

Edited by QuotePilgrim

Share this post


Link to post
3 minutes ago, QuotePilgrim said:

And how is it any different from playing a game written in Unity, which gives the developer the power to make arbitrary file operations?

 

Are you asking how running an independent language interpreter alongside a game that can perform arbitrary actions unrelated to the game based on certain inputs is different than a game engine being able to perform file operations?

Edited by dasho

Share this post


Link to post
1 minute ago, dasho said:

Are you asking how running an independent language interpreter alongside a game that can perform arbitrary actions unrelated to the game based on certain inputs is different than a game engine being able to perform file operations?

 

It's not unrelated to the game if the Python code is part of the game, shipped with the game. But whatever.

 

What I'm saying is, anything that the Python script might be able to do, is something that any game written in any game engine could also do. And though I said that GZDoom can't do that, that's only true if you're playing DOOM maps/mods, a standalone game made with GZDoom as its engine, which ships with its own copy of GZDoom, can also have the ability to run arbitrary code. You simply have to trust the developers not to have put any malicious code in it.

 

So, a GZDoom game that runs some Python code could delete all your files. A game written in Unity could also delete all your files. Why only the former worries you, I guess, is what I'm asking.

Share this post


Link to post
Just now, QuotePilgrim said:

 

It's not unrelated to the game if the Python code is part of the game, shipped with the game. But whatever.

 

What I'm saying is, anything that the Python script might be able to do, is something that any game written in any game engine could also do. And though I said that GZDoom can't do that, that's only true if you're playing DOOM maps/mods, a standalone game made with GZDoom as its engine, which ships with its own copy of GZDoom, can also have the ability to run arbitrary code. You simply have to trust the developers not to have put any malicious code in it.

 

So, a GZDoom game that runs some Python code could delete all your files. A game written in Unity could also delete all your files. Why only the former worries you, I guess, is what I'm asking.

You are correct, although certain operating systems require the program to be run in administrator mode to alter files that are not in the games directory. I believe Windows is one of them and I think 90% of GZDoom players are on Windows.

Share this post


Link to post

 

3 minutes ago, QuotePilgrim said:

 

It's not unrelated to the game if the Python code is part of the game, shipped with the game. But whatever.

 

What I'm saying is, anything that the Python script might be able to do, is something that any game written in any game engine could also do. And though I said that GZDoom can't do that, that's only true if you're playing DOOM maps/mods, a standalone game made with GZDoom as its engine, which ships with its own copy of GZDoom, can also have the ability to run arbitrary code. You simply have to trust the developers not to have put any malicious code in it.

 

So, a GZDoom game that runs some Python code could delete all your files. A game written in Unity could also delete all your files. Why only the former worries you, I guess, is what I'm asking.

 

I don't know how to put this in a way you'll understand. Would you not be more suspicious of this scenario:


"Hey, just run this PowerShell script and leave the terminal open while you run your game to do cool stuff"

 

Versus:


"Hey, our game engine is designed around being extensible, so please make sure you trust plugin authors but otherwise have fun"

Edited by dasho

Share this post


Link to post
34 minutes ago, QuotePilgrim said:

Anyway I got a little to excited by this title lol. I thought you managed to get Python to do stuff within the game, sort of like a replacement to ACS, so you could write Python script that raises a floor when the player takes some action in-game or something. Eevee (from fuzzy notepad) managed to to something like this with Lua. Although it did require to mess with GZDoom's source code.

This would be the optimal solution. I started developing this a couple days before posting it so it is still in it's infancy. I am looking into many things brought up in this thread and whatever else I can find. We'll see how far this can go.

Share this post


Link to post
20 minutes ago, dasho said:

I don't know how to put this in a way you'll understand. Would you not be more suspicious of this scenario:


"Hey, just run this PowerShell script and leave the terminal open while you run your game to do cool stuff"

 

Versus:


"Hey, our game engine is designed around being extensible, so please make sure you trust plugin authors but otherwise have fun"

 

See, your first example is just not how it would be done though. The game would probably just come with a script, or executable, or a shortcut file, whatever, that you double-click and it runs both the GZDoom executable and the Python script at the same time. It would be the same experience as with any other game, you double click the thing and it runs. If there was a game on Steam that was made with GZDoom and also ran Python code in the background, you wouldn't necessarily even know that's what it's doing. You would launch the game from Steam just like any other game, and not even think about it.

Would I be suspicious of scenario like the one proposed in your first example, however? Not necessarily, I would probably try to figure out why the game needs to do that, and once I knew the details, I would probably just play the game anyway. Doki Doki Literature Club! is one of my favorite games, and it's written in Ren'Py, which is a Python-based engine that, if you know well enough, can be made to run any arbitrary Python code; that was never something I worried about. Some of what that game even does is create and delete files in the user's computer. It only does that within the game's own directory, but not because there was anything preventing it from messing with files elsewhere. It was rather because the developer was making a game and nothing else, so of course they wouldn't do anything malicious even if their tools allowed them to.

Edited by QuotePilgrim

Share this post


Link to post
26 minutes ago, Joshua1974 said:

You are correct, although certain operating systems require the program to be run in administrator mode to alter files that are not in the games directory. I believe Windows is one of them and I think 90% of GZDoom players are on Windows. 

You do need administrator privileges to write and read from certain places, yes, but I'm pretty sure any program on Windows has access to all of the user files not owned by the system administrator. So anything inside in Desktop, Documents, Music, Downloads, etc. is accessible and can be read and written to without any special privileges required. On Linux, which is what I use, anything in the user's home directory is accessible by programs without root privilege.

Share this post


Link to post
13 minutes ago, QuotePilgrim said:

See, your first example is just not how it would be done though.

 

My first example is fundamentally what the OP is about. Whether or not it's done with a batch file is irrelevant. You keep talking about trust, and that is also what I am talking about yet you treat it differently. I would inherently place more trust in a game that has been published on Steam that is also designed around, and uses, an interpreted language (although I would not necessarily extend this trust to community addons) versus something like the original scenario which asks that the user also install and run an interpreter for a language that the game doesn't use and wasn't designed for in addition to having things like the ability to invoke system calls, host a web server, etc, out of the box.

Share this post


Link to post
48 minutes ago, dasho said:

My first example is fundamentally what the OP is about. Whether or not it's done with a batch file is irrelevant. You keep talking about trust, and that is also what I am talking about yet you treat it differently. I would inherently place more trust in a game that has been published on Steam that is also designed around, and uses, an interpreted language (although I would not necessarily extend this trust to community addons) versus something like the original scenario which asks that the user also install and run an interpreter for a language that the game doesn't use and wasn't designed for in addition to having things like the ability to invoke system calls, host a web server, etc, out of the box.

I think that's the heart of the issue, I just trust games to be games. I think it's way too unlikely for someone who just wants to make a cool game/mod/level for people to enjoy to have any motivation to do anything malicious for that to be something to worry about. If the game looks interesting, I'll play it even if I have to do something slightly out of the ordinary to get it to run, provided the developer has a reasonable explanation as to why such steps must be taken.

 

One of the things MyHouse.wad did was give people a QR Code that could easily have pointed to a malicious website and, well, it didn't. Why? Because it was made by someone who wanted to create an interesting experience for the community to enjoy, and I will always trust people like that not to do anything malicious, there simply isn't any motivation to.

 

As I said, any game you play at any point could be malicious. You simply have to trust them not to.

 

EDIT: I also realized the point of my original comment had nothing to do with the Python interpreter thing, I just got sidetracked. The point was about ACS not being able to write files to the user's machine. I don't think if ACS had a function that can write a file to the user's machine that would be such a terrible thing as that's something any game, or any program at all, is capable of doing. People are overreacting to the idea of that being a possibility, while I think it would just be a good tool for developers.

Edited by QuotePilgrim

Share this post


Link to post

I was looking into this, and you may be able to achieve the same thing through CVARs without the need to enable logging. You can create a custom CVAR with the CVARINFO lump, then use an ACS function such as SetCVar or SetUserCVar to set its value to whatever you want, then use ConsoleCommand function to run the archivecvar ZDoom console command to save it to a configuration file. Python can then read the CVar's value from the configuration file (finding where it is would be a little tricky, as you may have to know where the user's GZDoom binary is, so this approach would be better suited for a project that is shipped with its own copy of GZDoom alongside a Python interpreter within the same directory structure, so you always know where the file is relative to the location of your script).

 

You may also use "GetCVar" or "GetUserCVar" to read the value of the CVAR at a later point. Which may also mean you can change the value of the CVAR with Python by modifying the config file, and have the game react to the change made by the script. That is assuming you can get the CVAR to update in-game while the game is running after the file is changed, which may not be possible. If either any of the "GetCVar" ACS functions or the "get" console command reads from that file when run, it should work, otherwise you're out of luck.

 

I may try to get this all to work at some point.

Edited by QuotePilgrim

Share this post


Link to post
11 minutes ago, QuotePilgrim said:

I was looking into this, and you may be able to achieve the same thing through CVARs without the need to enable logging. You can create a custom CVAR with the CVARINFO lump, then use an ACS function such as SetCVar or SetUserCVar to set its value to whatever you want, then use ConseoleCommand function to run the archivecvar ZDoom console command to save it to a configuration file. Python can then read the CVar's value from the configuration file (finding where it is would be a little tricky, as you may have to know where the user's GZDoom binary is, so this approach would be better suited for a project that is shipped with its own copy of GZDoom alongside a Python interpreter within the same directory structure, so you always know where the file is relative to the location of your script).

 

You may also use "GetCVar" or "GetUserCVar" to read the value of the CVAR at a later point. Which may also mean you can change the value of the CVAR with Python by modifying the config file, and have the game react to the change made by the script. That is assuming you can get the CVAR to update in-game while the game is running after the file is changed, which may not be possible. If either any of the "GetCVar" ACS functions or the "get" console command reads from that file when run, it should work, otherwise you're out of luck.

 

I may try to get this all to work at some point.

That's amazing thanks!

Share this post


Link to post
On 6/4/2023 at 4:06 PM, smeghammer said:

have you seen the Doom python libs? IIRC there is at least one that can manipulate maps? Might be mileage in investigating this - Maybe triggering changes? can it alter maps at runtime??? (No idea if this would even work, though it might be fun to try). Maybe a map that can make another map? There seems to be an Oblige python wrapper too. How about calling this?..

i'd imagine that they wouldn't be able to, operating systems will usually lock access to files in use. maybe i'm wrong though. @Graf Zahl what do you think about this?

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...