mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

script.cc (7257B)


      1 #include "common.h"
      2 #include "platform.h"
      3 #include "ui.h"
      4 
      5 #include "platform_time.h"
      6 
      7 #if PLATFORM_WINDOWS
      8 #include "lua/lua.hpp"
      9 #else
     10 #include <lua.hpp>
     11 #endif
     12 
     13 #include "whereami/whereami.h"
     14 
     15 #if LUA_VERSION_NUM < 502
     16 #define luaL_len lua_objlen
     17 #endif
     18 
     19 static constexpr u8 lua_combined[] = {
     20 #include "../build/lua_combined.h"
     21 };
     22 
     23 static lua_State * lua;
     24 
     25 static int inputHandlerIdx = LUA_NOREF;
     26 static int macroHandlerIdx = LUA_NOREF;
     27 static int closeHandlerIdx = LUA_NOREF;
     28 static int socketHandlerIdx = LUA_NOREF;
     29 static int intervalHandlerIdx = LUA_NOREF;
     30 
     31 static void pcall( int args, const char * err ) {
     32 	if( lua_pcall( lua, args, 0, 1 ) ) {
     33 		printf( "%s: %s\n", err, lua_tostring( lua, -1 ) );
     34 		exit( 1 );
     35 	}
     36 
     37 	assert( lua_gettop( lua ) == 1 );
     38 }
     39 
     40 void script_handleInput( const char * buffer, int len ) {
     41 	ZoneScoped;
     42 
     43 	assert( inputHandlerIdx != LUA_NOREF );
     44 
     45 	lua_rawgeti( lua, LUA_REGISTRYINDEX, inputHandlerIdx );
     46 	lua_pushlstring( lua, buffer, len );
     47 
     48 	pcall( 1, "script_handleInput" );
     49 }
     50 
     51 void script_doMacro( const char * key, int len, bool shift, bool ctrl, bool alt ) {
     52 	ZoneScoped;
     53 
     54 	// printf( "macro %s%s%s%.*s\n", shift ? "s" : "", ctrl ? "c" : "", alt ? "a" : "", len, key );
     55 	// fflush( stdout );
     56 
     57 	assert( macroHandlerIdx != LUA_NOREF );
     58 
     59 	lua_rawgeti( lua, LUA_REGISTRYINDEX, macroHandlerIdx );
     60 
     61 	lua_pushlstring( lua, key, len );
     62 
     63 	lua_pushboolean( lua, shift );
     64 	lua_pushboolean( lua, ctrl );
     65 	lua_pushboolean( lua, alt );
     66 
     67 	pcall( 4, "script_doMacro" );
     68 }
     69 
     70 void script_handleClose() {
     71 	ZoneScoped;
     72 
     73 	assert( closeHandlerIdx != LUA_NOREF );
     74 
     75 	lua_rawgeti( lua, LUA_REGISTRYINDEX, closeHandlerIdx );
     76 	pcall( 0, "script_handleClose" );
     77 }
     78 
     79 void script_socketData( void * sock, const char * data, size_t len ) {
     80 	ZoneScoped;
     81 
     82 	assert( socketHandlerIdx != LUA_NOREF );
     83 
     84 	lua_rawgeti( lua, LUA_REGISTRYINDEX, socketHandlerIdx );
     85 
     86 	lua_pushlightuserdata( lua, sock );
     87 	if( data == NULL ) {
     88 		lua_pushnil( lua );
     89 	}
     90 	else {
     91 		lua_pushlstring( lua, data, len );
     92 	}
     93 
     94 	pcall( 2, "script_socketData" );
     95 }
     96 
     97 void script_fire_intervals() {
     98 	ZoneScoped;
     99 
    100 	assert( intervalHandlerIdx != LUA_NOREF );
    101 
    102 	lua_rawgeti( lua, LUA_REGISTRYINDEX, intervalHandlerIdx );
    103 	pcall( 0, "script_fire_intervals" );
    104 }
    105 
    106 namespace {
    107 
    108 template< typename F >
    109 static void generic_print( F * f, lua_State * L ) {
    110 	const char * str = luaL_checkstring( L, 1 );
    111 	size_t len = luaL_len( L, 1 );
    112 
    113 	Colour fg = Colour( luaL_checkinteger( L, 2 ) );
    114 	Colour bg = Colour( luaL_checkinteger( L, 3 ) );
    115 	bool bold = lua_toboolean( L, 4 );
    116 
    117 	f( str, len, fg, bg, bold );
    118 }
    119 
    120 extern "C" int mud_connect( lua_State * L ) {
    121 	const char * host = luaL_checkstring( L, 1 );
    122 	int port = luaL_checkinteger( L, 2 );
    123 
    124 	const char * err;
    125 	void * sock = platform_connect( &err, host, port );
    126 	if( sock != NULL ) {
    127 		lua_pushlightuserdata( lua, sock );
    128 		return 1;
    129 	}
    130 
    131 	lua_pushnil( lua );
    132 	lua_pushstring( lua, err );
    133 
    134 	return 2;
    135 }
    136 
    137 extern "C" int mud_send( lua_State * L ) {
    138 	luaL_argcheck( L, lua_isuserdata( L, 1 ) == 1, 1, "expected socket" );
    139 	void * sock = lua_touserdata( L, 1 );
    140 
    141 	const char * data = luaL_checkstring( L, 2 );
    142 	size_t len = luaL_len( L, 2 );
    143 
    144 	platform_send( sock, data, len );
    145 
    146 	return 0;
    147 }
    148 
    149 extern "C" int mud_close( lua_State * L ) {
    150 	luaL_argcheck( L, lua_isuserdata( L, 1 ) == 1, 1, "expected socket" );
    151 	void * sock = lua_touserdata( L, 1 );
    152 
    153 	platform_close( sock );
    154 
    155 	return 0;
    156 }
    157 
    158 extern "C" int mud_printMain( lua_State * L ) {
    159 	generic_print( ui_main_print, L );
    160 	return 0;
    161 }
    162 
    163 extern "C" int mud_newlineMain( lua_State * L ) {
    164 	ui_main_newline();
    165 	return 0;
    166 }
    167 
    168 extern "C" int mud_printChat( lua_State * L ) {
    169 	generic_print( ui_chat_print, L );
    170 	return 0;
    171 }
    172 
    173 extern "C" int mud_newlineChat( lua_State * L ) {
    174 	ui_chat_newline();
    175 	return 0;
    176 }
    177 
    178 extern "C" int mud_setStatus( lua_State * L ) {
    179 	luaL_argcheck( L, lua_type( L, 1 ) == LUA_TTABLE, 1, "expected function" );
    180 
    181 	size_t len = luaL_len( L, 1 );
    182 
    183 	ui_clear_status();
    184 
    185 	for( size_t i = 0; i < len; i++ ) {
    186 		lua_pushnumber( L, i + 1 );
    187 		lua_gettable( L, 1 );
    188 
    189 		lua_pushliteral( L, "text" );
    190 		lua_gettable( L, 2 );
    191 		size_t seglen;
    192 		const char * str = lua_tolstring( L, -1, &seglen );
    193 
    194 		lua_pushliteral( L, "fg" );
    195 		lua_gettable( L, 2 );
    196 		const Colour fg = Colour( lua_tointeger( L, -1 ) );
    197 
    198 		lua_pushliteral( L, "bold" );
    199 		lua_gettable( L, 2 );
    200 		const bool bold = lua_toboolean( L, -1 );
    201 
    202 		for( size_t j = 0; j < seglen; j++ ) {
    203 			ui_statusAdd( str[ j ], fg, bold );
    204 		}
    205 
    206 		lua_pop( L, 4 );
    207 	}
    208 
    209 	return 0;
    210 }
    211 
    212 extern "C" int mud_setHandlers( lua_State * L ) {
    213 	luaL_argcheck( L, lua_type( L, 1 ) == LUA_TFUNCTION, 1, "expected function" );
    214 	luaL_argcheck( L, lua_type( L, 2 ) == LUA_TFUNCTION, 2, "expected function" );
    215 	luaL_argcheck( L, lua_type( L, 3 ) == LUA_TFUNCTION, 3, "expected function" );
    216 	luaL_argcheck( L, lua_type( L, 4 ) == LUA_TFUNCTION, 4, "expected function" );
    217 	luaL_argcheck( L, lua_type( L, 5 ) == LUA_TFUNCTION, 5, "expected function" );
    218 
    219 	intervalHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
    220 	socketHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
    221 	closeHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
    222 	macroHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
    223 	inputHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
    224 
    225 	return 0;
    226 }
    227 
    228 extern "C" int mud_urgent( lua_State * L ) {
    229 	ui_urgent();
    230 	return 0;
    231 }
    232 
    233 extern "C" int mud_now( lua_State * L ) {
    234 	lua_pushnumber( L, get_time() );
    235 	return 1;
    236 }
    237 
    238 extern "C" int mud_set_font( lua_State * L ) {
    239 	const char * name = luaL_checkstring( L, 1 );
    240 	int size = luaL_checkinteger( L, 2 );
    241 
    242 	bool ok = ui_set_font( name, size );
    243 
    244 	lua_pushboolean( lua, ok );
    245 
    246 	return 1;
    247 }
    248 
    249 } // anon namespace
    250 
    251 static void push_exe_dir( lua_State * L ) {
    252 	int len = wai_getExecutablePath( NULL, 0, NULL );
    253 	if( len == -1 ) {
    254 		lua_pushliteral( L, "." );
    255 	}
    256 	else {
    257 		char * buf = alloc_many< char >( len );
    258 
    259 		int dirlen;
    260 		if( wai_getExecutablePath( buf, len, &dirlen ) == -1 ) {
    261 			lua_pushliteral( L, "." );
    262 		}
    263 		else {
    264 			lua_pushlstring( L, buf, dirlen );
    265 		}
    266 
    267 		free( buf );
    268 	}
    269 }
    270 
    271 #if PLATFORM_WINDOWS
    272 extern "C" int luaopen_lpeg( lua_State * L );
    273 extern "C" int luaopen_lfs( lua_State * L );
    274 #endif
    275 
    276 void script_init() {
    277 	ZoneScoped;
    278 
    279 	lua = luaL_newstate();
    280 	luaL_openlibs( lua );
    281 
    282 #if PLATFORM_WINDOWS
    283 	luaL_requiref( lua, "lpeg", luaopen_lpeg, 0 );
    284 	lua_pop( lua, 1 );
    285 
    286 	luaL_requiref( lua, "lfs", luaopen_lfs, 0 );
    287 	lua_pop( lua, 1 );
    288 #endif
    289 
    290 	lua_getglobal( lua, "debug" );
    291 	lua_getfield( lua, -1, "traceback" );
    292 	lua_remove( lua, -2 );
    293 
    294 	if( luaL_loadbufferx( lua, ( const char * ) lua_combined, sizeof( lua_combined ), "main", "t" ) != LUA_OK ) {
    295 		printf( "Error reading main.lua: %s\n", lua_tostring( lua, -1 ) );
    296 		exit( 1 );
    297 	}
    298 
    299 	lua_pushcfunction( lua, mud_printMain );
    300 	lua_pushcfunction( lua, mud_newlineMain );
    301 	lua_pushcfunction( lua, mud_printChat );
    302 	lua_pushcfunction( lua, mud_newlineChat );
    303 
    304 	lua_pushcfunction( lua, mud_setHandlers );
    305 
    306 	lua_pushcfunction( lua, mud_urgent );
    307 
    308 	lua_pushcfunction( lua, mud_setStatus );
    309 
    310 	lua_pushcfunction( lua, mud_connect );
    311 	lua_pushcfunction( lua, mud_send );
    312 	lua_pushcfunction( lua, mud_close );
    313 
    314 	lua_pushcfunction( lua, mud_now );
    315 
    316 	lua_pushcfunction( lua, mud_set_font );
    317 
    318 	push_exe_dir( lua );
    319 
    320 	pcall( 13, "Error running main.lua" );
    321 }
    322 
    323 void script_term() {
    324 	lua_close( lua );
    325 }