Not logged inGosu Forums
Forum back to Help Search Register Login
Up Topic Gosu / Gosu Exchange / Gosu implementation of RGSS
- - By meustrus Date 2011-02-04 19:42 Edited 2011-02-28 17:00
So I've been working on a Gosu implementation of RGSS. Here it is:

Some background: RGSS is the Ruby-based scripting language of RPG Maker XP and VX. The RGSS library is Windows-only GDI-based, and is generally considered slow for what it does.

What I've done above is to create an implementation of RGSS2 - that is, with the extensions added in RMVX - but with RGSS1 classes where there are conflicts. For example, windowskins are handled differently between the two; the RMXP version is implemented. Tilemaps are wildly different; I have only implemented the RMXP version.

To try it out, you need an RMXP project (uncompressed). Simply extract the files to the project's root directory (it will include main.rb and a directory). Run main.rb, either directly (it has a shebang) or with the ruby command. You can create a new project using the trial version of RMXP, available here:

Why am I posting it here? Well, besides the fact that people from the RPG Maker community are here (I know who you are!), I'm having some issues that are probably due to my relative inexperience with Gosu:

1. Image loading:
  a: Limited format support. I need at least JPG's. The current version cannot display the title screen because it has no means of loading JPG's.
  b: No transparency? For some reason, Gosu will not load the alpha value from some (but not all) RTP graphics (png's).
2. Really slow text drawing; RGSS implements text drawing as an operation on images. I'm implementing it as a Texplay splice with Gosu::Image.from_text. This is slow. I've tried using render_to_image and Gosu::Font.draw, but that method does not preserve alpha channels.
3. I need MIDI support! Granted, I've only tested on Mac, but MIDI's are currently disabled so I could test other things.
4. FIXED General performance issues: Scene_Map is running at about 9 fps, and I haven't been able to figure out why. Do I just need to do this in C instead?
5. FIXEDSegfaults with modifying $window. I am trying to set the window's caption to something else when "F2" is pressed (to display frame rate) during $window.update and that segfaults.
6. I'm also trying to bring the game in and out of fullscreen during $window.update by closing the existing $window object and restarting it; I'm getting a huge mound of untraceable Cocoa errors. I haven't even tried testing the resize_screen or frame_rate= methods, which do the same thing.

Any hints or suggestions? Any vouchers of support or condemnations?

For those worried about the legality, this is not "reverse engineering" which is forbidden in the RMXP license. I am just implementing an interface. I know full well that Enterbrain implemented it using GDI, and I am implementing it completely differently. It is still necessary to purchase a license to use the interface, especially because games made with RMXP basically never escape their roots in Enterbrain's coding style and practices. Further, this does not negatively affect the marketability or profitability of Enterbrain's products; in fact, it increases the marketability by allowing people to distribute their games on more platforms.

2011-02-07: I fixed #4 by figuring out that some sprites were creating a new Gosu::Image every frame. I've stopped them from doing that, and also rewritten that procedure to use Texplay clearing and splicing instead of creating a new Gosu::Image for source rectangles. New file:
2011-02-21: Part of #5 seems to have evaporated, either due to a Gosu update or a change I made to the dual-threading between Gosu::Window and Graphics. I separated #6 from that as it's a separate issue that may need a more involved solution, such as providing :fullscreen=, :update_interval=, and a :resize(width, height) method. Ne file:
2011-02-28: So #5 was still an issue, but I've fixed it now. Not posting the file again because I don't think anyone cares; I'll post the next time I fix something.
Parent - - By Spooner Date 2011-02-04 23:13 Edited 2011-02-05 00:20
1. a) If you use the Devil gem, it should magically increase the number of image types you can load (including jpeg).
    b) Never had a problem with this. Gosu manages PNG transparency fine; perhaps an issue with the way RPGMaker deals with transparency? Does it use the "gif" way of making a specific colour transparent when the image is loaded, rather use an alpha channel (easily dealt with in Texplay, if that is what happens)?

2) Now I understand why you were so insistent on rendering text to an image. It isn't that it is the best way to put text in a game, but the fact is that you are forced to do it that way because that is the way RPGMaker does it. I think you are screwed (in Gosu) if you want to do that particular thing anything near quickly.

5. Although it is possible to replace the Gosu window, it is not supported and I was warned away from relying on it (but with a promise of resize/fullscreen switching in some future life...maybe :P). Why it should segfault on #caption is anyone's guess

