-
Notifications
You must be signed in to change notification settings - Fork 66
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
Discussion / Feedback: Issues running in Node / Server Side #288
Comments
Hello! This issue happens because we use web workers in Wasmboy, and this is done in node using worker threads: https://nodejs.org/api/worker_threads.html Thus, could you add the flag Therefore, your command would be Thanks for checking out Wasmboy! If you don't mind, may I ask what you are building / want to do. I would love to support it / check it out / add it to the readme! :) |
Checked what I think was the docs, apparently, it is imported with a module require. |
Cool sounds good! Let me know if adding the flag works :) It's on my TODO list, to move the workers to use the node threads thing 👍 And Ohhhh that would be SOOOOO rad! I've been looking forward to seeing this run for an actual practical purpose on a server 😄 Rather than just my tests. Would love to help test this out in the future! 🎉 |
Yeah, the latest version of Node is running fine without issues. I didn't even have to use the flag, but I did call in |
Oh, @torch2424 , quick thing. It's saying all of the functions aren't functions.
|
Ahhh hmm. That's quite odd. I don't need to do that in the tests. Maybe I'll make a quick server side demo project on glitch or something to figure this out...
Can you console.log the WasmBoy Object? E.g I think I may have messed up my rollup config, and made WasmBoy a named export. Thus, you may have to do something like Which, yep, seems to be the case, my bad! https://github.com/torch2424/wasmboy/blob/master/test/accuracy/accuracy-test.js#L5 The tests will probably be another good place to figure things out for now. As the docs on the wiki are kinda out of date, and made with browsers in mind (mostly). But more than happy to answer questions here. I may be a bit delayed in response though 👍 |
This seems to be an internal thing, but here's my crappy code (threw as much from the lib docs together as possible for testing) that is most definitely organzied wrong for testing just in case it happens to be the cause:
|
Hello! So after looking at your code, it seems like you get to: So it seems you reach the So, the reason why you need to set
Which you can then generate a png and then do whatever you want with it 😄 |
This error just popped up when I tried to npm install the package on my main PC. I didn't know if you knew what was the cause... |
@EuphoricPenguin Seems like you don't have git installed? 🤔 |
Yeah, sounds right. Probably need the latest version of Node too... |
Here's my current test code. I think it's still mostly incorrect, but I'm not sure if the error above is something I did or the module itself...
|
So for your first error, I opened #291 , you are doing the right thing, and I totally made a bad assumption here 😂 (As you can tell, project is still a little rough around the edges for the headless case). But stoked that you brought this up! 😄 And your code looks good so far! But there are a few things:
I think this line returns a promise, and not the actual "image of the screen". You will want to get the image rgb array, and then pass that array to build a png. That being said, I don't know how you plan to pass the image back to discord quite yet (as a file or link), but if you want to do a link, you may want to url encode it? or if you are sending a file, you may want to send a jpg to save some bytes. 😄
This is what I opened an issue for. For now, load the rom file using fs, and pass that in 😄
I am not entirely sure what you are trying to do here. I may be just lacking in my node knowledge though. What do Thanks! 😄 |
Nah, that's some code I'm recycling from gbajs. Probably doesn't work here. I was just looking for errors, but I was stopped by the internal module file reading stuff before that became an issue. |
@EuphoricPenguin Oh rad, that makes sense than 😄 And yeah, let me know if you need anymore help extracting the image. I think you would honestly be fine just copy pasting my image functions, and then going from there. Every imageDataArray is just an RGB array. So It "should" be flexible 👍 |
I think I'm a little confused on how to get the RGB array. In the link you sent above, it looks like you're passing it to another function to do something... |
Yeah, the last part. What is that for? You're doing math on the whole array? Sorry, this is new to me. I'm a tad confused lol. |
Oh, separate question: |
So what that does is slice out (as in make a copy of) a section of the WASM linear memory. The Core (Wasm Module) write out [r, g, b, r, g, b, ....] values in memory, where each r, g, b collectively represents one pixel. Then after that, I convert the r, g ,b we got from WASM memory into an r, g, b, a array (this is because HTML canvas only accepts r, g, b, a, and I think pngs need this as well). And yes, this result, Does that help?
So kind of. It depends on how you want your thing to run. I'll take a look at the other project and try and see what they are doing. But for now, essentially you will want to do something like (psuedo code incoming):
|
So... yeah, that first part helped explain, thanks. So, for the second part, my understanding is that it just gets the frame for whenever you run the export function, correct? Some emulators actually require you to manually deal with keeping the clock running so lag would be significant if it couldn't advance itself. |
You are welcome! 👍
So it does "get the frame", by running
So yeah that makes sense though? The reason being, on the server at least, I don't have as much context on what you are doing (running tests, extracting audio, streaming a game, etc...). So the standard 60 frames per second doesn't really make us much sense? But if it does for you, here is a snippet on running code at 60fps. Just run the As a side note, for the browser, WasmBoy has a function called Could you share more of like your end to end goal, and perhaps I can help that way? E.g A user pings your bot on discord, emulator starts up, etc.... And thanks for sharing server boy, that was super rad to see 😄 |
Basically, with gbajs, I can grab the screen at any point, and the emulator will continue to run. I don't need every frame, just ones when people are actually using the bot. The GIF setup will basically still be the same, just I'll get a few more frames at once.
Here's my function to grab the screen like mentioned above. I think I understand what you're trying to say, but yeah, I don't need to export every frame if that helps, but I still need the emulator to compute every frame, since I don't know which frames I will end up needing. |
If you don't mind me asking, where can I try out your current bot? That way I can understand as an end user what should be happening? 😄
Cool, so yeah, You can wrap the execute frame in that 60fps. And then outside of the 60fps, use the image code I sent you? That or try something like:
Whenever you need images? 🤔
So you want the emulator to continue playing on the server, while waiting from a response from your client on what to do next? Is that what you mean? 🤔 If so, either try the 60fps code, and the execture frame code (what I reccomend, and probably easiest to maintin), or the Let me know if this helps. If you want the exact smae functionality as GBAJs, with that "screenshot" function, I'm open to open a new issue, or you can feel free to submit a PR? |
https://discord.gg/mVpKRwg - Our current code is running here. Not the final result I'm looking for, but it'll give you a better idea of the hopeful end result. Thanks for being this active/helpful to a newbie btw, very nice of you. I don't really need an exact replacement, since I'm just working with what I have. I'll worry about making the functions similar on my own later on (building functions I can use with simple logic stitches that on their own work out the differences...) |
Thanks for the invite! I tried it out, and my mind was blown, this is soooooo rad 😂
Anytime! I'm honestly super stoked on your project (since you first told me a little while ago), and it'll be the first major thing built with the emulator so I am super excited. 😄 Also, I am glad that you are a little new. This type of feedback will definitely help once I start rewriting the docs. Plus, it only strengthens my "how to explain this over text" skills. That being said, since you've been really nice, and you're project is really cool, it definitely helps me wanting to help haha 😂
Awesome glad to hear! That all sounds like a good idea to me. 👍 Now on the the good partsSo one last question I have is, are you constantly running the emulator because you want that real time clock support? Currently, WasmBoy does not support real time clock unfortunately (it will eventually), but what I'm about to suggest I think is worth it. So for a simple example, let's say I enter in discord 'up*3'. Then in my server I would do this:
With this approach, you'll also not be pegging your server constantly running an emulator (not a huge burden on CPU I know, but why not save some), and you could do this serverless if you wanted? 🤔 Which is why I suggest I think this is worth sacrificing real time clock support, because your server wont be constantly running the emulator. This all being said, while writing this, the You going to make your project open source by the way? I'd love to feature it on the README once it's getting close to done (no worries if not!). |
Also, I thought more About what I said about The only reason why I was skeptical, was you may get some vsync issues, or like half drawn frames. But if the gbajs isn't waiting for the frame to fully drawn, then you may have already noticied it, and been okay with it 😄 So yeah, let me know if you liked the method I sent you. If not, I can give you a snipper on how I'd do it with it always running 👍 |
Yeah, the style of the emulator is to run nonstop. I guess pausing might be a decent option, but since I don't think there's some kind of substitute in GBAjs, idk if it would be really nessecasry. What I meant by clock is the idea that frames are run at the CPU clock speed without me having to worry about keeping some resemblance of that correct. |
RTC is underutilized in both games, and in the GBA titles the one feature it is used for (growing berries) breaks after 100 hours anyways. |
Well if that is the case than, and you really want it constantly running, then I think the following code should work 😄 :
I opened #293 for a direct screenshot function. But then a lot of questins come to mind, like should it export png vs. jpg, and all of that. 🤔
Definitely, agreed 😄 |
Hey guys, sorry to bump this old discussion, but did anyone figure out a way to properly input a command, wait for it to fully finish, then pause for screenshot? My code: // actual input
WasmBoy.setJoypadState(newState);
await WasmBoy._runWasmExport('executeMultipleFrames', [60]);
await WasmBoy.play();
// reset input - looks like it holds the button down otherwise
WasmBoy.setJoypadState(WasmBoyJoypadState);
await WasmBoy.pause();
const image = await getScreenshot();
await saveState(); My problem is that it doesnt seem to fully finish rendering the button input so we have to do it multiple times, I've also messed around with frameskip and the frame number but that makes it skip ahead. @torch2424 awesome work btw, and thanks for the headless support |
No worries on the bump! 😄
Oh! Looking at your code, yeah, it's because the API is confusing, my apologies on that. I haven't had much time to work on WasmBoy, as I shifted a lot of my time to working on AssemblyScript itself 🙃 That being said, I do have a personal Code TODO list. and I reeaaalllyyyy want to pick this project back up. As I actually use WasmBoy to play some games for fun from time to time 😂 But, essentially, you don't want to run // Set the input, yes, this will be held down until you set the new input state
WasmBoy.setJoypadState(newState);
// Run the WasmBoy for 60 frames. This does the actual exectuion of the frames
await WasmBoy._runWasmExport('executeMultipleFrames', [60]);
// No need to play or pause here, see my explanation above
// This would just keep the emulator running at 60fps, rather than running a specific number of frames
// await WasmBoy.play();
// await WasmBoy.pause();
// Reset the input
WasmBoy.setJoypadState(WasmBoyJoypadState);
// Get the screenshot
const image = await getScreenshot();
// Save state returns an object if I'm not mistaken, and then you can save it however you like somewhere
const saveState = await saveState();
Thank you! Glad that you like it! And I'm stoked the headless support works for ya! 😄 👍 Let me know if this works for you! Thank you! 😄 |
@torch2424 Thanks for the help and quick reply 😄 Unfortunately it seems like the same issue, the game does run for 60 frames but its not enough to process the entire action. (trying to run a pokemon rom btw): So sending "UP" only works after multiple tries, so I'm thinking it is not running enough frames. Maybe thats not the issue though because even if i change # of frames to 300 or put it in a for-loop it doesn't always seem to run all the way through. |
@alex-red You are welcome! 😄
Hmmmm 🤔 Can you log out the object you are sending? This is the function that handles the object and sends along the appropriate array to the webworker that then talks to the wasm module: https://github.com/torch2424/wasmboy/blob/master/lib/controller/controller.js#L49 Also, I noticed you are doing a save state, are you trying to do this all in one go? Or are you saving and reloading the state? 🤔 |
Sure, I'm just sending this: const WasmBoyJoypadState = {
UP: false,
RIGHT: false,
DOWN: false,
LEFT: false,
A: false,
B: false,
SELECT: false,
START: false,
} With the corresponding input set to true. And yea I'm saving it at the same time as sending the input and getting the screenshot. |
Hmmm that seems correct 🤔
Oh I meant are you doing something like: // Set the input, yes, this will be held down until you set the new input state
// Set the input to down
WasmBoy.setJoypadState(downState);
// Run the WasmBoy for 60 frames. This does the actual exectuion of the frames
await WasmBoy._runWasmExport('executeMultipleFrames', [60]);
// Reset the input
WasmBoy.setJoypadState(resetState);
// Get the screenshot
const image = await getScreenshot();
// Save state returns an object if I'm not mistaken, and then you can save it however you like somewhere
const saveState = await saveState();
// .... Some time passes ....
await loadState(saveState);
// Set the input, yes, this will be held down until you set the new input state
// Set the input to up
WasmBoy.setJoypadState(upState);
// Run the WasmBoy for 60 frames. This does the actual exectuion of the frames
await WasmBoy._runWasmExport('executeMultipleFrames', [60]);
// Reset the input
WasmBoy.setJoypadState(resetState);
// Get the screenshot
const image = await getScreenshot(); Like, are you reloading the save state? Or are you running it all at once: // Set the input, yes, this will be held down until you set the new input state
// Set the input to down
WasmBoy.setJoypadState(downState);
// Run the WasmBoy for 60 frames. This does the actual exectuion of the frames
await WasmBoy._runWasmExport('executeMultipleFrames', [60]);
// Set the input to up
WasmBoy.setJoypadState(upState);
// Run the WasmBoy for 60 frames. This does the actual exectuion of the frames
await WasmBoy._runWasmExport('executeMultipleFrames', [60]);
// Reset the input
WasmBoy.setJoypadState(WasmBoyJoypadState);
// Get the screenshot
const image = await getScreenshot();
// Save state returns an object if I'm not mistaken, and then you can save it however you like somewhere
const saveState = await saveState(); Or better yet. Is your code on Github? I think I could understand better if it is, or if you can post a gist of everything or something? Thanks! 😄 👍 |
It is the latter :) I've put a gist here with the relevant calls: https://gist.github.com/alex-red/1221a55e79eeea9762695d0639c7493b The server call is at the bottom, let me know if you need anything else |
@alex-red Ah! I bet I found it! 😄 So it seems like you are running This will start running the emulator in a loop, and continually updating the joypad (thus, cancelling out something you may be setting). Can you try commenting out that Again, my apologies for the API being so confusing. API design was on my roadmap, just haven't had the time haha! 😂 Let me know if it works! Thanks! 😄 👍 |
That makes sense, unfortunately no luck though -- it looks like it isn't even advancing the frame without No worries though, I'll tinker around and see, otherwise I'll move on to just using it non-headless :) |
@alex-red So I went ahead and added a test, and I totally got it working 😄 https://github.com/torch2424/wasmboy/tree/save-state-per-frame/test/integration See the Also, see the And I never call I hope that helps! 😄 👍 |
Oops! I just noticed though. If you are trying to execute frame by frame, and save / load states. Because we're using web workers, I'll need to add something for a headless save state. As the current state wouldn't have been passed back yet by the worker without running I really gotta clean up this API 😂 |
@torch2424 Wow thanks so much, that worked! No idea why I didnt think to try: WasmBoy.setJoypadState(newState);
await WasmBoy._runWasmExport('executeMultipleFrames', [1]);
WasmBoy.setJoypadState(WasmBoyJoypadState); I also realized that the |
@alex-red You are welcome! 😄 I am glad that it works!
Oh nice! I am glad the stuttering got fixed on your end as well! 😄
So! I actually just fixed it! I happen to be working on a demo for work actually haha! And I also ran into this. So I'll publish a new version tommorrow-ish, and it's on the branch |
I've commented out everything in the code except for the call in for wasmboy, and this error still occurred.
The text was updated successfully, but these errors were encountered: