#include "LuaEngine.h" #include #include #include #include #include #include "SDL2/include/SDL.h" #include "SDL2/include/SDL_mixer.h" #include "SDL2/include/SDL_ttf.h" #include "SDL2/include/SDL_image.h" #include "PlatAPI.h" #include "LuaEnum.h" #include "LuaCommon.h" #include "LuaNetwork.h" using namespace std; // Global single-thread unprotected event callback id counter int callback_counter = 1; // Generate SDL error on lua stack. // NOTE: Never returns. If this function is used in C++ context, you should compile Lua as C++. void LuaSDLError(lua_State* L) { luaL_error(L, "SDLError: %s", SDL_GetError()); } void ConvertMusicAsyncImpl(const string& filename, const string& targetname, int ticket) { string command = "bin\\ffmpeg.exe -y -loglevel error -hide_banner -i \""s + filename + "\" \"" + targetname + "\""; cout << command << endl; system(command.c_str()); SDL_Event e; e.type = SDL_USEREVENT; e.user.code = 1002; // General zero ret callback. e.user.data1 = new int(ticket); e.user.data2 = nullptr; SDL_PushEvent(&e); } static int ConvertMusicAsync(lua_State* L) { check(L, { LUA_TSTRING, LUA_TSTRING, LUA_TFUNCTION }); lua_pushfstring(L, "cb%d", callback_counter); lua_pushvalue(L, 3); lua_settable(L, LUA_REGISTRYINDEX); string filename(lua_tostring(L, 1)); string targetname(lua_tostring(L, 2)); thread td(ConvertMusicAsyncImpl, filename, targetname, callback_counter); td.detach(); lua_pushnumber(L, callback_counter); callback_counter++; return 1; } struct LuaTimerCallbackData { void* p; int id; int type; // 0=setTimeout, 1=setInterval }; Uint32 LuaTimerCallbackGate(Uint32 interval, void* param) { SDL_Event e; e.type = SDL_USEREVENT; e.user.code = 1001; e.user.data1 = param; SDL_PushEvent(&e); return ((LuaTimerCallbackData*)(param))->type ? interval : 0; } int GetMouseState(const char** output, Uint32 state) { int i = 0; if (state & SDL_BUTTON_LMASK) { output[i++] = "left"; } if (state & SDL_BUTTON_MMASK) { output[i++] = "middle"; } if (state & SDL_BUTTON_RMASK) { output[i++] = "right"; } if (state & SDL_BUTTON_X1MASK) { output[i++] = "x1"; } if (state & SDL_BUTTON_X2MASK) { output[i++] = "x2"; } return i; } int LuaColorToColor(lua_State* L, int index, SDL_Color& c) { if (lua_getfield(L, index, "type") != LUA_TSTRING) { lua_pop(L, 1); return -1; } const char* type = lua_tostring(L, -1); if (strcmp(type, "rgba") == 0) { lua_pop(L, 1); lua_getfield(L, index, "r"); c.r = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, index, "g"); c.g = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, index, "b"); c.b = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, index, "a"); c.a = lua_tointeger(L, -1); lua_pop(L, 1); return 0; } else { return -1; } } #define setfn(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setfield(L, -2, name) struct Window { SDL_Window* wnd; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Window::close" << endl; Window* p = (Window*)lua_touserdata(L, 1); if (p->wnd) { // release data table lua_pushlightuserdata(L, p); lua_pushnil(L); lua_settable(L, LUA_REGISTRYINDEX); SDL_DestroyWindow(p->wnd); p->wnd = nullptr; } return 0; } static int set_event_handler(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TFUNCTION }, { "window" }); if (strcmp(lua_tostring(L, 2), "quit") == 0) // quit事件特殊处理 { lua_pushlightuserdata(L, &StartEngine); lua_gettable(L, LUA_REGISTRYINDEX); lua_getfield(L, -1, "onquit"); lua_pushvalue(L, 3); lua_setfield(L, -3, "onquit"); return 1; // return old callback. } Window* p = (Window*)lua_touserdata(L, 1); if (p->wnd) { lua_pushlightuserdata(L, p->wnd); if (lua_gettable(L, LUA_REGISTRYINDEX) != LUA_TTABLE) { lua_pop(L, 1); lua_pushlightuserdata(L, p->wnd); lua_newtable(L); lua_pushfstring(L, "on%s", lua_tostring(L, 2)); lua_pushvalue(L, 3); lua_settable(L, -3); lua_settable(L, LUA_REGISTRYINDEX); lua_pushnil(L); // No old callback. Will return empty. } else { lua_pushfstring(L, "on%s", lua_tostring(L, 2)); lua_pushvalue(L, -1); // window, eventName, callback, windowCBTable, onEvent, onEvent lua_gettable(L, -3); // window, eventName, callback, windowCBTable, onEvent, oldCallback lua_pushvalue(L, -2); lua_pushvalue(L, 3); // window, eventName, callback, windowCBTable, onEvent, oldCallback, onEvent, callback lua_settable(L, -5); // window, eventName, callback, windowCBTable, onEvent, oldCallback } return 1; } else { return luaL_error(L, "Unable to add event listener to null window."); } } static int showWindow(lua_State* L) { check(L, { LUA_TUSERDATA }); Window* p = (Window*)lua_touserdata(L, 1); SDL_ShowWindow(p->wnd); return 0; } static int hideWindow(lua_State* L) { check(L, { LUA_TUSERDATA }); Window* p = (Window*)lua_touserdata(L, 1); SDL_HideWindow(p->wnd); return 0; } static int create(lua_State* L) { check(L, { LUA_TSTRING, LUA_TNUMBER, LUA_TNUMBER }); cout << "In Window::create" << endl; Window* p = (Window*) lua_newuserdata(L, sizeof(Window)); // Data table (for storing lua values) lua_pushlightuserdata(L, p); lua_newtable(L); lua_settable(L, LUA_REGISTRYINDEX); // Metatable lua_getfield(L, LUA_REGISTRYINDEX, "__window_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "window"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, set_event_handler, "on"); setfn(L, showWindow, "show"); setfn(L, hideWindow, "hide"); // Binding lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__window_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__window_mt"); } lua_setmetatable(L, -2); // Internal logic p->wnd = SDL_CreateWindow(lua_tostring(L, 1), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, lua_tointeger(L, 2), lua_tointeger(L, 3), SDL_WINDOW_HIDDEN); return 1; } }; struct Texture { SDL_Texture* text; int w; int h; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Texture::close" << endl; Texture* t = (Texture*)lua_touserdata(L, 1); if (t->text) { SDL_DestroyTexture(t->text); t->text = nullptr; } return 0; } static void create(lua_State* L) // -2, +1 { // 此函数由Renderer在C层直接调用. 调用前需要先放一个renderer userdata和lightuserdata在栈上, 值就是当前的SDL_Texture. // 调用后栈上会留下Texture的userdata. // 因此在Lua层没有直接创建Texture的方法. cout << "In Texture::create" << endl; Texture* p = (Texture*)lua_newuserdata(L, sizeof(Texture)); // Metatable lua_getfield(L, LUA_REGISTRYINDEX, "__texture_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "texture"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); // Binding lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__texture_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__texture_mt"); } lua_setmetatable(L, -2); p->text = (SDL_Texture * )lua_touserdata(L, -2); SDL_QueryTexture(p->text, NULL, NULL, &p->w, &p->h); lua_pushvalue(L, -3); lua_setuservalue(L, -2); // 绑定这个Rendere userdata // stack balance lua_remove(L, lua_gettop(L) - 1); // 删除lightuserdata lua_remove(L, lua_gettop(L) - 1); // 删除rendere userdata } }; int LuaRectPointToRect(lua_State* L, int index, SDL_Rect& rect) { if (lua_getfield(L, index, "type") != LUA_TSTRING) { lua_pop(L, 1); return -1; } const char* type = lua_tostring(L, -1); if (strcmp(type, "point") == 0) { lua_pop(L, 1); lua_getfield(L, index, "x"); rect.x = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, index, "y"); rect.y = lua_tointeger(L, -1); lua_pop(L, 1); return 1; } else if (strcmp(type, "rect") == 0) { lua_pop(L, 1); lua_getfield(L, index, "x"); rect.x = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, index, "y"); rect.y = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, index, "w"); rect.w = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, index, "h"); rect.h = lua_tointeger(L, -1); lua_pop(L, 1); return 0; } else { return -1; } } struct Surface { SDL_Surface* surf; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Surface::close" << endl; Surface* p = (Surface*)lua_touserdata(L, 1); if (p->surf) { SDL_FreeSurface(p->surf); p->surf = nullptr; } return 0; } static void create(lua_State* L) // -1, +1 { cout << "In Surface::create" << endl; Surface* p = (Surface*)lua_newuserdata(L, sizeof(Surface)); // Metatable lua_getfield(L, LUA_REGISTRYINDEX, "__surface_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "surface"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); // Binding lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__surface_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__surface_mt"); } lua_setmetatable(L, -2); p->surf = (SDL_Surface*)lua_touserdata(L, -2); // stack balance lua_remove(L, lua_gettop(L) - 1); // 删除lightuserdata } }; struct Renderer { SDL_Renderer* rnd; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Renderer::close" << endl; Renderer* p = (Renderer*)lua_touserdata(L, 1); if (p->rnd) { SDL_DestroyRenderer(p->rnd); p->rnd = nullptr; } return 0; } static int update(lua_State* L) { check(L, { LUA_TUSERDATA }); Renderer* p = (Renderer*)lua_touserdata(L, 1); SDL_RenderPresent(p->rnd); return 0; } static int clear(lua_State* L) { check(L, { LUA_TUSERDATA }); Renderer* p = (Renderer*)lua_touserdata(L, 1); SDL_RenderClear(p->rnd); return 0; } static int setColor(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TTABLE }); Renderer* p = (Renderer*)lua_touserdata(L, 1); SDL_Color c; if (LuaColorToColor(L, 2, c) < 0) { luaL_error(L, "Bad argument #%d. Color expected, got %s", 2, lua_typename(L, lua_type(L, 2))); } SDL_SetRenderDrawColor(p->rnd, c.r, c.g, c.b, c.a); return 0; } static int getColor(lua_State* L) { check(L, { LUA_TUSERDATA }); Renderer* p = (Renderer*)lua_touserdata(L, 1); Uint8 r, b, g, a; SDL_GetRenderDrawColor(p->rnd, &r, &g, &b, &a); lua_newtable(L); lua_pushinteger(L, r); lua_setfield(L, -2, "r"); lua_pushinteger(L, g); lua_setfield(L, -2, "g"); lua_pushinteger(L, b); lua_setfield(L, -2, "b"); lua_pushinteger(L, a); lua_setfield(L, -2, "a"); lua_pushstring(L, "rgba"); lua_setfield(L, -2, "type"); return 1; } static int drawRect(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TTABLE }); Renderer* p = (Renderer*)lua_touserdata(L, 1); SDL_Rect r; if (LuaRectPointToRect(L, 2, r) < 0) { luaL_error(L, "Bad argument #%d. Rect expected, got %s", 2, lua_typename(L, lua_type(L, 2))); } SDL_RenderDrawRect(p->rnd, &r); return 0; } static int fillRect(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TTABLE }); Renderer* p = (Renderer*)lua_touserdata(L, 1); SDL_Rect r; if (LuaRectPointToRect(L, 2, r) < 0) { luaL_error(L, "Bad argument #%d. Rect expected, got %s", 2, lua_typename(L, lua_type(L, 2))); } SDL_RenderFillRect(p->rnd, &r); return 0; } static int loadTexture(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TSTRING }); Renderer* p = (Renderer*)lua_touserdata(L, 1); const char* filepath = lua_tostring(L, 2); SDL_Texture* text = IMG_LoadTexture(p->rnd, filepath); lua_pushvalue(L, 1); lua_pushlightuserdata(L, text); Texture::create(L); return 1; } static int copy(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE, LUA_TTABLE }); Renderer* r = (Renderer*)lua_touserdata(L, 1); Texture* t = (Texture*)lua_touserdata(L, 2); SDL_Rect src; int ret = LuaRectPointToRect(L, 3, src); if (ret <= 0) { luaL_error(L, "Bad argument #%d. Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3))); } SDL_Rect dst; ret = LuaRectPointToRect(L, 4, dst); if (ret <= 0) { luaL_error(L, "Bad argument #%d. Rect expected, got %s", 4, lua_typename(L, lua_type(L, 4))); } SDL_RenderCopy(r->rnd, t->text, &src, &dst); return 0; } static int copyTo(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE }); Renderer* r = (Renderer*)lua_touserdata(L, 1); Texture* t = (Texture*)lua_touserdata(L, 2); SDL_Rect rect; int ret = LuaRectPointToRect(L, 3, rect); if (ret < 0) { luaL_error(L, "Bad argument #%d. Point or Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3))); } if (ret == 1) { rect.w = t->w; rect.h = t->h; } SDL_RenderCopy(r->rnd, t->text, NULL, &rect); return 0; } static int copyFill(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE }); Renderer* r = (Renderer*)lua_touserdata(L, 1); Texture* t = (Texture*)lua_touserdata(L, 2); SDL_Rect rect; int ret = LuaRectPointToRect(L, 3, rect); if (ret < 0) { luaL_error(L, "Bad argument #%d. Point or Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3))); } if (ret == 1) { rect.w = t->w; rect.h = t->h; } SDL_RenderCopy(r->rnd, t->text, &rect, NULL); return 0; } static int create(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Renderer::create" << endl; Renderer* p = (Renderer*)lua_newuserdata(L, sizeof(Renderer)); lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "renderer"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, update, "update"); setfn(L, clear, "clear"); setfn(L, setColor, "setColor"); setfn(L, getColor, "getColor"); setfn(L, drawRect, "drawRect"); setfn(L, fillRect, "fillRect"); setfn(L, loadTexture, "loadTexture"); setfn(L, copy, "copy"); setfn(L, copyTo, "copyTo"); // Set __index of metatable. lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); } lua_setmetatable(L, -2); // 绑定当前Renderer的对象到自身, 防止SDL_Window被提前销毁. lua_pushvalue(L, 1); lua_setuservalue(L, -2); // Internal logic p->rnd = SDL_CreateRenderer(static_cast(lua_touserdata(L, 1))->wnd, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE); return 1; } }; struct Font { TTF_Font* font; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Font::close" << endl; Font* f = (Font*)lua_touserdata(L, 1); if (f->font) { TTF_CloseFont(f->font); f->font = nullptr; } return 0; } static int renderText(lua_State* L) { Renderer* r = nullptr; Font* f = nullptr; const char* text = nullptr; SDL_Color c; if (lua_gettop(L) >= 4) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE }); f = (Font*)lua_touserdata(L, 1); r = (Renderer*)lua_touserdata(L, 2); text = lua_tostring(L, 3); if (LuaColorToColor(L, 4, c) < 0) { luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4))); } } else { check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE }); f = (Font*)lua_touserdata(L, 1); text = lua_tostring(L, 3); if (LuaColorToColor(L, 4, c) < 0) { luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4))); } } SDL_Surface* surf = TTF_RenderText_Blended(f->font, text, c); if (r) { SDL_Texture* text = SDL_CreateTextureFromSurface(r->rnd, surf); SDL_FreeSurface(surf); lua_pushvalue(L, 2); lua_pushlightuserdata(L, text); Texture::create(L); } else { lua_pushlightuserdata(L, surf); Surface::create(L); } return 1; } static int create(lua_State* L) { check(L, { LUA_TSTRING, LUA_TNUMBER }); cout << "In Font::create" << endl; Font* f = (Font*)lua_newuserdata(L, sizeof(Font)); lua_getfield(L, LUA_REGISTRYINDEX, "__font_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "font"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, renderText, "renderText"); // Set __index of metatable. lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__font_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__font_mt"); } lua_setmetatable(L, -2); const char* filename = lua_tostring(L, 1); int fontsz = lua_tointeger(L, 2); f->font = TTF_OpenFont(filename, fontsz); return 1; } }; Mix_Chunk* Mix_LoadWAVEx(const char* filepath) { SDL_RWops* fileData = SDL_RWFromFile(filepath, "rb"); if (!fileData) { std::cout << "Mix_LoadWAVEx() failed to call SDL_RWFromFile().\n\t\"" << SDL_GetError() << "\"" << std::endl; return NULL; } Mix_Chunk* chunk = Mix_LoadWAV_RW(fileData, 1); if (!chunk) { std::cout << "Mix_LoadWAVEx() failed to call Mix_LoadWAV_RW().\n\t\"" << SDL_GetError() << "\"" << std::endl; return NULL; } return chunk; } struct Chunk { Mix_Chunk* chunk; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Chunk::close" << endl; Chunk* c = (Chunk*)lua_touserdata(L, 1); if (c->chunk) { Mix_FreeChunk(c->chunk); c->chunk = nullptr; } return 0; } static int setVolume(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); Chunk* c = (Chunk*)lua_touserdata(L, 1); Mix_VolumeChunk(c->chunk, lua_tointeger(L, 2)); return 0; } static int create(lua_State* L) { check(L, { LUA_TSTRING }); cout << "In Chunk::create" << endl; Chunk* c = (Chunk*)lua_newuserdata(L, sizeof(Chunk)); lua_getfield(L, LUA_REGISTRYINDEX, "__chunk_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "chunk"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, setVolume, "setVolumne"); // Set __index of metatable. lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__chunk_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__chunk_mt"); } lua_setmetatable(L, -2); const char* filename = lua_tostring(L, 1); cout << "LoadWAV: " << filename << endl; c->chunk = Mix_LoadWAVEx(filename); cout << "Load chunk error: " << SDL_GetError() << endl; return 1; } }; struct Music { Mix_Music* music; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In Music::close" << endl; Music* m = (Music*)lua_touserdata(L, 1); if (m->music) { Mix_FreeMusic(m->music); m->music = nullptr; } return 0; } static int create(lua_State* L) { check(L, { LUA_TSTRING }); cout << "In Music::create" << endl; Music* m = (Music*)lua_newuserdata(L, sizeof(Music)); lua_getfield(L, LUA_REGISTRYINDEX, "__music_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "music"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); // Set __index of metatable. lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__music_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__music_mt"); } lua_setmetatable(L, -2); const char* filename = lua_tostring(L, 1); m->music = Mix_LoadMUS(filename); cout << "Load music error: " << SDL_GetError() << endl; return 1; } }; struct MusicPlayer { static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In MusicPlayer::close" << endl; return 0; } static int playMusic(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Music* m = (Music*)lua_touserdata(L, 2); cout << "Play music ret: " << Mix_PlayMusic(m->music, 1) << " " << SDL_GetError() << endl; return 0; } static int resumeMusic(lua_State* L) { check(L, { LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_ResumeMusic(); return 0; } static int stopMusic(lua_State* L) { check(L, { LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_HaltMusic(); return 0; } static int pauseMusic(lua_State* L) { check(L, { LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_PauseMusic(); return 0; } static int rewindMusic(lua_State* L) { check(L, { LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_RewindMusic(); return 0; } static int fadeInMusic(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA , LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Music* m = (Music*)lua_touserdata(L, 2); cout << "Fade in music ret: " << Mix_FadeInMusic(m->music, 1, lua_tointeger(L, 3)) << " " << SDL_GetError() << endl; return 0; } static int fadeInMusicPos(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA , LUA_TNUMBER, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Music* m = (Music*)lua_touserdata(L, 2); cout << "Fade in music ret: " << Mix_FadeInMusicPos(m->music, 1, lua_tointeger(L, 3), lua_tonumber(L, 4)) << " " << SDL_GetError() << endl; return 0; } static int fadeOutMusic(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); cout << "Fade out music ret: " << Mix_FadeOutMusic(lua_tointeger(L, 2)) << " " << SDL_GetError() << endl; return 0; } static int setMusicVolume(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_VolumeMusic(lua_tointeger(L, 2)); return 0; } static int setMusicPos(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_SetMusicPosition(lua_tonumber(L, 2)); return 0; } static int isPlayingMusic(lua_State* L) { check(L, { LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); lua_pushboolean(L, Mix_PlayingMusic()); return 1; } static int isPausedMusic(lua_State* L) { check(L, { LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); lua_pushboolean(L, Mix_PausedMusic()); return 1; } static int isFadingMusic(lua_State* L) { check(L, { LUA_TUSERDATA }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); int ret = 0; switch (Mix_FadingMusic()) { case MIX_NO_FADING: ret = 0; break; case MIX_FADING_OUT: ret = -1; break; case MIX_FADING_IN: return 1; } lua_pushinteger(L, ret); return 1; } static int playChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER}); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Chunk* c = (Chunk*)lua_touserdata(L, 2); cout << "Play channel ret: " << Mix_PlayChannel(lua_tointeger(L, 3), c->chunk, lua_tointeger(L, 4)) << " " << SDL_GetError() << endl; return 0; } static int fadeInChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Chunk* c = (Chunk*)lua_touserdata(L, 2); Mix_FadeInChannel(lua_tointeger(L, 3), c->chunk, lua_tointeger(L, 4), lua_tointeger(L, 5)); return 0; } static int fadeOutChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_FadeOutChannel(lua_tointeger(L, 2), lua_tointeger(L, 3)); return 0; } static int stopChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_HaltChannel(lua_tointeger(L, 2)); return 0; } static int resumeChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_Resume(lua_tointeger(L, 2)); return 0; } static int pauseChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_Pause(lua_tointeger(L, 2)); return 0; } static int isPlayingChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); lua_pushinteger(L, Mix_Playing(lua_tointeger(L, 2))); return 1; } static int isPausedChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); lua_pushinteger(L, Mix_Paused(lua_tointeger(L, 2))); return 1; } static int isFadingChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); int ret = 0; switch (Mix_FadingChannel(lua_tointeger(L, 2))) { case MIX_NO_FADING: ret = 0; break; case MIX_FADING_OUT: ret = -1; break; case MIX_FADING_IN: return 1; } lua_pushinteger(L, ret); return 1; } static int setChannelVolume(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_Volume(lua_tointeger(L, 2), lua_tointeger(L, 3)); return 0; } static int setChannelDistance(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_SetDistance(lua_tointeger(L, 2), lua_tointeger(L, 3)); return 0; } static int setChannelPosition(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Sint16 angle = lua_tonumber(L, 3); unsigned int distance = lua_tonumber(L, 4); cout << "Mix_SetPosition " << lua_tointeger(L, 2) << " " << angle << " " << distance << endl; if (0 == Mix_SetPosition(lua_tointeger(L, 2), angle, distance)) { LuaSDLError(L); } return 0; } static int setChannelPanning(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_SetPanning(lua_tointeger(L, 2), lua_tointeger(L, 3), lua_tointeger(L, 4)); return 0; } static int setTotalChannel(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TNUMBER }); MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); Mix_AllocateChannels(lua_tointeger(L, 2)); return 0; } static int create(lua_State* L) { cout << "In MusicPlayer::create" << endl; MusicPlayer* m = (MusicPlayer*)lua_newuserdata(L, sizeof(MusicPlayer)); lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "musicplayer"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, playMusic, "playMusic"); setfn(L, resumeMusic, "resumeMusic"); setfn(L, pauseMusic, "pauseMusic"); setfn(L, stopMusic, "stopMusic"); setfn(L, rewindMusic, "rewindMusic"); setfn(L, fadeInMusic, "fadeInMusic"); setfn(L, fadeOutMusic, "fadeOutMusic"); setfn(L, fadeInMusicPos, "fadeInMusicPos"); setfn(L, setMusicVolume, "setMusicVolume"); setfn(L, setMusicPos, "setMusicPos"); setfn(L, isPlayingMusic, "isPlayingMusic"); setfn(L, isPausedMusic, "isPausedMusic"); setfn(L, isFadingMusic, "isFadingMusic"); setfn(L, playChannel, "playChannel"); setfn(L, resumeChannel, "resumeChannel"); setfn(L, pauseChannel, "pauseChannel"); setfn(L, stopChannel, "stopChannel"); setfn(L, fadeInChannel, "fadeInChannel"); setfn(L, fadeOutChannel, "fadeOutChannel"); setfn(L, setChannelVolume, "setChannelVolume"); setfn(L, setChannelDistance, "setChannelDistance"); setfn(L, setChannelPosition, "setChannelPosition"); setfn(L, setChannelPanning, "setChannelPanning"); setfn(L, setTotalChannel, "setTotalChannel"); setfn(L, isPlayingChannel, "isPlayingChannel"); setfn(L, isPausedChannel, "isPausedChannel"); setfn(L, isFadingChannel, "isFadingChannel"); // Set __index of metatable. lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt"); } lua_setmetatable(L, -2); return 1; } }; struct LibFS { static int listdir(lua_State* L) { check(L, { LUA_TSTRING }); vector vec = ListDir(lua_tostring(L, 1)); int sz = vec.size(); lua_newtable(L); for (lua_Integer i = 0; i < sz; i++) { const FileInfo& info = vec[i]; lua_newtable(L); lua_pushstring(L, info.name.c_str()); lua_setfield(L, -2, "name"); lua_pushstring(L, info.type ? "dir" : "file"); lua_setfield(L, -2, "type"); lua_pushinteger(L, info.size); lua_setfield(L, -2, "size"); lua_seti(L, -2, i + 1); } return 1; } static int create(lua_State* L) { lua_newtable(L); setfn(L, listdir, "listdir"); return 1; } }; void check_init(lua_State* L, const char* name, int ret, int expected = 0) { if (ret != expected) { luaL_error(L, "Failed to initialize %s. ret: %d. expected: %d. SDLError: %s", name, ret, expected, SDL_GetError()); } } void _Global_On_Music_Finished() { SDL_Event e; e.type = SDL_USEREVENT; e.user.code = 1003; SDL_PushEvent(&e); } void _Global_On_Channel_Finished(int channel) { SDL_Event e; e.type = SDL_USEREVENT; e.user.code = 1004; e.user.data1 = new int(channel); SDL_PushEvent(&e); } static int setTimeout(lua_State* L) { check(L, { LUA_TFUNCTION, LUA_TNUMBER }); // setTimeout(cb: function, ms: number) LuaTimerCallbackData* pd = new LuaTimerCallbackData(); pd->p = nullptr; pd->type = 0; pd->id = SDL_AddTimer(lua_tointeger(L, 2), LuaTimerCallbackGate, pd); lua_pushlightuserdata(L, &setTimeout); if (lua_gettable(L, LUA_REGISTRYINDEX) != LUA_TTABLE) { lua_pop(L, 1); lua_pushlightuserdata(L, &setTimeout); lua_newtable(L); lua_pushvalue(L, 1); lua_seti(L, -2, pd->id); // { [timerID] = callback } lua_settable(L, LUA_REGISTRYINDEX); // registry[&setTimeout] = timerCallbackTable } else { lua_pushvalue(L, 1); lua_seti(L, -2, pd->id); // timerCallbackTable[timerID] = callback } return 0; } static int setInterval(lua_State* L) { check(L, { LUA_TFUNCTION, LUA_TNUMBER }); // setTimeout(cb: function, ms: number) LuaTimerCallbackData* pd = new LuaTimerCallbackData(); pd->p = nullptr; pd->type = 1; pd->id = SDL_AddTimer(lua_tointeger(L, 2), LuaTimerCallbackGate, pd); lua_pushlightuserdata(L, &setTimeout); if (lua_gettable(L, LUA_REGISTRYINDEX) != LUA_TTABLE) { lua_pop(L, 1); lua_pushlightuserdata(L, &setTimeout); lua_newtable(L); lua_pushvalue(L, 1); lua_seti(L, -2, pd->id); // { [timerID] = callback } lua_settable(L, LUA_REGISTRYINDEX); // registry[&setTimeout] = timerCallbackTable } else { lua_pushvalue(L, 1); lua_seti(L, -2, pd->id); // timerCallbackTable[timerID] = callback } return 0; } static int evFireExit(lua_State* L) { SDL_Event e; e.type = SDL_QUIT; e.quit.timestamp = SDL_GetTicks(); SDL_PushEvent(&e); return 0; } #define reg_in_lua(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setglobal(L, name) int InitEngine(lua_State* L) { int ret; ret = SDL_Init(SDL_INIT_EVERYTHING); check_init(L, "SDL", ret); ret = IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); check_init(L, "IMG", ret, IMG_INIT_JPG | IMG_INIT_PNG); ret = TTF_Init(); check_init(L, "TTF", ret); ret = Mix_Init(MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG ); check_init(L, "MIX", ret, MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG); ret = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 512); check_init(L, "AUDIO", ret); Mix_AllocateChannels(16); Mix_HookMusicFinished(_Global_On_Music_Finished); Mix_ChannelFinished(_Global_On_Channel_Finished); reg_in_lua(L, Window::create, "Window"); reg_in_lua(L, Renderer::create, "Renderer"); reg_in_lua(L, Font::create, "Font"); reg_in_lua(L, Chunk::create, "Chunk"); reg_in_lua(L, Music::create, "Music"); reg_in_lua(L, MusicPlayer::create, "MusicPlayer"); // Global functions reg_in_lua(L, ConvertMusicAsync, "ConvertMusic"); reg_in_lua(L, setTimeout, "SetTimeout"); reg_in_lua(L, setInterval, "SetInterval"); reg_in_lua(L, evFireExit, "FireExit"); // Global Library LibFS::create(L); lua_setglobal(L, "fs"); InitEnum(L); InitNetwork(L); // Registry table lua_pushlightuserdata(L, &StartEngine); lua_newtable(L); lua_settable(L, LUA_REGISTRYINDEX); return 0; } int LuaRunCallback(lua_State* L, int nargs, int nresults = LUA_MULTRET) { int ret = lua_pcall(L, nargs, nresults, 0); if (ret) { switch (ret) { case LUA_ERRRUN: cout << "LuaRunError: " << lua_tostring(L, -1) << endl; break; case LUA_ERRMEM: cout << "LuaMemError: " << lua_tostring(L, -1) << endl; break; case LUA_ERRERR: cout << "LuaRecursiveError: " << lua_tostring(L, -1) << endl; break; case LUA_ERRGCMM: cout << "LuaGCError: " << lua_tostring(L, -1) << endl; break; default: cout << "LuaError: " << lua_tostring(L, -1) << endl; break; } lua_pop(L, 1); return -1; } return 0; } int StartEngine(lua_State* L) { // The Event Loop bool running = true; SDL_Event e; while (running && SDL_WaitEvent(&e)) { switch (e.type) { case SDL_MOUSEBUTTONDOWN: { SDL_Window* wnd = SDL_GetWindowFromID(e.button.windowID); if (wnd) { lua_pushlightuserdata(L, wnd); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onmousedown") != LUA_TNIL) { lua_pushnumber(L, e.button.x); lua_pushnumber(L, e.button.y); lua_pushnumber(L, e.button.button); const char* arr[8]; int ret = GetMouseState(arr, e.button.state); lua_newtable(L); for (int i = 0; i < ret; i++) { lua_pushboolean(L, 1); lua_setfield(L, -2, arr[i]); } LuaRunCallback(L, 4); } else lua_pop(L, 1); lua_pop(L, 1); } break; } case SDL_MOUSEBUTTONUP: { SDL_Window* wnd = SDL_GetWindowFromID(e.button.windowID); if (wnd) { lua_pushlightuserdata(L, wnd); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onmouseup") != LUA_TNIL) { lua_pushnumber(L, e.button.x); lua_pushnumber(L, e.button.y); lua_pushnumber(L, e.button.button); const char* arr[8]; int ret = GetMouseState(arr, e.button.state); lua_newtable(L); for (int i = 0; i < ret; i++) { lua_pushboolean(L, 1); lua_setfield(L, -2, arr[i]); } LuaRunCallback(L, 4); } else lua_pop(L, 1); lua_pop(L, 1); } break; } case SDL_MOUSEMOTION: { SDL_Window* wnd = SDL_GetWindowFromID(e.motion.windowID); if (wnd) { lua_pushlightuserdata(L, wnd); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onmousemove") != LUA_TNIL) { lua_pushnumber(L, e.motion.x); lua_pushnumber(L, e.motion.y); lua_pushnumber(L, e.motion.xrel); lua_pushnumber(L, e.motion.yrel); const char* arr[8]; int ret = GetMouseState(arr, e.button.state); lua_newtable(L); for (int i = 0; i < ret; i++) { lua_pushboolean(L, 1); lua_setfield(L, -2, arr[i]); } LuaRunCallback(L, 5); } else lua_pop(L, 1); lua_pop(L, 1); } break; } case SDL_QUIT: { running = 0; lua_pushlightuserdata(L, &StartEngine); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onquit") != LUA_TNIL) { if (!LuaRunCallback(L, 0, 1)) { if (lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1)) { running = 1; } lua_pop(L, 1); } } else lua_pop(L, 1); lua_pop(L, 1); break; } case SDL_KEYDOWN: { SDL_Window* wnd = SDL_GetWindowFromID(e.key.windowID); if (wnd) { lua_pushlightuserdata(L, wnd); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onkeydown") != LUA_TNIL) { lua_pushnumber(L, e.key.keysym.sym); LuaRunCallback(L, 1); } else lua_pop(L, 1); lua_pop(L, 1); } break; } case SDL_KEYUP: { SDL_Window* wnd = SDL_GetWindowFromID(e.key.windowID); if (wnd) { lua_pushlightuserdata(L, wnd); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onkeyup") != LUA_TNIL) { lua_pushnumber(L, e.key.keysym.sym); LuaRunCallback(L, 1); } else lua_pop(L, 1); lua_pop(L, 1); } break; } case SDL_USEREVENT: { switch (e.user.code) { case 1001: { // 定时器回调 LuaTimerCallbackData* pd = (LuaTimerCallbackData*)e.user.data1; lua_pushlightuserdata(L, &setTimeout); lua_gettable(L, LUA_REGISTRYINDEX); // registry[&setTimeout] lua_geti(L, -1, pd->id); // registry[&setTimeout][timerID] if (!pd->type) { delete pd; LuaRunCallback(L, 0); } else { if (LuaRunCallback(L, 0, 1) == 0) { if (lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1)) { SDL_RemoveTimer(pd->id); delete pd; } lua_pop(L, 1); } } lua_pop(L, 1); break; } case 1002: { // 全局回调, 异步操作无返回值 lua_pushfstring(L, "cb%d", *(int*)e.user.data1); lua_gettable(L, LUA_REGISTRYINDEX); delete e.user.data1; LuaRunCallback(L, 0); break; } case 1003: // 音乐播放完毕 lua_pushlightuserdata(L, &StartEngine); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onmusicover") != LUA_TNIL) { LuaRunCallback(L, 0); } else lua_pop(L, 1); lua_pop(L, 1); break; } case 1004: { // 频道播放完毕 lua_pushlightuserdata(L, &StartEngine); lua_gettable(L, LUA_REGISTRYINDEX); if (lua_getfield(L, -1, "onchannelover") != LUA_TNIL) { lua_pushinteger(L, *(int*)e.user.data1); delete e.user.data1; LuaRunCallback(L, 1); } else lua_pop(L, 1); lua_pop(L, 1); break; } case 1005: case 1006: { HandleNetworkEvent(L, e); break; } break; } default: { cout << "Unhandled Event: 0x" << hex << e.type << endl; } } } return 0; } int StopEngine() { Mix_CloseAudio(); Mix_Quit(); TTF_Quit(); IMG_Quit(); SDL_Quit(); return 0; }