I'm pretty sure that "reverse engineering" covers replicating the functionality in a slightly different way, though I have no idea of the legal ramifications. By definition, reverse engineering will implement things slightly differently; what you are doing it copying an interface (whether it is GUI or API interface) without looking at source or decompiling, which is exactly what you are doing.

EDIT: Corrected reference to Texplay to be Devil (oops)
Parent - - By meustrus Date 2011-02-05 00:32
Questions about Devil: Firstly, does libdevil need to be installed for it to work, or just for the gem to compile? Is it possible to package it in rubyscript2exe and/or an OS X .app package? Does it replace or augment Gosu's image loading? If it replaces, that might fix the PNG problem.

While earlier versions of RPG Maker defined the transparent color as the zero-index color in the 8-bit color table, RMXP uses full alpha-channel support on 24-bit PNG's. There is a tool inside RMXP to import images made for "the gif way" for full alpha-transparency (it gives to a dropper tool to select full-transparent and half-transparent colors) but I am reasonably certain that this changes the actual files as it copies them to the project directory, rather than storing the information somewhere (where it would be stored, I have no idea, and I know basically everything about how RMXP stores its project information).
Parent - By Spooner Date 2011-02-05 00:41
The DevIL gem requires DevIL.dll, ILU.dll and ILUT.dll, which you can package with your application using Ocra, as I have done previously (Ocra is the preferred Windows packager and works with Ruby 1.9.2; rubyscript2exe is pretty obsolete). Assume similarly for OS X, but I have no direct experience. It monkey-patches Gosu's Image#new and automatically loads non-png images through Devil on their way to being gosu images (so completely transparently). To save non-png images, you'd have to use the Devil API, though, but you could just monkey-patch Image#save if you wanted to.

If the images are proper alpha-channel PNGs, then they should work fine with Gosu as far as I'm aware. *shrugs*
Parent - - By jlnr (dev) Date 2011-02-05 07:32 Edited 2011-02-05 07:38
First, that sounds like an awesome project! Allowing people to move their code is always a good thing.

>  1a: Limited format support. I need at least JPG's.

libjpeg looks exactly like libpng, which is absurdly hard to use, so I've avoided it so far. A contribution for JPEG specificially would be very welcome though, I doubt there are any other interesting formats after that.*

> 1b: No transparency? For some reason, Gosu will not load the alpha value from RTP graphics (png's).

(Insert repeated rant about libpng :)) Can you attach one of these files? Everything I save from Photoshop works with Gosu, but I was mailed one other PNG file for which transparency was broken. I hope it's the same bug.

> 2. Really slow text drawing; RGSS implements text drawing as an operation on images.

That interface will never work great with OpenGL-based backends, I guess. It would be easier to work around this from the C++ side too. I guess a proper render_to_texture with transparency support could save the day, but it's pretty far down the list.*

> 3. I need MIDI support! Granted, I've only tested on Mac, but MIDI's are currently disabled so I could test other things.

It seems no major audio library has MIDI support anymore. On Windows and OS X at least, one could use system APIs for MIDI playback. A clean next step would be if someone submitted a Gem that implements a Gosu::Song like interface for MIDI files. I would happily add hooks to Gosu to integrate with that, or include its source.*

> 4. General performance issues: Scene_Map is running at about 9 fps, and I haven't been able to figure out why.

Is the map static? There is a record{} function in the works that will take all the drawing code in the block and return a Gosu::Image that can than be redrawn with only one Ruby call. Not sure if that explanation is very good, but if the map doesn't change aa lot, then this will give you a huge boost.

> 5. Segfaults with modifying $window. I am trying to set the window's caption to something else when "F2" is pressed (to display frame rate) during $window.update and that segfaults.

Can you post a minimal example for that? That sounds like a genuine bug.

> I'm also trying to bring the game in and out of fullscreen during $window.update by closing the existing $window object and restarting it; I'm getting segfault during $window.close.

Multiple windows aren't supported, however #fullscreen= and similar methods are in the works.

* With these and other problems, I really wonder if anyone would like to help out? I'd gladly throw in €20-€50 for contributions like these. Anyone interested in setting up a pledgie for such contributions? Between working on my game and just keeping Gosu *alive*, I really can't invest much more time right now. For example—behind the scenes, I'm wasting hours after hours trying to improve the—that is also a project that shouldn't even be part of Gosu. Stuff really explodes everywhere lately. :(
Parent - - By mathias Date 2011-02-05 12:37
I for one would really like to help out on the C++/OpenGL side of things, but I am lacking proper goals and directions to do so. The GLFW port for example was even much more fun than I imagined. :)

