LuaYard/LuaEngine.cpp

744 lines
17 KiB
C++
Raw Normal View History

2019-08-06 20:36:04 +08:00
#include "LuaEngine.h"
#include <iostream>
#include <string>
#include <vector>
#include "SDL2/include/SDL.h"
#include "SDL2/include/SDL_mixer.h"
#include "SDL2/include/SDL_ttf.h"
#include "SDL2/include/SDL_image.h"
using namespace std;
void check(lua_State* L, const vector<int>& typearr)
{
int sz = typearr.size();
for (int i = 0; i < sz; i++)
{
if (lua_type(L, i + 1) != typearr[i])
{
2019-08-07 03:34:48 +08:00
luaL_error(L, "Bad argument #%d. %s expected, got %s", i + 1, lua_typename(L, typearr[i]), lua_typename(L, lua_type(L, i + 1)));
2019-08-06 20:36:04 +08:00
}
}
}
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;
}
#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 });
2019-08-07 03:34:48 +08:00
cout << "In Window::close" << endl;
2019-08-06 20:36:04 +08:00
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 start(lua_State* L)
{
check(L, { LUA_TUSERDATA });
Window* p = (Window*)lua_touserdata(L, 1);
lua_settop(L, 1);
lua_pushlightuserdata(L, p);
lua_gettable(L, LUA_REGISTRYINDEX);
bool running = true;
SDL_Event e;
while (running && SDL_WaitEvent(&e))
{
switch (e.type)
{
case SDL_MOUSEBUTTONDOWN:
{
if (lua_getfield(L, -1, "onclick") != LUA_TNIL)
{
lua_pushnumber(L, e.button.x);
lua_pushnumber(L, e.button.y);
lua_call(L, 2, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case SDL_QUIT:
{
if (lua_getfield(L, -1, "onquit") != LUA_TNIL)
{
lua_call(L, 0, LUA_MULTRET);
}
else lua_pop(L, 1);
running = 0;
break;
}
case SDL_KEYDOWN:
{
if (lua_getfield(L, -1, "onkeydown") != LUA_TNIL)
{
lua_pushnumber(L, e.key.keysym.sym);
lua_call(L, 1, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case SDL_USEREVENT:
{
switch (e.user.code)
{
case 1001:
{
LuaTimerCallbackData* pd = (LuaTimerCallbackData* )e.user.data1;
lua_pushlightuserdata(L, pd->p);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushfstring(L, "tmcb%d", pd->id);
lua_gettable(L, -2);
delete pd;
lua_call(L, 0, LUA_MULTRET);
lua_pop(L, 1);
break;
}
}
break;
}
}
}
return 0;
}
static int set_event_handler(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TFUNCTION });
Window* p = (Window*)lua_touserdata(L, 1);
lua_pushlightuserdata(L, p);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushfstring(L, "on%s", lua_tostring(L, 2));
lua_pushvalue(L, 3);
lua_settable(L, -3);
return 0;
}
static int setTimeout(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TFUNCTION, LUA_TNUMBER }); // ms
Window* p = (Window*)lua_touserdata(L, 1);
LuaTimerCallbackData* pd = new LuaTimerCallbackData();
pd->p = p;
pd->type = 0;
pd->id = SDL_AddTimer(lua_tointeger(L, 3), LuaTimerCallbackGate, pd);
lua_pushlightuserdata(L, p);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushfstring(L, "tmcb%d", pd->id);
lua_pushvalue(L, 2);
lua_settable(L, -3);
return 0;
}
static int create(lua_State* L)
{
check(L, { LUA_TSTRING, LUA_TNUMBER, LUA_TNUMBER });
2019-08-07 03:34:48 +08:00
cout << "In Window::create" << endl;
2019-08-06 20:36:04 +08:00
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, start, "start");
setfn(L, setTimeout, "setTimeout");
// 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_SHOWN);
return 1;
}
};
2019-08-07 03:34:48 +08:00
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
{
// <20>˺<EFBFBD><CBBA><EFBFBD><EFBFBD><EFBFBD>Renderer<65><72>C<EFBFBD><43>ֱ<EFBFBD>ӵ<EFBFBD><D3B5><EFBFBD>. <20><><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>Ҫ<EFBFBD>ȷ<EFBFBD>һ<EFBFBD><D2BB>renderer userdata<74><61>lightuserdata<74><61>ջ<EFBFBD><D5BB>, ֵ<><D6B5><EFBFBD>ǵ<EFBFBD>ǰ<EFBFBD><C7B0>SDL_Texture.
// <20><><EFBFBD>ú<EFBFBD>ջ<EFBFBD>ϻ<EFBFBD><CFBB><EFBFBD><EFBFBD><EFBFBD>Texture<72><65>userdata.
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Lua<75><61>û<EFBFBD><C3BB>ֱ<EFBFBD>Ӵ<EFBFBD><D3B4><EFBFBD>Texture<72>ķ<EFBFBD><C4B7><EFBFBD>.
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); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Rendere userdata
// stack balance
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>lightuserdata
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>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); // ɾ<><C9BE>lightuserdata
}
};
2019-08-06 20:36:04 +08:00
struct Renderer
{
SDL_Renderer* rnd;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
2019-08-07 03:34:48 +08:00
cout << "In Renderer::close" << endl;
2019-08-06 20:36:04 +08:00
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;
}
2019-08-07 03:34:48 +08:00
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* p = (Renderer*)lua_touserdata(L, 1);
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;
}
2019-08-06 20:36:04 +08:00
static int create(lua_State* L)
{
check(L, { LUA_TUSERDATA });
2019-08-07 03:34:48 +08:00
cout << "In Renderer::create" << endl;
2019-08-06 20:36:04 +08:00
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");
2019-08-07 03:34:48 +08:00
setfn(L, clear, "clear");
setfn(L, loadTexture, "loadTexture");
setfn(L, copy, "copy");
setfn(L, copyTo, "copyTo");
2019-08-06 20:36:04 +08:00
// 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);
2019-08-07 03:34:48 +08:00
// <20>󶨵<EFBFBD>ǰRenderer<65>Ķ<EFBFBD><C4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><>ֹSDL_Window<6F><77><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>.
lua_pushvalue(L, 1);
lua_setuservalue(L, -2);
2019-08-06 20:36:04 +08:00
// Internal logic
p->rnd = SDL_CreateRenderer(static_cast<Window*>(lua_touserdata(L, 1))->wnd, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
return 1;
}
};
2019-08-07 03:34:48 +08:00
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, "color") == 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;
}
}
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;
}
};
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 play(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 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, play, "play");
// 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;
}
};
2019-08-06 20:36:04 +08:00
#define reg_in_lua(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setglobal(L, name)
2019-08-07 03:34:48 +08:00
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());
}
}
2019-08-06 20:36:04 +08:00
int InitEngine(lua_State* L)
{
2019-08-07 03:34:48 +08:00
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);
2019-08-06 20:36:04 +08:00
lua_newtable(L);
reg_in_lua(L, Window::create, "Window");
reg_in_lua(L, Renderer::create, "Renderer");
2019-08-07 03:34:48 +08:00
reg_in_lua(L, Font::create, "Font");
reg_in_lua(L, Music::create, "Music");
reg_in_lua(L, MusicPlayer::create, "MusicPlayer");
2019-08-06 20:36:04 +08:00
return 0;
}