diff options
Diffstat (limited to '')
-rw-r--r-- | src/lua/UHookableEvent.pas | 758 |
1 files changed, 379 insertions, 379 deletions
diff --git a/src/lua/UHookableEvent.pas b/src/lua/UHookableEvent.pas index 8ad7ea9c..7193654a 100644 --- a/src/lua/UHookableEvent.pas +++ b/src/lua/UHookableEvent.pas @@ -1,380 +1,380 @@ -{* 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$
- * $Id$
- *}
-
-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
-
- Func: String; //< Name of the global that holds the function
-
- 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(L: PLua_State): 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
- CallinProcess: boolean; //< true if a chain call is in process, to prepare unhooking during calls
- HooksToRemove: array of PHook; // hooks to delete after chaincall
-
- procedure RemoveWaitingHooks;
- 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; Func: 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; override;
- end;
-
-{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
-function PrepareStack_Dummy(L: PLua_State): 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; Func: String);
- var
- Item: PHook;
- P: TLuaPlugin;
-begin
- P := LuaCore.GetPluginById(Parent);
- if (P <> nil) then
- begin
- // get mem and fill it w/ data
- New(Item);
- Item.Handle := NextHookHandle;
- Inc(NextHookHandle);
-
- Item.Parent := Parent;
- Item.Func := Func;
-
- // 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;
-end;
-
-{ removes hooks in HookstoRemove array from chain }
-procedure THookableEvent.RemoveWaitingHooks;
- function IsInArray(Cur: PHook): boolean;
- var I: Integer;
- begin
- Result := false;
- for I := 0 to high(HooksToRemove) do
- if (HooksToRemove[I] = Cur) then
- begin
- Result := true;
- Break;
- end;
- end;
-
- var
- Cur, Prev: PHook;
-begin
- Prev := nil;
- Cur := LastHook;
-
- while (Cur <> nil) do
- begin
- if (IsInArray(Cur)) then
- begin //we found the hook
- if (prev <> nil) then
- Prev.Next := Cur.Next
- else //last hook found
- LastHook := Cur.Next;
-
- //free hooks memory
- Dispose(Cur);
-
- if (prev <> nil) then
- Cur := Prev.Next
- else
- Cur := LastHook;
- end
- else
- begin
- Prev := Cur;
- Cur := Prev.Next;
- end;
- end;
-
- SetLength(HooksToRemove, 0);
-end;
-
-{ unhook by plugin. push true or error string to lua stack }
-procedure THookableEvent.UnHook(L: Plua_State; hHook: Integer);
- var
- Cur, Prev: PHook;
- Len: integer;
-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
- if not CallinProcess then
- begin // => remove it
- if (prev <> nil) then
- Prev.Next := Cur.Next
- else //last hook found
- LastHook := Cur.Next;
-
- //free hooks memory
- Dispose(Cur);
- end
- else
- begin // add to list of hooks to remove
- Len := Length(HooksToRemove);
- SetLength(HooksToRemove, Len + 1);
- HooksToRemove[Len] := Cur;
- end;
-
- //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);
-
- if (Prev <> nil) then
- Cur := Prev.Next
- else
- Cur := LastHook;
- 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
- 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;
- P: TLuaPlugin;
-begin
- Result := nil;
-
- CallinProcess := true;
-
- Cur := LastHook;
- While (Cur <> nil) do
- begin
- P := LuaCore.GetPluginById(Cur.Parent);
- lua_pushboolean(P.LuaState, Breakable);
-
- if (P.CallFunctionByName(Cur.Func, 1 + PrepareStack(P.LuaState), LUA_MULTRET))
- and Breakable
- and (lua_gettop(P.LuaState) > 0) then
- begin //Chain Broken
- Result := P.LuaState;
- Break;
- end;
-
- Cur := Cur.Next;
- end;
-
- RemoveWaitingHooks;
- CallinProcess := false;
-end;
-
-{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
-function PrepareStack_Dummy(L: PLua_State): 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;
- var
- Name: string;
- Event: THookableEvent;
- hHook: integer;
-begin
- Result := 0;
-
- if not lua_isTable(L, 1) then
- LuaL_Error(L, 'Can''t find hook table in LuaHook_Unhook. Please call Unhook with method seperator (colon) instead of a point.');
-
- // get event name
- Lua_GetField(L, 1, 'Event');
- if not lua_isString(L, -1) then
- LuaL_Error(L, 'Can''t get event name in LuaHook_Unhook');
-
- Name := Lua_ToString(L, -1);
-
- // get event by name
- Event := LuaCore.GetEventbyName(Name);
-
- // free stack slots
- Lua_pop(L, Lua_GetTop(L));
-
- if (Event = nil) then
- LuaL_Error(L, PAnsiChar('event ' + Name + ' does not exist (anymore?) in LuaHook_Unhook'));
-
- // get the hookid
- hHook := lua_ToInteger(L, lua_upvalueindex(1));
-
- Event.UnHook(L, hHook);
-end;
-
+{* 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$ + * $Id$ + *} + +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 + + Func: String; //< Name of the global that holds the function + + 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(L: PLua_State): 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 + CallinProcess: boolean; //< true if a chain call is in process, to prepare unhooking during calls + HooksToRemove: array of PHook; // hooks to delete after chaincall + + procedure RemoveWaitingHooks; + 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; Func: 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; override; + end; + +{ the default function for THookableEvent.PrepareStack it don't pass any arguments } +function PrepareStack_Dummy(L: PLua_State): 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; Func: String); + var + Item: PHook; + P: TLuaPlugin; +begin + P := LuaCore.GetPluginById(Parent); + if (P <> nil) then + begin + // get mem and fill it w/ data + New(Item); + Item.Handle := NextHookHandle; + Inc(NextHookHandle); + + Item.Parent := Parent; + Item.Func := Func; + + // 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; +end; + +{ removes hooks in HookstoRemove array from chain } +procedure THookableEvent.RemoveWaitingHooks; + function IsInArray(Cur: PHook): boolean; + var I: Integer; + begin + Result := false; + for I := 0 to high(HooksToRemove) do + if (HooksToRemove[I] = Cur) then + begin + Result := true; + Break; + end; + end; + + var + Cur, Prev: PHook; +begin + Prev := nil; + Cur := LastHook; + + while (Cur <> nil) do + begin + if (IsInArray(Cur)) then + begin //we found the hook + if (prev <> nil) then + Prev.Next := Cur.Next + else //last hook found + LastHook := Cur.Next; + + //free hooks memory + Dispose(Cur); + + if (prev <> nil) then + Cur := Prev.Next + else + Cur := LastHook; + end + else + begin + Prev := Cur; + Cur := Prev.Next; + end; + end; + + SetLength(HooksToRemove, 0); +end; + +{ unhook by plugin. push true or error string to lua stack } +procedure THookableEvent.UnHook(L: Plua_State; hHook: Integer); + var + Cur, Prev: PHook; + Len: integer; +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 + if not CallinProcess then + begin // => remove it + if (prev <> nil) then + Prev.Next := Cur.Next + else //last hook found + LastHook := Cur.Next; + + //free hooks memory + Dispose(Cur); + end + else + begin // add to list of hooks to remove + Len := Length(HooksToRemove); + SetLength(HooksToRemove, Len + 1); + HooksToRemove[Len] := Cur; + end; + + //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); + + if (Prev <> nil) then + Cur := Prev.Next + else + Cur := LastHook; + 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 + 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; + P: TLuaPlugin; +begin + Result := nil; + + CallinProcess := true; + + Cur := LastHook; + While (Cur <> nil) do + begin + P := LuaCore.GetPluginById(Cur.Parent); + lua_pushboolean(P.LuaState, Breakable); + + if (P.CallFunctionByName(Cur.Func, 1 + PrepareStack(P.LuaState), LUA_MULTRET)) + and Breakable + and (lua_gettop(P.LuaState) > 0) then + begin //Chain Broken + Result := P.LuaState; + Break; + end; + + Cur := Cur.Next; + end; + + RemoveWaitingHooks; + CallinProcess := false; +end; + +{ the default function for THookableEvent.PrepareStack it don't pass any arguments } +function PrepareStack_Dummy(L: PLua_State): 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; + var + Name: string; + Event: THookableEvent; + hHook: integer; +begin + Result := 0; + + if not lua_isTable(L, 1) then + LuaL_Error(L, 'Can''t find hook table in LuaHook_Unhook. Please call Unhook with method seperator (colon) instead of a point.'); + + // get event name + Lua_GetField(L, 1, 'Event'); + if not lua_isString(L, -1) then + LuaL_Error(L, 'Can''t get event name in LuaHook_Unhook'); + + Name := Lua_ToString(L, -1); + + // get event by name + Event := LuaCore.GetEventbyName(Name); + + // free stack slots + Lua_pop(L, Lua_GetTop(L)); + + if (Event = nil) then + LuaL_Error(L, PAnsiChar('event ' + Name + ' does not exist (anymore?) in LuaHook_Unhook')); + + // get the hookid + hHook := lua_ToInteger(L, lua_upvalueindex(1)); + + Event.UnHook(L, hHook); +end; + end.
\ No newline at end of file |