aboutsummaryrefslogtreecommitdiffstats
path: root/Lua/src
diff options
context:
space:
mode:
authorwhiteshark0 <whiteshark0@b956fd51-792f-4845-bead-9b4dfca2ff2c>2009-04-17 16:45:41 +0000
committerwhiteshark0 <whiteshark0@b956fd51-792f-4845-bead-9b4dfca2ff2c>2009-04-17 16:45:41 +0000
commita7d1d94cbae98f8cd804f453d24bd8036e314518 (patch)
tree0f59b9ac6d4bd4dff2c9469b21370bcd5d606efd /Lua/src
parent44d215e69d768b32c26f62b711baaf41b36fe1ea (diff)
downloadusdx-a7d1d94cbae98f8cd804f453d24bd8036e314518.tar.gz
usdx-a7d1d94cbae98f8cd804f453d24bd8036e314518.tar.xz
usdx-a7d1d94cbae98f8cd804f453d24bd8036e314518.zip
lua plugin interface implemented
it offers some basic environment to the loaded plugins see ./game/plugins/PluginDescription.odt for further explantation. git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@1674 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'Lua/src')
-rw-r--r--Lua/src/base/UMain.pas11
-rw-r--r--Lua/src/lua/UHookableEvent.pas267
-rw-r--r--Lua/src/lua/ULuaCore.pas864
-rw-r--r--Lua/src/ultrastardx.dpr2
4 files changed, 1142 insertions, 2 deletions
diff --git a/Lua/src/base/UMain.pas b/Lua/src/base/UMain.pas
index 2da1ca39..d0a9afee 100644
--- a/Lua/src/base/UMain.pas
+++ b/Lua/src/base/UMain.pas
@@ -183,6 +183,8 @@ uses
UGraphicClasses,
UPluginDefs,
UPlatform,
+ ULuaCore,
+ UHookableEvent,
ULuaGl,
ULuaLog,
ULuaTexture,
@@ -417,8 +419,13 @@ begin
chr(0))
); }
- Log.LogStatus('Running Core', 'Initialization');
- //Core.Run;
+ LuaCore := TLuaCore.Create;
+
+
+ LuaCore.BrowseDir(PluginPath);
+ LuaCore.DumpPlugins;
+
+
//------------------------------
//Start- Mainloop
diff --git a/Lua/src/lua/UHookableEvent.pas b/Lua/src/lua/UHookableEvent.pas
new file mode 100644
index 00000000..cc7cc443
--- /dev/null
+++ b/Lua/src/lua/UHookableEvent.pas
@@ -0,0 +1,267 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/branches/experimental/Lua/src/lua/ULuaGl.pas $
+ * $Id: ULuaGl.pas 1555 2009-01-11 18:19:42Z Hawkear $
+ *}
+
+unit UHookableEvent;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses ULua;
+
+type
+ { Record holding information about a hook of an event }
+ PHook = ^THook;
+ THook = record
+ Handle: Integer; //< Handle to identify the hook, e.g. for unhooking by plugin
+ Parent: Integer; //< Lua Core Handle this hook belongs to
+
+ Next: PHook; //< Next Hook in list (nil for the first)
+ end;
+
+ { procedure is called before each call to the hooking lua functions, to push values on stack
+ returns the number of pushed arguments}
+ PrepareStackProc = Function: Integer;
+
+ { class representing a hookable event }
+ THookableEvent = class
+ private
+ iHandle: Integer; //< used to unregister at lua core
+ LastHook: PHook; //< last hook in hook list, first to be called
+ NextHookHandle: Integer; //< handle to identify next hook
+
+ sName: String; //< the events name
+
+ PrepareStack: PrepareStackProc; //< prepare stack procedure passed to constructor
+ public
+ constructor Create(Name: String; const Proc: PrepareStackProc = nil);
+
+ property Name: String read sName; //< returns the events name
+ property Handle: Integer read iHandle; //< returns the events name
+
+ procedure Hook(L: Plua_State; Parent: Integer; Proc: String); //< pushes hook object/table to the lua stack
+ procedure UnHook(L: Plua_State; hHook: Integer); //< unhook by plugin. push true or error string to lua stack
+
+ procedure UnHookByParent(Parent: Integer); //< deletes all hooks by a specified parent (unhook by core)
+
+ function CallHookChain(Breakable: Boolean): PLua_State; //< calls the events hookchain. if breakable, plugin can breake the chain by returning a value != 0 or false or nil
+
+ destructor Destroy;
+ end;
+
+{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
+function PrepareStack_Dummy: Integer;
+
+{ function in resulting hook table. it calls the unhook command of the event on plugins demand }
+function LuaHook_UnHook(L: Plua_State): Integer; cdecl;
+
+implementation
+uses ULuaCore;
+
+constructor THookableEvent.Create(Name: String; const Proc: PrepareStackProc);
+begin
+ inherited Create;
+
+ Self.sName := Name;
+
+ if (@Proc = nil) then
+ Self.PrepareStack := @PrepareStack_Dummy
+ else
+ Self.PrepareStack := Proc;
+
+ //init LastHook pointer w/ nil
+ LastHook := nil;
+ NextHookHandle := 1;
+
+ iHandle := LuaCore.RegisterEvent(Self);
+end;
+
+destructor THookableEvent.Destroy;
+var
+ Prev: PHook;
+ Cur: PHook;
+begin
+ //delete all hooks
+ Cur := LastHook;
+ While (Cur <> nil) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+
+ Dispose(Prev);
+ end;
+
+ //remove from luacores list
+ LuaCore.UnRegisterEvent(iHandle);
+
+ inherited;
+end;
+
+{ adds hook to events list and pushes hook object/table to the lua stack }
+procedure THookableEvent.Hook(L: Plua_State; Parent: Integer; Proc: String);
+ var
+ Item: PHook;
+begin
+ // get mem and fill it w/ data
+ New(Item);
+ Item.Handle := NextHookHandle;
+ Inc(NextHookHandle);
+
+ Item.Parent := Parent;
+
+ // add at front of the hook chain
+ Item.Next := LastHook;
+ LastHook := Item;
+
+ //we need 2 free stack slots
+ lua_checkstack(L, 2);
+
+ //create the hook table, we need 2 elements (event name and unhook function)
+ lua_createtable(L, 0, 2);
+
+ //push events name
+ lua_pushstring(L, PAnsiChar(Name));
+
+ //add the name to the table
+ lua_setfield(L, -2, 'Event');
+
+ //push hook id to the stack
+ lua_pushinteger(L, Item.Handle);
+
+ //create a c closure, append one value from stack(the id)
+ //this will pop both, the function and the id
+ lua_pushcclosure(L, LuaHook_UnHook, 1);
+
+ //add the function to our table
+ lua_setfield(L, -2, 'Unhook');
+
+ //the table is left on the stack, it is our result
+end;
+
+{ unhook by plugin. push true or error string to lua stack }
+procedure THookableEvent.UnHook(L: Plua_State; hHook: Integer);
+ var
+ Cur, Prev: PHook;
+begin
+ if (hHook < NextHookHandle) and (hHook > 0) then
+ begin
+ //Search for the Hook
+ Prev := nil;
+ Cur := LastHook;
+
+ while (Cur <> nil) do
+ begin
+ if (Cur.Handle = hHook) then
+ begin //we found the hook => remove it
+ if (prev <> nil) then
+ Prev.Next := Cur.Next
+ else //last hook found
+ LastHook := Cur.Next;
+
+ //free hooks memory
+ Dispose(Cur);
+
+ //indicate success
+ lua_pushboolean(L, True);
+ exit; //break the chain and exit the function
+ end;
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+
+ lua_pushstring(L, PAnsiChar('handle already unhooked')); //the error description
+ end
+ else
+ lua_pushstring(L, PAnsiChar('undefined hook handle')); //the error description
+end;
+
+{ deletes all hooks by a specified parent (unhook by core) }
+procedure THookableEvent.UnHookByParent(Parent: Integer);
+ var
+ Cur, Prev: PHook;
+begin
+ Prev := nil;
+ Cur := LastHook;
+
+ While (Cur <> nil) do
+ begin
+ If (Cur.Parent = Parent) then
+ begin //found a hook from parent => remove it
+ If (Prev <> nil) then
+ Prev.Next := Cur.Next
+ Else
+ LastHook := Cur.Next;
+
+ Dispose(Cur);
+
+ Cur := Prev.Next;
+ end
+ else //move through the chain
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+ end;
+end;
+
+{ calls the events hookchain. if breakable, plugin can breake the chain
+ by returning a value other than 0 or false or nil
+ breakable is pushed as the first parameter to the hooking functions
+ if chain is broken the LuaStack is returned, with all results left
+ you may call lua_clearstack }
+function THookableEvent.CallHookChain(Breakable: Boolean): Plua_State;
+ var
+ Cur: PHook;
+begin
+
+end;
+
+{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
+function PrepareStack_Dummy: Integer;
+begin
+ Result := 0;
+end;
+
+{ function in resulting hook table. it calls the unhook command of the event on plugins demand }
+function LuaHook_UnHook(L: Plua_State): Integer; cdecl;
+begin
+ //lua_upvalueindex
+ Result := 0;
+end;
+
+{ this is a helper in case an evenet owner don't has no use for the results
+ returns number of popped elements }
+function Lua_ClearStack(L: Plua_State): Integer;
+ var I: Integer;
+begin
+ Result := lua_gettop(L);
+ lua_pop(L, Result);
+end;
+
+end. \ No newline at end of file
diff --git a/Lua/src/lua/ULuaCore.pas b/Lua/src/lua/ULuaCore.pas
new file mode 100644
index 00000000..9f9188c1
--- /dev/null
+++ b/Lua/src/lua/ULuaCore.pas
@@ -0,0 +1,864 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/branches/experimental/Lua/src/lua/ULuaGl.pas $
+ * $Id: ULuaGl.pas 1555 2009-01-11 18:19:42Z Hawkear $
+ *}
+
+unit ULuaCore;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses SysUtils, ULua, UHookableEvent;
+
+type
+ { this exception is raised when the lua panic function
+ is called. Only in case we use call instead of pcall.
+ it has the lua error string in its message attribute }
+ ELuaException = class(Exception);
+
+ { record represents item of Eventlist of TLuaCore }
+ PEventListItem = ^TEventListItem;
+ TEventListItem = record
+ Event: THookableEvent;
+ Next: PEventListItem;
+ end;
+
+ { record represents a module }
+ TLuaModule = record
+ Name: String;
+ Functions: Array of luaL_reg; //modules functions, w/ trailing nils this time
+ end;
+
+ TLuaPlugin_Status = (psNone, psRunning, psClosed, psErrorOnLoad, psErrorOnCall, psErrorInInit, psErrorOnRun);
+ { class represents a loaded plugin }
+ TLuaPlugin = class
+ private
+ iId: Integer;
+ Filename: WideString;
+ State: Plua_State; //< all functions of this plugin are called with this Lua state
+ bPaused: Boolean; //< If true no lua functions from this state are called
+ ErrorCount: Integer; //< counts the errors that occured during function calls of this plugin
+
+ sName: String;
+ sVersion: String;
+ sAuthor: String;
+ sURL: String;
+
+ sStatus: TLuaPlugin_Status;
+ public
+ constructor Create(Filename: WideString; Id: Integer);
+
+ property Id: Integer read iId;
+ property Name: String read sName;
+ property Version: String read sVersion;
+ property Author: String read sAuthor;
+ property Url: String read sUrl;
+
+ property Status: TLuaPlugin_Status read sStatus;
+ property CountErrors: Integer read ErrorCount;
+
+ property LuaState: Plua_State read State;
+
+ procedure Load;
+
+ procedure Register(Name, Version, Author, Url: String);
+ function HasRegistred: Boolean;
+
+ procedure PausePlugin(doPause: Boolean);
+ property Paused: boolean read bPaused write PausePlugin;
+
+ { calls the lua function in the global w/ the given name.
+ the arguments to the function have to be pushed to the stack
+ before calling this function.
+ the arguments and the function will be removed from stack
+ results will not be removed.
+ if result is false there was an error calling the function
+ if ReportErrors is true the errorstring is popped from stack
+ and written to error.log otherwise it is left on stack}
+ function CallFunctionByName(Name: String; const nArgs: Integer = 0; const nResults: Integer = 0; const ReportErrors: Boolean = True): Boolean;
+ procedure ClearStack;
+
+ procedure Unload; //< Destroys the Luastate, and frees as much mem as possible, w/o destroying the class and important information
+
+ destructor Destroy;
+ end;
+
+ { class managing the plugins w/ their LuaStates, the events and modules
+ it also offers the usdx table to the plugins w/ some basic functionality
+ like self unload or hook getting}
+ TLuaCore = class
+ private
+ EventList: PEventListItem; //< pointer to first registred Event, ordered by name
+ EventHandles: Array of String; //< Index is Events handle, value is events name. if length(value) is 0 handle is considered unregistred
+
+ Plugins: Array of TLuaPlugin;
+ protected
+ Modules: Array of TLuaModule; //< modules that has been registred, has to be proctected because fucntions of this unit need to get access
+
+ function GetModuleIdByName(Name: String): Integer; //returns id of given module, or -1 if module is not found
+ public
+ constructor Create;
+ destructor Destroy;
+
+ procedure BrowseDir(Dir: WideString); //< searches for files w/ extension .usdx in the specified dir and tries to load them w/ lua
+ procedure LoadPlugin(Filename: WideString); //< tries to load filename w/ lua and creates the default usdx lua environment for the plugins state
+
+ function GetPluginByName(Name: String): TLuaPlugin;
+ function GetPluginById(Id: Integer): TLuaPlugin;
+
+ { this function adds a module loader for your functions
+ name is the name the script needs to write in its require()
+ Functions is an array of lua calling compatible functions
+ w/o trailing nils! }
+ procedure RegisterModule(Name: String; const Functions: Array of luaL_reg);
+
+ function RegisterEvent(Event: THookableEvent): Integer; //< adds the event to eventlist and returns its handle
+ procedure UnRegisterEvent(hEvent: Integer); //< removes the event from eventlist by handle
+
+ function GetEventbyName(Name: String): THookableEvent; //< tries to find the event w/ the given name in the list
+ function GetEventbyHandle(hEvent: Integer): THookableEvent; //< tries to find the event w/ the given handle
+
+ procedure PrepareState(L: Plua_State);
+
+ procedure DumpPlugins; //< prints plugin runtime information w/ Log.LogStatus
+ end;
+
+//some luastyle functions to call from lua scripts
+{ register global, used by plugins to identify
+ register(plugin name, plugin version, [plugin author], [plugin homepage])
+ can only be called once since the global "register" is niled by the function
+ returns true on success. (name does not exist)}
+function TLuaPlugin_Register (L: Plua_State): Integer; cdecl;
+
+{ moduleloader for usdx.* modules
+ stored in package.loaders[3]
+ package.loaders[3] (module name)
+ returns a function to load the requested module or a error
+ description(string) when the module is not found }
+function TLuaCore_ModuleLoader (L: Plua_State): Integer; cdecl;
+
+{ loads module specified by a cfunction upvalue to
+ usdx.modulename and returns it.
+ loadmodule(module name) }
+function TLuaCore_LoadModule (L: Plua_State): Integer; cdecl;
+
+{ custom lua panic function
+ it writes error string to error.log and raises an ELuaException
+ that may be caught }
+function TLua_CustomPanic (L: Plua_State): Integer; cdecl;
+
+
+var
+ LuaCore: TLuaCore;
+
+implementation
+uses ULog, UPlatform;
+
+constructor TLuaCore.Create;
+begin
+ inherited;
+
+ //init EventList w/ nil
+ EventList := nil;
+end;
+
+destructor TLuaCore.Destroy;
+var
+ Cur: PEventListItem;
+ Prev: PEventListItem;
+begin
+ SetLength(EventHandles, 0);
+
+ //delete event list
+ Cur := EventList;
+
+ While(Cur <> nil) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+
+ Dispose(Prev);
+ end;
+
+ inherited;
+end;
+
+{ searches for files w/ extension .usdx in the specified
+ dir and tries to load them w/ lua }
+procedure TLuaCore.BrowseDir(Dir: WideString);
+ var
+ Files: TDirectoryEntryArray;
+ I: Integer;
+begin
+ try
+ Files := Platform.DirectoryFindFiles(Dir, '.usdx', true)
+ except
+ Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TLuaCore.BrowseDir')
+ end;
+
+ for I := 0 to High(Files) do
+ if (Files[I].IsDirectory) then
+ BrowseDir(Dir + Files[i].Name) //browse recursive
+ else if (Files[I].IsFile) then
+ LoadPlugin(Dir + Files[i].Name);
+end;
+
+{ tries to load filename w/ lua and creates the default
+ usdx lua environment for the plugins state }
+procedure TLuaCore.LoadPlugin(Filename: WideString);
+ var
+ Len: Integer;
+begin
+ Len := Length(Plugins);
+ SetLength(Plugins, Len + 1);
+ Plugins[Len] := TLuaPlugin.Create(Filename, Len);
+ Plugins[Len].Load;
+end;
+
+{ returns Plugin on success nil on failure }
+function TLuaCore.GetPluginByName(Name: String): TLuaPlugin;
+ var
+ I: Integer;
+begin
+ Result := nil;
+ Name := lowercase(Name);
+
+ For I := 0 to High(Plugins) do
+ If (lowercase(Plugins[I].Name) = Name) then
+ begin
+ Result := GetPluginById(I);
+ Exit;
+ end;
+end;
+
+{ returns Plugin on success nil on failure }
+function TLuaCore.GetPluginById(Id: Integer): TLuaPlugin;
+begin
+ If (Id >= 0) AND (Id <= High(Plugins)) then
+ Result := Plugins[Id]
+ Else
+ Result := nil;
+end;
+
+{ this function adds a module loader for your functions
+ name is the name the script needs to write in its require()
+ Functions is an array of lua calling compatible functions
+ w/o trailing nils! }
+procedure TLuaCore.RegisterModule(Name: String; const Functions: Array of luaL_reg);
+ var
+ Len: Integer;
+ FuncLen: Integer;
+ I: Integer;
+begin
+ Len := Length(Modules);
+ SetLength(Modules, Len + 1);
+ Modules[Len].Name := Name;
+
+ FuncLen := Length(Functions);
+ SetLength(Modules[Len].Functions, FuncLen + 1);
+
+ For I := 0 to FuncLen-1 do
+ Modules[Len].Functions[I] := Functions[I];
+
+ Modules[Len].Functions[FuncLen].name := nil;
+ Modules[Len].Functions[FuncLen].func := nil;
+end;
+
+{ adds the event to eventlist and returns its handle
+ called by THookableEvent on creation }
+function TLuaCore.RegisterEvent(Event: THookableEvent): Integer;
+var
+ Cur, Prev, Item: PEventListItem;
+begin
+ If (Event <> nil) and (Length(Event.Name) > 0) then
+ begin
+ Result := Length(EventHandles);
+ SetLength(EventHandles, Result + 1); //get Handle and copy it to result
+
+ EventHandles[Result] := Event.Name;
+
+ //create eventlist item
+ New(Item);
+ Item.Event := Event;
+
+ //search for a place for this event in alphabetical order
+ Prev := nil;
+ Cur := EventList;
+
+ While (Cur <> nil) and (CompareStr(Cur.Event.Name, EventHandles[Result]) < 0) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+
+ //found the place => add new item
+ if (Prev <> nil) then
+ Prev.Next := Item
+ else //first item
+ EventList := Item;
+
+ Item.Next := Cur;
+ end
+ else
+ Result := -1;
+end;
+
+{ removes the event from eventlist by handle }
+procedure TLuaCore.UnRegisterEvent(hEvent: Integer);
+ var
+ Cur, Prev: PEventListItem;
+begin
+ If (hEvent >= 0) AND (hEvent <= High(EventHandles)) AND (Length(EventHandles[hEvent]) > 0) then
+ begin //hEvent in bounds and not already deleted
+ //delete from eventlist
+ Prev := nil;
+ Cur := EventList;
+
+ While (Cur <> nil) and (CompareStr(Cur.Event.Name, EventHandles[hEvent]) < 0) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+
+ If (Cur <> nil) and (Cur.Event.Name = EventHandles[hEvent]) then
+ begin //delete if found
+ Prev.Next := Cur.Next; // remove from list
+ Dispose(Cur); // free memory
+ end;
+
+ //delete from handle array
+ EventHandles[hEvent] := '';
+ end;
+end;
+
+{ tries to find the event w/ the given name in the list }
+function TLuaCore.GetEventbyName(Name: String): THookableEvent;
+ var
+ Cur: PEventListItem;
+begin
+ Result := nil;
+
+ if (Length(Name) > 0) then
+ begin
+ //search in eventlist
+ Cur := EventList;
+
+ While (Cur <> nil) and (CompareStr(Cur.Event.Name, Name) < 0) do
+ begin
+ Cur := Cur.Next;
+ end;
+
+ If (Cur <> nil) and (Cur.Event.Name = Name) then
+ begin //we found what we want to find
+ Result := Cur.Event;
+ end;
+ end;
+end;
+
+{ tries to find the event w/ the given handle }
+function TLuaCore.GetEventbyHandle(hEvent: Integer): THookableEvent;
+begin
+ If (hEvent >= 0) AND (hEvent <= High(EventHandles)) AND (Length(EventHandles[hEvent]) > 0) then
+ begin //hEvent in bounds and not already deleted
+ Result := GetEventByName(EventHandles[hEvent]);
+ end
+ else
+ Result := nil;
+end;
+
+{ prepares the given already opened Lua state with the
+ basic usdx environment, e.g.: base and package Modules,
+ usdx moduleloaders and usdx table }
+procedure TLuaCore.PrepareState(L: Plua_State);
+begin
+ //load basic lib functionality
+ lua_pushcfunction(L, luaopen_base);
+ lua_call(L, 0, 0);
+ lua_pop(L, lua_gettop(L)); //pop the results
+
+ //load module functionality
+ lua_pushcfunction(L, luaopen_package);
+ lua_call(L, 0, 0);
+ lua_pop(L, lua_gettop(L)); //pop the results
+
+ { adds the loader for the other standard lib to package.preload table
+ plugins can call e.g. require('math') if they need math functionality }
+
+ // we need 3 free stack slots
+ lua_checkstack(L, 3);
+
+ // get package table
+ lua_getglobal (L, PChar('package'));
+
+ // get package.preload table
+ lua_getfield (L, -1, PChar('preload'));
+
+ {**** add string lib }
+
+ // push loader function
+ lua_pushcfunction(L, luaopen_string);
+
+ // set package.preload.x loader
+ lua_setfield (L, -2, PChar('string'));
+
+ {**** add table lib }
+
+ // push loader function
+ lua_pushcfunction(L, luaopen_table);
+
+ // set package.preload.x loader
+ lua_setfield (L, -2, PChar('table'));
+
+ {**** add math lib }
+
+ // push loader function
+ lua_pushcfunction(L, luaopen_math);
+
+ // set package.preload.x loader
+ lua_setfield (L, -2, PChar('math'));
+
+ {**** add os lib }
+
+ // push loader function
+ lua_pushcfunction(L, luaopen_os);
+
+ // set package.preload.x loader
+ lua_setfield (L, -2, PChar('os'));
+
+ //pop package.preload table from stack
+ lua_pop(L, 1);
+
+ // get package.loaders table
+ lua_getfield (L, -1, PChar('loaders'));
+
+ {**** Move C-Library and all-in-one module loader backwards,
+ slot 3 is free now }
+ // get package.loaders[4] function
+ lua_getfield (L, -1, '4');
+
+ // and move it to package.loaders[5]
+ lua_setfield (L, -2, '5');
+
+ // get package.loaders[3] function
+ lua_getfield (L, -1, '3');
+
+ // and move it to package.loaders[4]
+ lua_setfield (L, -2, '4');
+
+ {**** now we add the core module to package.loaders[3] }
+ lua_pushcfunction(L, TLuaCore_ModuleLoader);
+
+ // and move it to package.loaders[3]
+ lua_setfield (L, -2, '3');
+
+
+ //pop both package and package.loaders tables from stack
+ lua_pop(L, 2);
+
+end;
+
+{ returns id of given module, or -1 if module is not found }
+function TLuaCore.GetModuleIdByName(Name: String): Integer;
+ var
+ I: Integer;
+begin
+ Result := -1;
+
+ for I := 0 to High(Modules) do
+ if (Modules[I].Name = Name) then
+ begin
+ Result := I;
+ Exit;
+ end;
+end;
+
+{ moduleloader for usdx.* modules
+ stored in package.loaders[3]
+ package.loaders[3] (module name)
+ returns a function to load the requested module or an error
+ description(string) when the module is not found }
+function TLuaCore_ModuleLoader (L: Plua_State): Integer; cdecl;
+ var
+ Name: String;
+ ID: Integer;
+begin
+ Result := 1; //we will return one value in every case (or never return in case of an error)
+
+ if (lua_gettop(L) >= 1) then
+ begin
+ // pop all arguments but the first
+ if (lua_gettop(L) > 1) then
+ lua_pop(L, lua_gettop(L)-1);
+
+
+ if (lua_IsString(L, 1)) then
+ begin //we got the name => go get it
+ Name := lua_toString(L, 1);
+
+ //we need at least 6 letters
+ //and first 5 letters have to be usdx.
+ if (Length(Name) > 5) and (copy(Name, 1, 5)='usdx.') then
+ begin
+ ID := LuaCore.GetModuleIdByName(copy(Name, 6, Length(Name) - 5));
+ If (ID >= 0) then
+ begin //found the module -> return loader function
+ lua_pushinteger(L, Id);
+ lua_pushcclosure(L, TLuaCore_LoadModule, 1);
+ //the function is the result, so we leave it on stack
+ end
+ else
+ lua_pushString(L, PChar('usdx module "' + Name + '" couldn''t be found'));
+ end
+ else
+ lua_pushString(L, PChar('module doesn''t have "usdx." prefix'));
+
+ end
+ else
+ luaL_argerror(L, 1, PChar('string expected'));
+ end
+ else
+ luaL_error(L, PChar('no modulename specified in usdx moduleloader'));
+end;
+
+{ loads module specified by a cfunction upvalue to
+ usdx.modulename and returns it.
+ loadmodule(module name) }
+function TLuaCore_LoadModule (L: Plua_State): Integer; cdecl;
+ var
+ Id: Integer;
+begin
+ if (not lua_isnoneornil(L, lua_upvalueindex(1))) then
+ begin
+ Id := lua_ToInteger(L, lua_upvalueindex(1));
+
+ luaL_register(L, PChar('usdx.' + LuaCore.Modules[Id].Name), @LuaCore.Modules[Id].Functions[0]);
+
+ Result := 1; //return table
+ end
+ else
+ luaL_error(L, PChar('no upvalue found in LuaCore_LoadModule'));
+end;
+
+{ prints plugin runtime information w/ Log.LogStatus }
+procedure TLuaCore.DumpPlugins;
+ function PluginStatusToString(Status: TLuaPlugin_Status): String;
+ begin
+ Case Status of
+ psNone: Result := 'not loaded';
+ psRunning: Result := 'running';
+ psClosed: Result := 'closed';
+ psErrorOnLoad: Result := 'error during load';
+ psErrorOnCall: Result := 'error during call';
+ psErrorInInit: Result := 'error in plugin_init()';
+ psErrorOnRun: Result := 'error on function call';
+ else Result := 'unknown';
+ end;
+ end;
+ var
+ I: Integer;
+begin
+ //print table header
+ Log.LogError(' # ' + #09 + ' name ' + #09 + ' version ' + #09 + ' status ' + #09 + ' paused ' + #09 + ' #errors ', 'plugins');
+
+
+ For I := 0 to High(Plugins) do
+ Log.LogError(' ' + IntToStr(Plugins[I].Id) + ' ' + #09 +
+ ' ' + Plugins[I].Name + ' ' + #09 +
+ ' ' + Plugins[I].Version + ' ' + #09 +
+ ' ' + PluginStatusToString(Plugins[I].Status) + ' ' + #09 +
+ ' ' + BoolToStr(Plugins[I].Paused, true) + ' ' + #09 +
+ ' ' + IntToStr(Plugins[I].CountErrors) + ' ', 'plugins');
+
+ If (High(Plugins)<0) then
+ Log.LogError(' no plugins loaded ');
+end;
+
+// Implementation of TLuaPlugin
+//--------
+constructor TLuaPlugin.Create(Filename: WideString; Id: Integer);
+begin
+ inherited Create;
+ Self.iId := Id;
+ Self.Filename := Filename;
+
+ // set some default attributes
+ Self.bPaused := False;
+ Self.ErrorCount := 0;
+ Self.sName := 'not registred';
+ Self.sStatus := psNone;
+end;
+
+destructor TLuaPlugin.Destroy;
+begin
+ Unload;
+ inherited;
+end;
+
+{ does the main loading part
+ can not be called by create, because Plugins[Id] isn't defined there }
+procedure TLuaPlugin.Load;
+begin
+ // create Lua state for this plugin
+ State := luaL_newstate;
+
+ //set our custom panic function if s/t went wrong along the init
+ //we don't expect
+ lua_atPanic(State, TLua_CustomPanic);
+
+ if (luaL_loadfile(State, PChar(String(Self.Filename))) = 0) then
+ begin // file loaded successful
+ { note: we run the file here, but the environment isn't
+ set up now. it just causes the functions to
+ register in globals and runs the code in the file
+ body. At least there should be no code, it could
+ neither use functions from baselibs nor load libs
+ with require, this code would be useless. }
+ if (lua_pcall(State, 0, 0, 0) = 0) then
+ begin // file called successful
+
+ //let the core prepare our state
+ LuaCore.PrepareState(State);
+
+ // set register function
+ lua_checkstack(State, 2);
+ lua_pushinteger(State, Id);
+ lua_pushcclosure(State, TLuaPlugin_Register, 1);
+ lua_setglobal(State, PChar('register'));
+
+ // now run the plugin_init function
+ // plugin_init() if false or nothing is returned plugin init is aborted
+ if (CallFunctionByName('plugin_init', 0, 1)) then
+ begin
+ If (HasRegistred) AND (lua_toBoolean(State, 1)) then
+ begin
+ sStatus := psRunning;
+ ClearStack;
+ end
+ else
+ Unload;
+ end
+ else
+ begin
+ sStatus := psErrorInInit;
+ Log.LogError('error in plugin_init: ' + Filename, 'lua');
+ Unload;
+ end;
+ end
+ else
+ begin
+ sStatus := psErrorOnLoad;
+ Log.LogError('unable to call file: ' + Filename, 'lua');
+ Unload;
+ end;
+
+ end
+ else
+ begin
+ sStatus := psErrorOnLoad;
+ Log.LogError('unable to load file: ' + Filename, 'lua');
+ Unload;
+ end;
+end;
+
+procedure TLuaPlugin.Register(Name, Version, Author, Url: String);
+begin
+ sName := Name;
+ sVersion := Version;
+ sAuthor := Author;
+ sURL := Url;
+end;
+
+{ returns true if plugin has called register }
+function TLuaPlugin.HasRegistred: Boolean;
+begin
+ Result := (Self.sName = 'not registred');
+end;
+
+procedure TLuaPlugin.PausePlugin(doPause: Boolean);
+begin
+ bPaused := doPause;
+end;
+
+{ calls the lua function in the global w/ the given name.
+ the arguments to the function have to be pushed to the stack
+ before calling this function.
+ the arguments and the function will be removed from stack
+ results will not be removed.
+ if result is false there was an error calling the function,
+ if ReportErrors is true the errorstring is popped from stack
+ and written to error.log otherwise it is left on stack}
+function TLuaPlugin.CallFunctionByName(Name: String; const nArgs: Integer; const nResults: Integer; const ReportErrors: Boolean): Boolean;
+begin
+ Result := false;
+ if not bPaused then
+ begin
+ // we need at least one stack slot free
+ lua_checkstack(State, 1);
+
+ // lua_getglobal(State, PChar(Name)); //this is just a macro:
+ lua_getfield(State, LUA_GLOBALSINDEX, PChar(Name));
+
+ If (lua_isfunction(State, -1)) then
+ begin //we got a function
+ // move function in front of the arguments (if any)
+ if (nArgs > 0) then
+ lua_insert(State, -(nArgs + 1));
+
+ // call it!
+ if (lua_pcall(State, nArgs, nResults, 0) = 0) then
+ Result := true //called w/o errors
+ else //increase error counter
+ Inc (ErrorCount);
+ end
+ else
+ begin //we have to pop the args and the field we pushed from stack
+ lua_pop(State, nArgs + 1);
+ //leave an errormessage on stack
+ lua_pushstring(State, Pchar('could not find function named ' + Name));
+ end;
+ end
+ else
+ begin //we have to pop the args from stack
+ lua_pop(State, nArgs);
+ //leave an errormessage on stack
+ lua_pushstring(State, PChar('plugin paused'));
+ end;
+
+ if (not Result) AND (ReportErrors) then
+ Log.LogError(lua_toString(State, -1), 'lua/' + sName);
+end;
+
+{ removes all values from stack }
+procedure TLuaPlugin.ClearStack;
+begin
+ if (lua_gettop(State) > 0) then
+ lua_pop(State, lua_gettop(State));
+end;
+
+{ Destroys the Luastate, and frees as much mem as possible,
+ w/o destroying the class and important information }
+procedure TLuaPlugin.Unload;
+begin
+ if (State <> nil) then
+ begin
+ if (Status in [psRunning, psErrorOnRun]) then
+ CallFunctionByName('plugin_unload', 1, 0, False);
+
+ ClearStack;
+ lua_close(State);
+ State := nil; //don't forget to nil it ;)
+
+ if (sStatus = psRunning) then
+ sStatus := psClosed;
+ end;
+end;
+
+function TLuaPlugin_Register (L: Plua_State): Integer; cdecl;
+ var
+ Id: Integer;
+ P: TLuaPlugin;
+ Name, Version, Author, Url: String;
+begin
+ if (lua_gettop(L) >= 2) then
+ begin // we got at least name and version
+ if (not lua_isNumber(L, lua_upvalueindex(1))) then
+ luaL_Error(L, PChar('upvalue missing'));
+
+ if (not lua_isString(L, 1)) then
+ luaL_ArgError(L, 1, 'string expected');
+
+ if (not lua_isString(L, 2)) then
+ luaL_ArgError(L, 1, 'string expected');
+
+ Id := lua_ToInteger(L, lua_upvalueindex(1));
+
+ //get version and name
+ Name := lua_tostring(L, 1);
+ Version := lua_tostring(L, 2);
+
+ //get optional parameters
+ if (lua_isString(L, 3)) then //author
+ Author := lua_toString(L, 3)
+ else
+ begin
+ Author := 'unknown';
+ end;
+
+ // homepage
+ if (lua_isString(L, 4)) then
+ Url := lua_toString(L, 4)
+ else
+ begin
+ Url := '';
+ end;
+
+ //clear stack
+ if (lua_gettop(L) > 0) then
+ lua_pop(L, lua_gettop(L));
+
+ //call register
+ P := LuaCore.GetPluginById(Id);
+ if (P <> nil) then
+ P.Register(Name, Version, Author, Url)
+ Else
+ luaL_error(L, PChar('wrong id in upstream'));
+
+ // remove function from global register
+ lua_pushnil(L);
+ lua_setglobal(L, PChar('register'));
+
+ // return true
+ Result := 1;
+ lua_pushboolean(L, True);
+ end
+ else
+ luaL_error(L, PChar('not enough arguments, at least 2 expected. in TLuaPlugin_Register'));
+end;
+
+{ custom lua panic function
+ it writes error string to error.log and raises an ELuaException
+ that may be caught }
+function TLua_CustomPanic (L: Plua_State): Integer; cdecl;
+ var
+ Msg: String;
+begin
+ if (lua_isString(L, -1)) then
+ Msg := lua_toString(L, -1)
+ else
+ Msg := 'undefined lua panic';
+
+ Log.LogError(Msg, 'lua');
+
+ raise ELuaException.Create(Msg);;
+
+ Result := 0;
+end;
+
+
+
+end. \ No newline at end of file
diff --git a/Lua/src/ultrastardx.dpr b/Lua/src/ultrastardx.dpr
index c42d8215..296a330d 100644
--- a/Lua/src/ultrastardx.dpr
+++ b/Lua/src/ultrastardx.dpr
@@ -149,6 +149,8 @@ uses
ULuaLog in 'lua\ULuaLog.pas',
ULuaTextGL in 'lua\ULuaTextGL.pas',
ULuaTexture in 'lua\ULuaTexture.pas',
+ UHookableEvent in 'lua\UHookableEvent.pas',
+ ULuaCore in 'lua\ULuaCore.pas',
//------------------------------
//Includes - Menu System