Also I am afraid that contributing is not that easy, what is the reason to have both a github and a google code page, if you only push the svn changes back to git anyways? Merging git-forks back into the svn seems like an awful waste of time :\
Parent - By jlnr (dev) Date 2011-02-06 10:13
Hmm, I'm not sure if the use cases are convincing enough right now, but I'd be curious to benchmark some stuff using render-to-texture (with alpha). Really analogous to beginRecording()/endRecording()—except it doesn't render the DrawOpQueue into a vertex array but to a texture. That would be a nicely isolated feature too :)

There's also lots of things that are annoying but that I'd happily throw money into a pledgie pot for. For example, it's terribly annoying to develop or test C++ example games because there are no MSVC(08/10)/Xcode(4) templates. I have tested every single feature in Gosu from the Ruby side for the last years because it's just way faster, even without a debugger.

> what is the reason to have both a github and a google code page

The reason is to make forking easier for people who are used to git. You really shouldn't mind the minutes that *I* may waste merging stuff back in just because I'm too stupid to use git-svn (or even git), which can apparently sync stuff back. Manually merging isn't even much slower than reviewing the diff.

I think I should post time consumption statistics for Gosu. For example, in the time I wasted burning and testing two Ubuntu live CDs only to throw them away, I could have merged a dozen contributions even from 1.44" floppies ;)
Parent - - By meustrus Date 2011-02-05 14:45
Alright, here's the project inside an RMXP project:

I've tried to include all the RTP files. Under Windows, it should look for them in the right place (if you installed Enterbrain's RTP) but I haven't tested that part yet. Everywhere else, it looks for RTP in the ~/.rgss folder. Anyway, it shouldn't be an issue since I've included the files.

For an example of an image where transparency isn't working, look in /Graphics/Characters. Neither files are loading transparency correctly, although now that I think about it the transparency IS getting loaded correctly on the windowskin. Here's how it loads the images:

Typically, game scripts ask for RPG::Cache.character(name, hue). 'name' is filename and 'hue' is a hue change on load (haven't tested that part yet). That function adds 'Graphics/Characters/' to the beginning and calls RPG::Cache.load_bitmap(name, hue). This keeps a cache of loaded images. It tries to create the image with My implementation of Bitmap takes the filename, searches for it in the project directory and then the RTP path(s), also searching for the right file extension (filenames are stored without their extensions for some reason), and passes the filename to super($GameWindow, filename). It also takes two arguments as a width and height to pass to Texplay.create_image.

I'm not sure what's causing the lag in Scene_Map. There are tons of things being told to draw even if they're empty, like pictures sprites and two parallaxes. My Tilemap implementation could be the culprit, but I can't have it render to a static image because the different tiles have different Z values.

As for the problem with :caption=, the code that operates with that are in the GosuWindow class (global.rb:102) and the Input module (input.rb:46). Don't let those small line numbers fool you, there are 1300 lines of new code here (and 1400 of the RPG module written by Enterbrain, which is mostly data types with no real functions).

Now, I haven't been able to test whether this exact version works because it's including devil and I get an error from that:

dyld: lazy symbol binding failed: Symbol not found: _ilInit
  Referenced from: /Library/Ruby/Gems/1.8/gems/devil-
  Expected in: flat namespace
Parent - - By jlnr (dev) Date 2011-02-06 09:49
Since I don't have DevIL installed either, can you post a minimal example for the caption= bug?

I could reproduce the bug with "001-Fighter01.png". I don't know what program they've been saved with, but if you just use Apple's Preview to open and save them again, the alpha channel works. I'll attach it to my To Do list, I really think it's the same alpha issue that shawn42 ran into.
Parent - - By mathias Date 2011-02-07 14:07
These sample images are only on your local drive or can we see them too?
Parent - By jlnr (dev) Date 2011-02-07 15:17
I have no idea about their license, so I'm hesitant to check them in. I guess it's okay to forward them on though. :)
Parent - - By meustrus Date 2011-02-07 21:19
Here's a new one:

It's also wrapped in an RMXP project which contains all the graphics necessary to run (you shouldn't need the RTP installed). The original loader (Game.exe) is included as well, but you do need to install the RTP from Enterbrain to use that.

