-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Awesome #1
Comments
Thanks! If you're not really needing the coroutines then you might luck out getting the DLR's insides to just yield every once in awhile. You'd basically just inject some instructions that have it pause for air. The reason I didn't try that myself was because my real killer was scripts that required other subsystems across multiple frames to produce some result to continue. If I wrote a regular IronPython script, that kind of thing would hang since the interpreter runs in the same thread as the game logic. I caused some bad explosions when I tried to use async-await in that kind of environment, and it looked like I was eating up a lot of resources if I wanted to create contexts for every simultaneous script I wanted to run. I did take other parts of IronPython for granted though. Dropping .NET objects into the interpreter is pretty trivial in IronPython, but it turns out to be a huge chore in reflection. I can't really prove how well this whole thing works in Unity until I'm interacting with some of the stuff in there. Sure, I got a virtual console to run arbitrary code, but that code was pure Python. You might notice the most recent commits have had to do with operators. I'm at the point of trying to integrate events so I needed += and -=. Finally, one of the perks of purely interpreting everything is that the script state can theoretically be serialized. I don't think I could do that with IronPython, although IronPython would definitely run scripts much faster. |
I'm just having a good read through the code at the moment, looks like it will drop in to what i'm doing very easily : video here : www.dropbox.com/s/pph7j5hwmxctwa6/2020-02-09%2017-05-24.flv?dl=0 (warning there is a flashing light in the video). The terminal in the video is a fully emulated ECMA48 device with stdin/out support - i'll have a go later to see if i can get this integrated instead of iron python, my tests are very similar to your GuiDemo. |
That's giving me some Minecraft + ComputerCraft vibes right there! |
Haha very true, i'm terrible at Blender so MagicaVoxel is my goto modelling tool for prototyping things. I hadn't heard of ComputerCraft before - looks interesting. https://www.dropbox.com/s/lmkzw9zmucd6s2w/success.png?dl=0 |
Implementing PyFile would be a pretty big undertaking. I haven't looked at the pedantic details of file I/O but I can think of some problems:
|
Yeah it's probably not the easiest one to go for, but it's pretty much the core of what i'm doing - i have something similar up and running in the my engine already - there is a virtual file system, and i hijack 'open' with some hacking of the IronPython source to return a custom Stream rather than a file stream so accessing real files isn't possible - i ripped out a lot of stuff from IronPython for exactly the reasons you mention so players couldn't escape the sandbox - it's an issue i'm aware of. I think initially to get things working a simple open method via the Builtin interface would work. I use TDD for pretty much everything these days so Unit Tests are a given - have a look at my Twitter @mutatedmedia for some equally deranged projects ;) I'm up to the task.. I'll get something basic up and running and create a PR just for discussing it until you're happy. |
I'm back (whether or not that's a good thing :)...unfortunately real life got in the way there for a bit, but we're back on track and lockdown means i've had some time to spend on this again. Looks like you've been very busy. I got rid of my initial fork and created an new clean one just to start over really, and wow everything seems to be working, i fleshed out my file io classes a bit to test out the inheritance and all seems fine - and a simple program
runs 👍 . So now i need to fully implement the python spec for file io stuff.
I tried implementing this as
and modifying Injector.cs
and handling the default in the PyClass
which works but feels a bit awkward but since we can't do
I don't really see any other option ? |
I didn't do anything with kwargs yet. I ... just never got to it. It is not
possible to use PyInteger.Create? If it has to do with PyNetConverter then
I think we can just expand that to pass through PyObjects in general. I'm
saying that without putting too much thought into it.
…On Fri, May 15, 2020 at 4:10 AM bamyazi ***@***.***> wrote:
I'm back (whether or not that's a good thing :)...unfortunately real life
got in the way there for a bit, but we're back on track and lockdown means
i've had some time to spend on this again. Looks like you've been very
busy. I got rid of my initial fork and created an new clean one just to
start over really, and wow everything seems to be working, i fleshed out my
file io classes a bit to test out the inheritance and all seems fine - and
a simple program
file = open("test.dat","r")
line = file.readline(1)
print(line)
file.close()
runs 👍 . So now i need to fully implement the python spec for file io
stuff.
So i have a couple of question (I know it didn't take me long - sorry, at
least it's not problems this time). First up when creating new classes i
didn't see a way to specify optional parameters eg. the TextIoWrapper spec
defines readline
readline(limit=-1)
Read and return one line from the stream. If limit is specified, at most
limit bytes will be read.
I tried implementing this as
[ClassMember]
public static PyString readline(PyIOBase self, PyInteger size = null)
and modifying Injector.cs
if (in_param_i >= args.Length)
{
// We have a missing parameter
if (paramInfo.HasDefaultValue)
{
// But it has a default
outParams[out_param_i] = paramInfo.DefaultValue==null ? null : PyNetConverter.Convert(paramInfo.DefaultValue, paramInfo.ParameterType);
}
else
{
throw new Exception("Missing parameter.");
}
}
else
{
outParams[out_param_i] = PyNetConverter.Convert(args[in_param_i], paramInfo.ParameterType);
}
and handling the default in the PyClass
[ClassMember]
public static PyString readline(PyIOBase self, PyInteger size = null)
{
if (size==null) size = PyInteger.Create(-1);
return PyString.Create(self.Readline(size.number));
}
which works but feels a bit awkward but since we can't do
public static PyString readline(PyIOBase self, PyInteger size =
PyInteger.Create(-1))
I don't really see any other option ?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#1 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AARP6TEMIYSDRPMV4IZJNULRRUBHLANCNFSM4KSRXJCQ>
.
|
I was thinking about your injector modifications. I probably just screwed that all up for you with what I just did with extension methods. I was about to consider refactoring that code given what I know now between normal arguments, generic parameters, and the 'this class' that shows up for extension methods. Now I realize I should hold off until we have an understand of optional arguments. Throw injected arguments into this and you might say there's a problem! Consider that code to have become rather brittle with all the off-by-one shenanigans that can erupt from the different permutations of methods. I really suffered to support, say, generic extension methods that have arguments! I have a bunch of tests added in EmbeddingTests for all of that. I'm also wondering if I want to isolate all the code that's matching and binding everything so I can test it without doing code tests. |
Not a problem, i'm currently really just working through the code again to get familiar with it all - and testing out things that i need to get working to have file/stream support - i'll just update as you go along and fit in with any changes. I'm currently trying to wrap by head around the best way to handle the blocking io and understanding the IScheduledAwaiter/FutureAwaiter stuff |
I see the problem with the readline call; optionals need to be compile-time
constants, and the PyInteger is an instance.
I think I have at least up to your current level of support for optionals
included in what I'm about to push to master. I think I'd need kwarg
support to do any better. I'm afraid to jump into it because I think there
are going to be some dragons there.
…On Tue, May 19, 2020 at 1:17 AM bamyazi ***@***.***> wrote:
Not a problem, i'm currently really just working through the code again to
get familiar with it all - and testing out things that i need to get
working to have file/stream support - i'll just update as you go along and
fit in with any changes. I'm currently trying to wrap by head around the
best way to handle the blocking io and understanding the
IScheduledAwaiter/FutureAwaiter stuff
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#1 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AARP6TA6QOMR5GTD3NPSW6DRSIP6RANCNFSM4KSRXJCQ>
.
|
The null default is basically what I do when I'm dealing with defaults in
Python already that would be derived from other arguments. So it's kind of
crappy but not without precedent. I have a pile of TODOs for kwargs now but
I'm not about to pounce on them. At this point, I'm trying to embed it more
and more into my game project and I've been tightening all the nuts and
bolts that keep falling off from that. The next big thing I'd probably have
to tackle is for-loops. That includes generators, which means dealing with
iterating, which means dealing with .NET iteration at the same time! Pah!
I'll say you don't want to get into implementing kwargs yourself unless you
want to really get into the woods with this. I do not think I am even
parsing all the syntax for them. In particular, having *args and **kwargs
in a declaration will probably not even parse.
…On Sun, May 24, 2020 at 11:55 PM Adam Preble ***@***.***> wrote:
I see the problem with the readline call; optionals need to be
compile-time constants, and the PyInteger is an instance.
I think I have at least up to your current level of support for optionals
included in what I'm about to push to master. I think I'd need kwarg
support to do any better. I'm afraid to jump into it because I think there
are going to be some dragons there.
On Tue, May 19, 2020 at 1:17 AM bamyazi ***@***.***> wrote:
> Not a problem, i'm currently really just working through the code again
> to get familiar with it all - and testing out things that i need to get
> working to have file/stream support - i'll just update as you go along and
> fit in with any changes. I'm currently trying to wrap by head around the
> best way to handle the blocking io and understanding the
> IScheduledAwaiter/FutureAwaiter stuff
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <#1 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AARP6TA6QOMR5GTD3NPSW6DRSIP6RANCNFSM4KSRXJCQ>
> .
>
|
Here's a current, real-life example of the future stuff. The FutureAwaiter wraps a lot of the sausagemaking the scheduler does into something you'd expect from a Future design pattern. You can see an example of that in concurrent.futures in Python, and other languages have a version of it. It's normally a way to have one thread block until a result is available from elsewhere. I have a special version of it because we're not multithreading. You can think of it as asynchronously getting an IOU from a function call that you'd normally block on when you absolutely, positively need the value to continue doing whatever you have to do. It's been usurped here to have the scheduler take the IOU and ignore the script until it has been fulfilled. That's what puts it in the blocked state. Here's a script. The prompt() is using it:
Here's the implementation of prompt in C#:
What you need to do is figure out internally how you're going to asynchronously fulfill the result in your own runtime. In my case, it's an event that's fired by my dialog subsystem when a choice is made. When that fires, I call SetResult() on the future. That kicks it out of the blocked queue and puts it into the active queue. To complete the contract with the scheduler, you have to do the stuff at the end. That tells the scheduler whatever invoked this--which is why it needs the context, by the way--need to be put in the blocked queue until the given awaiter comes in. Then you await on it to suspend execution. The scheduler will be waiting for the continuation created by the .NET runtime and will file it with the blocked task. You finally return the future. Cloaca will know to open it up and extract the value out to put it on the data stack when things resume. It's really simple! (no it's not). =D |
This is awesome thanks very much for posting, exactly the guide I
need...I'll give it a go and try and get a blocking file read working.
…On Thu, 28 May 2020, 07:09 rockobonaparte, ***@***.***> wrote:
Here's a current, real-life example of the future stuff. The FutureAwaiter
wraps a lot of the sausagemaking the scheduler does into something you'd
expect from a Future design pattern. You can see an example of that in
concurrent.futures in Python, and other languages have a version of it.
It's normally a way to have one thread block until a result is available
from elsewhere. I have a special version of it because we're not
multithreading.
You can think of it as asynchronously getting an IOU from a function call
that you'd normally block on when you absolutely, positively need the value
to continue doing whatever you have to do. It's been usurped here to have
the scheduler take the IOU and ignore the script until it has been
fulfilled. That's what puts it in the blocked state.
Here's a script. The prompt() is using it:
import GlobalState, DayPhase
from UnityEngine import Debug
from game import prompt
dayNight = GlobalState.Instance.DayNightTracker
if dayNight.Phase == DayPhase.Day:
prompt_text = "Take a nap until night time?"
elif dayNight.Phase == DayPhase.Night:
prompt_text = "Go to bed until morning?"
else:
raise Exception("BedScript didn't recognize this day/night phase and couldn't change it: " + dayNight.Phase);
selected_idx = prompt(prompt_text, ["No", "Yes"])
if selected_idx == 1:
if dayNight.Phase == DayPhase.Day:
dayNight.Phase = DayPhase.Night
elif dayNight.Phase == DayPhase.Night:
dayNight.Phase = DayPhase.Day
Here's the implementation of prompt in C#:
public async Task<FutureAwaiter<PyInteger>> Script_Prompt(IScheduler scheduler, FrameContext context, PyModule ignored, string prompt, PyList choices)
{
var choiceCollection = new List<string>();
foreach (var choice in choices)
{
choiceCollection.Add(choice.ToString());
}
DialogRequest newRequest = new DialogRequest(prompt, choiceCollection.ToArray());
SetupForRequest(newRequest);
var future = new FutureAwaiter<PyInteger>(scheduler, context);
void fireWhenDialogDone(DialogRequest request)
{
request.WhenDialogRequestIsDone -= fireWhenDialogDone;
future.SetResult(PyInteger.Create(request.ChosenIndex));
}
newRequest.WhenDialogRequestIsDone += fireWhenDialogDone;
scheduler.NotifyBlocked(context, future);
await future;
return future;
}
What you need to do is figure out internally how you're going to
asynchronously fulfill the result in your own runtime. In my case, it's an
event that's fired by my dialog subsystem when a choice is made. When that
fires, I call SetResult() on the future. That kicks it out of the blocked
queue and puts it into the active queue.
To complete the contract with the scheduler, you have to do the stuff at
the end. That tells the scheduler whatever invoked this--which is why it
needs the context, by the way--need to be put in the blocked queue until
the given awaiter comes in. Then you await on it to suspend execution. The
scheduler will be waiting for the continuation created by the .NET runtime
and will file it with the blocked task. You finally return the future.
Cloaca will know to open it up and extract the value out to put it on the
data stack when things resume.
It's really simple! (no it's not). =D
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADRTKK67KZMTUXMU6BTZPJ3RTX5YFANCNFSM4KSRXJCQ>
.
|
This is exactly what i've been hunting for, particularly green threading. I'm also working on a game which features python programming on a virtual computer/network - i've been using IronPython until now, which all works until the player creates a tight loop in a python script and steals all the CPU cycles. I started to build something similar, but this looks way more developed. Look forward to having a play and hopefully contributing.
The text was updated successfully, but these errors were encountered: