mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-04-29 19:44:49 +02:00
Throw exceptions for Resets and Exits instead of directly raising.
While this does close small memory leaks, this is mostly for threading reasons. We're not supposed to call rb_raise with the gvl released, and calling rb_raise prevents GFX_UNLOCK from being called, which would cause problems for any games that want to call graphical operations in multiple threads should the user reset. We're also now calling Graphics.__reset__ and Audio.__reset__ via eval instead of directly calling the functions, in case a game wants to hook them.
This commit is contained in:
parent
2622a84c53
commit
99ad4fa636
10 changed files with 172 additions and 86 deletions
|
@ -315,21 +315,29 @@ static void printP(int argc, VALUE *argv, const char *convMethod,
|
|||
}
|
||||
|
||||
|
||||
RB_METHOD(mriPrint) {
|
||||
RB_METHOD_GUARD(mriPrint) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
printP(argc, argv, "to_s", "");
|
||||
|
||||
shState->checkShutdown();
|
||||
shState->checkReset();
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(mriP) {
|
||||
RB_METHOD_GUARD(mriP) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
printP(argc, argv, "inspect", "\n");
|
||||
|
||||
shState->checkShutdown();
|
||||
shState->checkReset();
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(mkxpDelta) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
@ -757,15 +765,21 @@ static VALUE rgssMainRescue(VALUE arg, VALUE exc) {
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
static void processReset() {
|
||||
shState->graphics().reset();
|
||||
shState->audio().reset();
|
||||
|
||||
shState->rtData().rqReset.clear();
|
||||
shState->graphics().repaintWait(shState->rtData().rqResetFinish, false);
|
||||
static bool processReset(bool rubyExc) {
|
||||
const char *str = "Audio.__reset__; Graphics.__reset__;";
|
||||
|
||||
if (rubyExc) {
|
||||
rb_eval_string(str);
|
||||
} else {
|
||||
int state;
|
||||
rb_eval_string_protect(str, &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RB_METHOD(mriRgssMain) {
|
||||
RB_METHOD_GUARD(mriRgssMain) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
while (true) {
|
||||
|
@ -783,15 +797,16 @@ RB_METHOD(mriRgssMain) {
|
|||
break;
|
||||
|
||||
if (rb_obj_class(exc) == getRbData()->exc[Reset])
|
||||
processReset();
|
||||
processReset(true);
|
||||
else
|
||||
rb_exc_raise(exc);
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(mriRgssStop) {
|
||||
RB_METHOD_GUARD(mriRgssStop) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
while (true)
|
||||
|
@ -799,6 +814,7 @@ RB_METHOD(mriRgssStop) {
|
|||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(_kernelCaller) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
@ -1038,7 +1054,8 @@ static void runRMXPScripts(BacktraceData &btData) {
|
|||
if (rb_obj_class(exc) != getRbData()->exc[Reset])
|
||||
break;
|
||||
|
||||
processReset();
|
||||
if (processReset(false))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1247,6 +1264,6 @@ static void mriBindingExecute() {
|
|||
shState->rtData().rqTermAck.set();
|
||||
}
|
||||
|
||||
static void mriBindingTerminate() { rb_raise(rb_eSystemExit, " "); }
|
||||
static void mriBindingTerminate() { throw Exception(Exception::SystemExit, " "); }
|
||||
|
||||
static void mriBindingReset() { rb_raise(getRbData()->exc[Reset], " "); }
|
||||
static void mriBindingReset() { throw Exception(Exception::Reset, " "); }
|
||||
|
|
|
@ -58,8 +58,9 @@ RbData::~RbData() {}
|
|||
|
||||
/* Indexed with Exception::Type */
|
||||
const RbException excToRbExc[] = {
|
||||
RGSS, /* RGSSError */
|
||||
ErrnoENOENT, /* NoFileError */
|
||||
RGSS, /* RGSSError */
|
||||
Reset, /* Reset/RGSSReset */
|
||||
ErrnoENOENT, /* NoFileError */
|
||||
IOError,
|
||||
|
||||
TypeError, ArgumentError, SystemExit, RuntimeError,
|
||||
|
@ -317,3 +318,39 @@ int rb_get_args(int argc, VALUE *argv, const char *format, ...) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if RAPI_MAJOR >= 2
|
||||
#include <ruby/thread.h>
|
||||
|
||||
typedef struct gvl_guard_args {
|
||||
Exception *exc;
|
||||
void *(*func)(void *);
|
||||
void *args;
|
||||
} gvl_guard_args;
|
||||
|
||||
static void *gvl_guard(void *args) {
|
||||
gvl_guard_args *gvl_args = (gvl_guard_args*)args;
|
||||
try{
|
||||
return gvl_args->func(gvl_args->args);
|
||||
} catch (const Exception &e) {
|
||||
gvl_args->exc = new Exception(e.type, e.msg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *drop_gvl_guard(void *(*func)(void *), void *args,
|
||||
rb_unblock_function_t *ubf, void *data2) {
|
||||
gvl_guard_args gvl_args = {0, func, args};
|
||||
|
||||
void *ret = rb_thread_call_without_gvl(&gvl_guard, &gvl_args, ubf, data2);
|
||||
|
||||
Exception *&exc = gvl_args.exc;
|
||||
if (exc){
|
||||
Exception e(exc->type, exc->msg);
|
||||
delete exc;
|
||||
throw e;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -78,6 +78,11 @@ struct Exception;
|
|||
VALUE excToRbClass(const Exception &exc);
|
||||
void raiseRbExc(Exception exc);
|
||||
|
||||
#if RAPI_MAJOR >= 2
|
||||
void *drop_gvl_guard(void *(*func)(void *), void *args,
|
||||
rb_unblock_function_t *ubf, void *data2);
|
||||
#endif
|
||||
|
||||
#if RAPI_FULL > 187
|
||||
#define DECL_TYPE(Klass) extern rb_data_type_t Klass##Type
|
||||
|
||||
|
@ -309,15 +314,6 @@ static inline void _rb_define_module_function(VALUE module, const char *name,
|
|||
rb_define_module_function(module, name, RUBY_METHOD_FUNC(func), -1);
|
||||
}
|
||||
|
||||
#define GUARD_EXC(exp) \
|
||||
{ \
|
||||
try { \
|
||||
exp \
|
||||
} catch (const Exception &exc) { \
|
||||
raiseRbExc(exc); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define GFX_GUARD_EXC(exp) \
|
||||
{ \
|
||||
GFX_LOCK; \
|
||||
|
|
|
@ -26,10 +26,6 @@
|
|||
#include "binding-types.h"
|
||||
#include "exception.h"
|
||||
|
||||
#if RAPI_MAJOR >= 2
|
||||
#include <ruby/thread.h>
|
||||
#endif
|
||||
|
||||
RB_METHOD(graphicsDelta) {
|
||||
RB_UNUSED_PARAM;
|
||||
GFX_LOCK;
|
||||
|
@ -38,14 +34,12 @@ RB_METHOD(graphicsDelta) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
RB_METHOD(graphicsUpdate)
|
||||
RB_METHOD_GUARD(graphicsUpdate)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
#if RAPI_MAJOR >= 2
|
||||
rb_thread_call_without_gvl([](void*) -> void* {
|
||||
GFX_LOCK;
|
||||
shState->graphics().update();
|
||||
GFX_UNLOCK;
|
||||
drop_gvl_guard([](void*) -> void* {
|
||||
GFX_GUARD_EXC( shState->graphics().update(); );
|
||||
return 0;
|
||||
}, 0, 0, 0);
|
||||
#else
|
||||
|
@ -53,6 +47,7 @@ RB_METHOD(graphicsUpdate)
|
|||
#endif
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(graphicsAverageFrameRate)
|
||||
{
|
||||
|
@ -63,16 +58,28 @@ RB_METHOD(graphicsAverageFrameRate)
|
|||
return ret;
|
||||
}
|
||||
|
||||
RB_METHOD(graphicsFreeze)
|
||||
RB_METHOD_GUARD(graphicsFreeze)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
GFX_LOCK;
|
||||
#if RAPI_MAJOR >= 2
|
||||
drop_gvl_guard([](void*) -> void* {
|
||||
GFX_GUARD_EXC( shState->graphics().freeze(); );
|
||||
return 0;
|
||||
}, 0, 0, 0);
|
||||
#else
|
||||
shState->graphics().freeze();
|
||||
GFX_UNLOCK;
|
||||
#endif
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
typedef struct {
|
||||
int duration;
|
||||
const char *filename;
|
||||
int vague;
|
||||
} TransitionArgs;
|
||||
|
||||
RB_METHOD_GUARD(graphicsTransition)
|
||||
{
|
||||
|
@ -84,7 +91,20 @@ RB_METHOD_GUARD(graphicsTransition)
|
|||
|
||||
rb_get_args(argc, argv, "|izi", &duration, &filename, &vague RB_ARG_END);
|
||||
|
||||
TransitionArgs args = {duration, filename, vague};
|
||||
|
||||
#if RAPI_MAJOR >= 2
|
||||
drop_gvl_guard([](void *args) -> void* {
|
||||
TransitionArgs &a = *((TransitionArgs*)args);
|
||||
GFX_GUARD_EXC( shState->graphics().transition(a.duration,
|
||||
a.filename,
|
||||
a.vague
|
||||
); );
|
||||
return 0;
|
||||
}, &args, 0, 0);
|
||||
#else
|
||||
GFX_GUARD_EXC( shState->graphics().transition(duration, filename, vague); )
|
||||
#endif
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
@ -180,17 +200,15 @@ RB_METHOD(graphicsDisplayHeight)
|
|||
return rb_fix_new(shState->graphics().displayHeight());
|
||||
}
|
||||
|
||||
RB_METHOD(graphicsWait)
|
||||
RB_METHOD_GUARD(graphicsWait)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
int duration;
|
||||
rb_get_args(argc, argv, "i", &duration RB_ARG_END);
|
||||
#if RAPI_MAJOR >= 2
|
||||
rb_thread_call_without_gvl([](void* d) -> void* {
|
||||
GFX_LOCK;
|
||||
shState->graphics().wait(*(int*)d);
|
||||
GFX_UNLOCK;
|
||||
drop_gvl_guard([](void* d) -> void* {
|
||||
GFX_GUARD_EXC( shState->graphics().wait(*(int*)d); );
|
||||
return 0;
|
||||
}, (int*)&duration, 0, 0);
|
||||
#else
|
||||
|
@ -198,34 +216,47 @@ RB_METHOD(graphicsWait)
|
|||
#endif
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(graphicsFadeout)
|
||||
RB_METHOD_GUARD(graphicsFadeout)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
int duration;
|
||||
rb_get_args(argc, argv, "i", &duration RB_ARG_END);
|
||||
|
||||
GFX_LOCK;
|
||||
#if RAPI_MAJOR >= 2
|
||||
drop_gvl_guard([](void* d) -> void* {
|
||||
GFX_GUARD_EXC( shState->graphics().fadeout(*(int*)d); );
|
||||
return 0;
|
||||
}, (int*)&duration, 0, 0);
|
||||
#else
|
||||
shState->graphics().fadeout(duration);
|
||||
GFX_UNLOCK;
|
||||
#endif
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(graphicsFadein)
|
||||
RB_METHOD_GUARD(graphicsFadein)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
int duration;
|
||||
rb_get_args(argc, argv, "i", &duration RB_ARG_END);
|
||||
|
||||
GFX_LOCK;
|
||||
#if RAPI_MAJOR >= 2
|
||||
drop_gvl_guard([](void* d) -> void* {
|
||||
GFX_GUARD_EXC( shState->graphics().fadein(*(int*)d); );
|
||||
return 0;
|
||||
}, (int*)&duration, 0, 0);
|
||||
#else
|
||||
shState->graphics().fadein(duration);
|
||||
GFX_UNLOCK;
|
||||
#endif
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
void bitmapInitProps(Bitmap *b, VALUE self);
|
||||
|
||||
|
@ -274,16 +305,15 @@ RB_METHOD(graphicsResizeWindow)
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
RB_METHOD(graphicsReset)
|
||||
RB_METHOD_GUARD(graphicsReset)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
GFX_LOCK;
|
||||
shState->graphics().reset();
|
||||
GFX_UNLOCK;
|
||||
GFX_GUARD_EXC( shState->graphics().reset(); );
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
RB_METHOD(graphicsCenter)
|
||||
{
|
||||
|
@ -301,17 +331,17 @@ typedef struct {
|
|||
|
||||
void *playMovieInternal(void *args) {
|
||||
PlayMovieArgs *a = (PlayMovieArgs*)args;
|
||||
GFX_GUARD_EXC(
|
||||
shState->graphics().playMovie(a->filename, a->volume, a->skippable);
|
||||
|
||||
// Signals for shutdown or reset only make playMovie quit early,
|
||||
// so check again
|
||||
shState->graphics().update();
|
||||
);
|
||||
GFX_GUARD_EXC( shState->graphics().playMovie(a->filename, a->volume, a->skippable); );
|
||||
|
||||
// Signals for shutdown or reset only make playMovie quit early,
|
||||
// so check again
|
||||
shState->checkShutdown();
|
||||
shState->checkReset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RB_METHOD(graphicsPlayMovie)
|
||||
RB_METHOD_GUARD(graphicsPlayMovie)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
|
@ -329,20 +359,21 @@ RB_METHOD(graphicsPlayMovie)
|
|||
args.volume = (volumeArg == Qnil) ? 100 : NUM2INT(volumeArg);;
|
||||
args.skippable = skip;
|
||||
#if RAPI_MAJOR >= 2
|
||||
rb_thread_call_without_gvl(playMovieInternal, &args, 0, 0);
|
||||
drop_gvl_guard(playMovieInternal, &args, 0, 0);
|
||||
#else
|
||||
playMovieInternal(&args);
|
||||
#endif
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
void graphicsScreenshotInternal(const char *filename)
|
||||
{
|
||||
GFX_GUARD_EXC(shState->graphics().screenshot(filename););
|
||||
}
|
||||
|
||||
RB_METHOD(graphicsScreenshot)
|
||||
RB_METHOD_GUARD(graphicsScreenshot)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
|
@ -351,7 +382,7 @@ RB_METHOD(graphicsScreenshot)
|
|||
SafeStringValue(filename);
|
||||
|
||||
#if RAPI_MAJOR >= 2
|
||||
rb_thread_call_without_gvl([](void* fn) -> void* {
|
||||
drop_gvl_guard([](void* fn) -> void* {
|
||||
graphicsScreenshotInternal((const char*)fn);
|
||||
return 0;
|
||||
}, (void*)RSTRING_PTR(filename), 0, 0);
|
||||
|
@ -360,6 +391,7 @@ RB_METHOD(graphicsScreenshot)
|
|||
#endif
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
DEF_GRA_PROP_I(FrameRate)
|
||||
DEF_GRA_PROP_I(FrameCount)
|
||||
|
|
|
@ -10,10 +10,6 @@
|
|||
#include "util/json5pp.hpp"
|
||||
#include "binding-util.h"
|
||||
|
||||
#if RAPI_MAJOR >= 2
|
||||
#include <ruby/thread.h>
|
||||
#endif
|
||||
|
||||
#include "net/net.h"
|
||||
|
||||
VALUE stringMap2hash(mkxp_net::StringMap &map) {
|
||||
|
@ -78,10 +74,8 @@ VALUE formResponse(mkxp_net::HTTPResponse &res) {
|
|||
void* httpGetInternal(void *req) {
|
||||
VALUE ret;
|
||||
|
||||
GUARD_EXC(
|
||||
mkxp_net::HTTPResponse res = ((mkxp_net::HTTPRequest*)req)->get();
|
||||
ret = formResponse(res);
|
||||
);
|
||||
mkxp_net::HTTPResponse res = ((mkxp_net::HTTPRequest*)req)->get();
|
||||
ret = formResponse(res);
|
||||
|
||||
return (void*)ret;
|
||||
}
|
||||
|
@ -102,7 +96,7 @@ RB_METHOD_GUARD(httpGet) {
|
|||
req.headers().insert(headers.begin(), headers.end());
|
||||
}
|
||||
#if RAPI_MAJOR >= 2
|
||||
return (VALUE)rb_thread_call_without_gvl(httpGetInternal, &req, 0, 0);
|
||||
return (VALUE)drop_gvl_guard(httpGetInternal, &req, 0, 0);
|
||||
#else
|
||||
return (VALUE)httpGetInternal(&req);
|
||||
#endif
|
||||
|
@ -122,10 +116,8 @@ void* httpPostInternal(void *args) {
|
|||
mkxp_net::HTTPRequest *req = ((httpPostInternalArgs*)args)->req;
|
||||
mkxp_net::StringMap *postData = ((httpPostInternalArgs*)args)->postData;
|
||||
|
||||
GUARD_EXC(
|
||||
mkxp_net::HTTPResponse res = req->post(*postData);
|
||||
ret = formResponse(res);
|
||||
);
|
||||
mkxp_net::HTTPResponse res = req->post(*postData);
|
||||
ret = formResponse(res);
|
||||
|
||||
return (void*)ret;
|
||||
}
|
||||
|
@ -149,7 +141,7 @@ RB_METHOD_GUARD(httpPost) {
|
|||
mkxp_net::StringMap postData = hash2StringMap(postDataHash);
|
||||
httpPostInternalArgs args {&req, &postData};
|
||||
#if RAPI_MAJOR >= 2
|
||||
return (VALUE)rb_thread_call_without_gvl(httpPostInternal, &args, 0, 0);
|
||||
return (VALUE)drop_gvl_guard(httpPostInternal, &args, 0, 0);
|
||||
#else
|
||||
return httpPostInternal(&args);
|
||||
#endif
|
||||
|
@ -170,16 +162,14 @@ void* httpPostBodyInternal(void *args) {
|
|||
const char *reqbody = ((httpPostBodyInternalArgs*)args)->body;
|
||||
const char *reqctype = ((httpPostBodyInternalArgs*)args)->ctype;
|
||||
|
||||
GUARD_EXC(
|
||||
mkxp_net::HTTPResponse res = req->post(reqbody, reqctype);
|
||||
ret = formResponse(res);
|
||||
);
|
||||
mkxp_net::HTTPResponse res = req->post(reqbody, reqctype);
|
||||
ret = formResponse(res);
|
||||
|
||||
return (void*)ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
RB_METHOD(httpPostBody) {
|
||||
RB_METHOD_GUARD(httpPostBody) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
VALUE path, body, ctype, rheaders;
|
||||
|
@ -197,11 +187,12 @@ RB_METHOD(httpPostBody) {
|
|||
|
||||
httpPostBodyInternalArgs args {&req, RSTRING_PTR(body), RSTRING_PTR(ctype)};
|
||||
#if RAPI_MAJOR >= 2
|
||||
return (VALUE)rb_thread_call_without_gvl(httpPostBodyInternal, &args, 0, 0);
|
||||
return (VALUE)drop_gvl_guard(httpPostBodyInternal, &args, 0, 0);
|
||||
#else
|
||||
return httpPostBodyInternal(&args);
|
||||
#endif
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
VALUE json2rb(json5pp::value const &v) {
|
||||
if (v.is_null())
|
||||
|
|
|
@ -37,13 +37,14 @@ RB_METHOD(inputDelta) {
|
|||
return rb_float_new(shState->input().getDelta());
|
||||
}
|
||||
|
||||
RB_METHOD(inputUpdate) {
|
||||
RB_METHOD_GUARD(inputUpdate) {
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
shState->input().update();
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
RB_METHOD_GUARD_END
|
||||
|
||||
static int getButtonArg(VALUE *argv) {
|
||||
int num;
|
||||
|
|
|
@ -1609,6 +1609,13 @@ void Graphics::reset() {
|
|||
|
||||
setFrameRate(DEF_FRAMERATE);
|
||||
setBrightness(255);
|
||||
|
||||
// Always update at least once to clear the screen
|
||||
if (p->threadData->rqResetFinish)
|
||||
update();
|
||||
else
|
||||
repaintWait(p->threadData->rqResetFinish, false);
|
||||
p->threadData->rqReset.clear();
|
||||
}
|
||||
|
||||
void Graphics::center() {
|
||||
|
|
|
@ -800,7 +800,9 @@ void EventThread::showMessageBox(const char *body, int flags)
|
|||
SDL_PushEvent(&event);
|
||||
|
||||
/* Keep repainting screen while box is open */
|
||||
shState->graphics().repaintWait(msgBoxDone);
|
||||
try{
|
||||
shState->graphics().repaintWait(msgBoxDone);
|
||||
}catch(...){}
|
||||
/* Prevent endless loops */
|
||||
resetInputStates();
|
||||
}
|
||||
|
|
|
@ -286,7 +286,9 @@ struct RGSSThreadData
|
|||
scale(scalingFactor),
|
||||
config(newconf),
|
||||
glContext(ctx)
|
||||
{}
|
||||
{
|
||||
rqResetFinish.set();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EVENTTHREAD_H
|
||||
|
|
|
@ -31,6 +31,7 @@ struct Exception
|
|||
enum Type
|
||||
{
|
||||
RGSSError,
|
||||
Reset,
|
||||
NoFileError,
|
||||
IOError,
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue