LuaYard/LuaEngine.cpp
2019-08-07 14:15:01 +08:00

777 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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"
#include "PlatAPI.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])
{
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)));
}
}
}
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 });
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 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 });
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, 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;
}
};
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
}
};
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 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;
}
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, 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);
// <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);
// 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;
}
};
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;
}
};
struct LibFS
{
static int listdir(lua_State* L)
{
check(L, { LUA_TSTRING });
vector<FileInfo> 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;
}
};
#define reg_in_lua(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setglobal(L, name)
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());
}
}
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);
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, Music::create, "Music");
reg_in_lua(L, MusicPlayer::create, "MusicPlayer");
LibFS::create(L); lua_setglobal(L, "fs");
return 0;
}