Writing Simba Extensions

Simba extensions are scripts written for the interpreter that can be embedded into Simba. Purposes vary from updaters to editors.

How they work

Extensions are event based. This means you don’t have some loop in your program that never ends and does all the logic for you. When a system is event based, you implement some functions and those are called on a certain event.

Extension core hooks

Simba offers several core hooks: init, free, attach and detach. These are used to initialize, show, hide and free your extension. GetName and GetVersion are called to retreive the name and version of an extension.

init

Called when the Extension is initialized.

procedure init;
begin;
    Writeln('Initialize your extension here.');
end;

If you want to add a button to the menu, do it now. From the SRL updater extension:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
procedure Init;
begin;
  MainMenuItem := TMenuItem.Create(Simba_MainMenu);
  MainMenuItem.Caption := 'SRL';
  Simba_MainMenu.Items.Add(MainMenuItem);

  MenuCheck := TMenuItem.Create(MainMenuItem);
  MenuCheck.Caption := 'Check for new SRL';
  MenuCheck.OnClick := @OnSRLCheckClick;
  MainMenuItem.Add(MenuCheck);

  MenuUpdate := TMenuItem.Create(MainMenuItem);
  MenuUpdate.Caption := 'Update SRL';
  MenuUpdate.OnClick := @OnSRLUpdateClick;
  MainMenuItem.Add(MenuUpdate);

  AutoUpdate := TMenuItem.Create(MainMenuItem);
  AutoUpdate.Caption := 'Automatically update';
  AutoUpdate.OnClick := @SetAutoUpdate;
  AutoUpdate.Checked := LowerCase(Settings.GetKeyValueDef('AutoUpdate',
                                'True')) = 'true';
  MainMenuItem.Add(AutoUpdate);

  Timer := TTimer.Create(Simba);
  Timer.Interval := 5000;
  Timer.OnTimer := @OnUpdateTimer;
  Timer.Enabled :=AutoUpdate.Checked;

  started := True;
end;

free

Called upon freeing the extension. Usually this means that Simba is closed.

procedure free;
begin
    if started then
        writeln('Free() was called');
end;

From the SRL updater extension:

procedure Free;
begin
  if (started) then
    Timer.Enabled := False;
    { Freeing the components is not needed, as they will be freed
       upon the closure of Simba. }
end;

attach

Called when your extension has been enabled.

From the SRL updater extension:

procedure Attach;
begin;
  Writeln('From now on, you shall be alerted as to when your SRL is'+
            +'out of date!');
  MainMenuItem.Visible := True;
  Timer.Enabled := AutoUpdate.Checked;
end;

detach

Called when your extension has been disabled. (This is not the same as freeing)

Procedure Detach;
begin
  Timer.Enabled := False;
  MainMenuItem.Visible := False;
end;

GetName

Called when Simba requests the name of your extension.

function GetName : string;
begin;
  result := 'SRL Updater';
end;

GetVersion

Called when Simba requests the version of the extension.

function GetVersion : string;
begin;
  result := '1.0';
end;

More extension hooks

The following hooks are called upon if the event that the name describes has happened.

EventHooks: Array [0..9] of TEventHook =
(      (HookName : 'onColourPick'    ; ArgumentCount : 3),
   (HookName : 'onOpenFile'      ; ArgumentCount : 2),
   (HookName : 'onWriteFile'     ; ArgumentCount : 2),
       (HookName : 'onOpenConnection'; ArgumentCount : 2),
       (HookName : 'onScriptStart'   ; ArgumentCount : 2),
       (HookName : 'onScriptCompile' ; ArgumentCount : 1),
   (HookName : 'onScriptExecute' ; ArgumentCount : 1),
   (HookName : 'onScriptPause'   ; ArgumentCount : 1),
   (HookName : 'onScriptStop'    ; ArgumentCount : 1),
   (HookName : 'onScriptOpen'    ; ArgumentCount : 1));

For the exact arguments to all these functions, refer to Example code.

onOpenConnection

onWriteFile

onOpenFile

onColourPick

onScriptStart

onScriptOpen

This is a function, and should return the script, plus any modifications.

Special Cases

Multiple extensions hooking the same event

So what happens when multiple extensions hook onto the same event/hook?

The behaviour is currently defined, but prone to change in the near future. Currently all extensions are called in the order they were loaded.

The behaviour will probably change to something like the following:

In the order they were loaded, call any available extensions. The first extension to return non-zero terminates the calling loop.

Pitfalls

Extensions can be very dangerous in the sense that they run on the main thread of Simba; it is very easy to crash Simba or cause it to hang. There is no way to prevent this, so make sure to check what you’re doing before you try your own (or someone else’s) extension.

Example code

Example code for most hooks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
program new;

procedure init;
begin;
  Writeln('init your extension here');
end;
procedure onOpenConnection(var url : string; var Cont : boolean);
begin
  Writeln('Opening url: '  + url);
  Writeln('We shall allow this.. For now!! Gna Gna!');
  Cont := True;
end;
procedure onWriteFile(var FileName : string; var Cont : boolean);
begin
  Writeln('So.. You want to write to file: ' + FileName);
  Writeln('Well for this time only!');
  Cont := True;
end;

procedure onOpenFile(var FileName : string; var Cont : boolean);
begin
  Writeln('So you want to open this file: ' + filename);
  Writeln('Well I don''t care much, lets see what the other hooks think!');
  //Not set Cont as we don't care, while other hooks might
end;

procedure onColourPick(const Colour,x,y : integer);
begin
  Writeln('So you''ve picked a color, huh!?');
  Writeln(inttostr(colour) + ' attuh (' + inttostr(x) +',' + inttostr(y) + ')');
end;
 
function onScriptStart(var Script : string; var Cont : boolean): String;
begin
  Writeln('So you want to compile the following script!!');
  Writeln(script);
  Writeln('lets allow that for now ;)');
  Cont := True;
  Result := Script;
end;

function onScriptOpen(var Script: String): String;
begin
  Result := Script + 'THIS IS AFTER THE .END, HOPEFULLY');
end;

procedure free;
begin
  Writeln('Free your extension here');
end;

procedure Attach;
begin;
  Writeln('Your extension has been enabled, do stuff here');
end;

Procedure Detach;
begin
  Writeln('Your extension has ben disabled, do stuff here');
end;

//Called to retrieve the name of your extension
function GetName : string;
begin;
  result := 'Leet Extension';
end;

//Called to retrieve the version of your extension
function GetVersion : string;
begin;
  result := '0.001';
end;
begin
end.

Note

If you need more examples, you can always look at the Extensions in the Simba git repository.