It's got Devil removed for now so it should work with only Gosu and Texplay installed. I also managed to figure out what was making it really laggy sometimes - two of the sprites were being recached every frame, which used to involve creating a new Gosu::Image. I've both replaced that procedure to use Texplay clearing and splicing instead, and fixed what was causing it to recache every frame.

I extracted the code for :caption= but there was no error with the simplified example. Could it have something to do with my multithreading tricks to get out of's loop? I had to do tricks because in Gosu, the update procedure calls upon objects, while in RGSS the objects call upon the update procedure. GosuWindow.update basically tells Graphics.update that it's OK to continue, then uses Thread.stop to wait for the next call to Graphics.update to call @@window_thread.wakeup. So while I am technically using two threads, there's never a point of concurrency because Graphics.update just calls sleep(0.001) until the window thread has stopped.
Parent - - By banister Date 2011-02-08 01:51
You can also squeeze some extra performance out by using :sync_mode => :no_sync for the clear() and only syncing after the splice
Parent - - By meustrus Date 2011-02-08 02:44
I'm using a :paint block. Doesn't that make it wait to sync until the end?
Parent - By banister Date 2011-02-08 03:02
hmm, yes, but imo - dont use paint blocks, it's a lot faster not to use them. Im actually thinking of removing the paint block in a future version.
Parent - - By jlnr (dev) Date 2011-02-08 03:01 Edited 2011-02-08 03:07

> Could it have something to do with my multithreading tricks to get out of's loop?

Yes, none of Gosu is built for multithreading. #caption= specifically uses Cocoa, which needs some special flag to work with pthreads. Can you move all of Gosu onto one thread? I'm wondering if there's some other way to invert the control. Jumping around with continuations doesn't sound very inviting right now though.
Parent - By erisdiscord Date 2011-02-09 03:45
Jumping into the Gosu run loop actually causes a hard crash (SIGSEGV? I forget) on OS X. Actually, might have been something to do with a mutex being unlocked too many times. Definitely not inviting.
Parent - - By meustrus Date 2011-02-09 04:36
I imagine I could try a :try block, but that only gets me out when Graphics.update :throw's; there would be no way to jump back to that execution point after Gosu has gone through another update step. Would it be better if I had Gosu::Window do the :sleep'ing while it waited for the other thread to stop, instead of the other way around? I can't think of any other solution to my problem and there definitely shouldn't be any thread conflicts since they're just bouncing control back and forth.
Parent - - By jlnr (dev) Date 2011-02-09 07:15
Yes, having Gosu on the main thread sounds good. Many things in Gosu are not technically thread safe (not even "milliseconds"). But I cannot think of anything that would be a problem if the threads really never overlap.
Parent - - By meustrus Date 2011-02-21 18:20
I'm not sure what changed (maybe it was when I changed to Thread.stop and @@window_thread.wakeup, or the last GOSU update) but I'm not having any more problems with :caption=.
Parent - - By meustrus Date 2011-02-24 22:36
Nevermind, I'm having the problem again. It's not a segfault, it's a "Bus Error":

./gosu-rgss/global.rb:105: [BUG] Bus Error
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]

The code:

[class GosuWindow < Gosu::Window]
102:  def recaption
103:    to_show = Settings['Title']
104:    to_show += ' (%d fps)' % [Gosu.fps] if @showing_fps
105:    self.caption = to_show
106:  end
Parent - By jlnr (dev) Date 2011-02-25 00:25
But this code is still called from another thread, right?
Parent - - By erisdiscord Date 2011-02-25 04:18
For the record, I seem to encounter bus errors on OS X in situations that could have caused a segfault on Linux. Usually invalid memory access or so, but also whatever it is that happens when you call Gosu from another thread. If you're doing that, then it's almost certainly the cause of your woes.
Parent - - By meustrus Date 2011-02-28 16:30
Alright, changed the code to this and it works now. I still don't understand why it matters which thread is calling the function if only one of the two is ever running at the same time.

  def showing_fps=(bool)
    @showing_fps = bool
    @recaption = true
  def recaption
    @recaption = @showing_fps
    to_show = Settings['Title']
    to_show += ' (%d fps)' % [Gosu.fps] if @showing_fps
    self.caption = to_show
  def update
    Thread.stop if @drawn
    recaption if @recaption
Parent - By jlnr (dev) Date 2011-03-01 10:35

> I still don't understand why it matters which thread is calling the function if only one of the two is ever running at the same time.

The system may rely on thread-local storage as one simple reason. Hey, at least it seems to crash pretty reliably as opposed to "every third user experiences it" :)
Parent - - By mathias Date 2011-02-09 11:36
The transparency bug is now fixed in the svn and it should be part of the next update. The problem existed only with Images that are using the palette, and most image manipulation programs don't use these. In these images, the transparency chunk (tRNS) was misinterpreted. See and for details.
Parent - By meustrus Date 2011-02-09 16:29
Excellent! I shall wait in anticipation for the next release.
- - By meustrus Date 2011-02-09 16:27
Is there anything I can do about MIDI support? Should I look into getting it into Audiere? What if I were such a stud I could write MIDI support brand new? Or if I could patch in an existing package? Is there an existing package that could coexist with Audiere?
Parent - - By jlnr (dev) Date 2011-02-09 17:47
I just double-checked and take back my statement that MIDI is completely unsupported. It's just very special on each platform.

On Windows, Audiere actually has MIDI support:

This is completely independent from the rest of Audiere, which is why I thought it didn't work after a quick test. Gosu would need to specifically add support for this class. No big problem, as the internals of Gosu are designed to support two kinds of Song implementations (yay). I can schedule this right after the upcoming graphics changes.

For Linux, which uses SDL_mixer right now, there is some theoretical MIDI support. You should verify that it still works as it sounds a bit sketchy. It may need extra packages.

This leaves OS X. Apparently, SDL_mixer for OS X supports MIDI, but is missing other features. I could dynamically link SDL_mixer and use it for MIDIs on demand. I just have no idea how to reference a .framework from within a Ruby dynamic library. Or you could check if using the Apple MIDI APIs is reasonably easy. I can't just copy&paste SDL_mixer sadly as it is LGPL licensed. :(

I've actually tried to get MIDI to work on OS X, but Core MIDI looked a bit intimidating and I didn't have a use case for it at the time. (Existing games on the forum would benefit from MOD/IT actually.) I'd be a happy coder if it would work reliably on all platforms though.
Parent - - By meustrus Date 2011-02-09 21:54
I'm a bit confused about Audiere. Is it only being used on Windows? Does it even support OS X? I thought Gosu was moving to Audiere as a solution for all platforms, but it's starting to seem like the only reason was to avoid FMOD.
Parent - - By jlnr (dev) Date 2011-02-10 18:48
For iOS, OpenAL is almost the only choice. So having Audiere for Win+Linux, and OpenAL for Mac+iOS would have been as cool as it gets—there's no way to unify everything without writing a LOT of code. So that was the plan.

Sadly, Audiere turned out not to be compatible with modern Linux distributions anymore. :( So yeah, Audiere only replaced FMOD in the end. I could put it in place on Linux with an inofficial backend, but that would break MIDI, so I'm think I'm not going to bother. Being able to play the same files on all platforms > using the same library on all platforms. :)
Parent - - By meustrus Date 2011-02-11 18:47
So...doesn't OpenAL support MIDI? Or does it have to be implemented with Core Audio? If nobody is willing, I could try to write that up. All I need is a starting point to get involved in developing and contributing to Gosu.
Parent - - By jlnr (dev) Date 2011-02-12 08:24
OpenAL only provides a route for waveform data from memory to the sound card, with some positioning and stuff. So you need to turn the file into waveform data. Right now I use Apple's system library "Audio Toolbox" to do this. I am not sure if there is an API that streams MIDI files into waveform data or if you have to take a completely separate route for that. If Audio Toolbox can't do it, then I'd assume either Core Midi or QTKit could.
Parent - - By meustrus Date 2011-02-15 21:49
Is it possible to link to TiMidity or FluidSynth? Or is that linking from a dylib what's giving you too much trouble?
Parent - By jlnr (dev) Date 2011-02-16 00:13
I only know Timidity, but that would be quite a daunting task since it also includes (lots of) resource files. On Linux, the package manager can handle all of this. I think system APIs are really the way to go for OS X, which should have great MIDI support out of the box.
Parent - - By Dahrkael Date 2011-02-09 18:57
theres a crossplatform ruby gem for midi playback

you can try it and tell us if it works.
Parent - By meustrus Date 2011-02-09 21:44
AFAIK that package does not play MIDI files, just provides a general interface to the MIDI processing system. I could theoretically write up something that uses the interface to play a MIDI file (each file would need its own execution thread) but that wouldn't be all too useful for Gosu as a whole because it would be a Ruby-only implementation.
Up Topic Gosu / Gosu Exchange / Gosu implementation of RGSS

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill