Pluginsystem ... Multithreading möglich?

  • Hi,
    Als Mitglied im "Flutch-Team" bin ich für den Multiplayer verantwortlich. Allerdings wollte ich die Serversoftware nicht ganz so fade gestalten, deshalb habe ich ein "Pluginsystem" entworfen. Das ganze funktioniert so:
    Man schreibt z.B: folgendes Script:

    [autoit]


    #Plugintyp=Commandplugin
    ;Pluginart wird ausgelesen. Später möchte ich noch ein System entwerfen, das einfaches verändern ;der GUI ermöglicht

    [/autoit][autoit][/autoit][autoit]

    Func Testbefehl()
    MsgBox(0,"Gut","Du hast 'Testbefehl' in der Serverconsole eingegeben!")
    EndFunc

    [/autoit]


    , gibt das Plugin in den Pluginordner, beim Serverstart werden dann die einzelnen Funktionen ausgelesen. Wird etwas in der Serverkonsole eingegeben, wird die entsprechende Funktion ausgeführt (Mit einer erweiterten Execute-Funktion, die Schleifen, usw. ermöglicht).

    Das Problem, an das ich blöderweise nicht gedacht habe:
    Wenn ein Plugin ausgeführt wird, das eine Endlosschleife drin hat, gibt es quasi einen Endlos-Lagg, da der Server während dem Ausführen der Pluginfunktionen keine Packets empfangen/verarbeiten/schicken kann. Eine Lösung währe Multithreading, allerdings bin ich bis jetzt auf keine funktionierende UDF gestoßen ...

    Hat jemand eine Lösung?

  • Ein solches problem hatte ich bei meinem Server auch.
    Meine lösung war schmeiß die schleife über bord.
    Ich habe meine befehle die öfters ausgeführt werden müssen versucht in die main einzubauen.
    Also jedes mal wenn der server main passiert rattert er die anstehenden befehle der user ab.

    Bei mir war das problem das ich eine ewig lange text file zeilenweiße übertragen wollte.
    Das darf man logischer weiße nicht auf einmal machen sondern immer nur stückweiße.
    Also wurde am ende von main die zeile eingelesen und verschickt.
    das dauert länger ... blockiert aber nicht die anderen user^^

    EDIT:
    Und das eben so lange bis EOF anstand.

  • Ich würde das so umsetzen.

    Statt die kompletten Funktionen ins Mainscript zu holen nur einfach mit "Run" das Plugin starten. So startet das Script in einem eingen Thread und du kannst auch beliebige Paramter übergeben.

    Andy hat mir ein Schnitzel gebacken aber da war ein Raupi drauf und bevor Oscar das Bugfixen konnte kam Alina und gab mir ein AspirinJunkie.

  • Zitat

    So startet das Script in einem eingen Thread und du kannst auch beliebige Paramter übergeben.

    Wie starte ich einen eigenen Thread?
    Danke für die Antworten!

    Edit: Also ist das Plugin schon eine Exe?
    Edit2: Gute Idee setzte es wahrscheinlich so um.

  • Das ganze kannst auch noch schön erweitern.

    Du kannst eine zentrale ini machen in der die Plugins eintragen mit welchen Parametern sie aufgerufen werden können. Wenn ein Plugin nun startet schaut es ob seine Paramter schon in der Ini steht und wenn nicht schreibt es diese rein.

    Der Server kann nun einfach die Ini einlesen und die Plugins ausführen.

    Das ganze kannst sogar so weit treiben, dass der Server bei einem neuen Plugin nichtmal gestoppt/neugestartet werden muss. Also plugin on the fly. Dazu einfach den Server preidisch das Pluginverzeichniss scannen und schauen ob ein Plugin im Verzeichniss ist aber noch nicht in der ini. Dieses wird dann einfach direkt vom Server gestartet und trägt sich dann in die ini ein.

    Musst dich dann aber halt ein einheitliches Schema entscheiden.

    Andy hat mir ein Schnitzel gebacken aber da war ein Raupi drauf und bevor Oscar das Bugfixen konnte kam Alina und gab mir ein AspirinJunkie.

    Einmal editiert, zuletzt von chip (23. September 2011 um 22:54)

  • Du kannst, von dem Server-Script aus, auch einfach andere AutoIt Scripte direkt aufrufen (diese müssen nicht einmal als .exe vorliegen).

    [autoit]


    ; $sScriptPath = Pfad zu der .au3 Datei, die du ausführen möchtest.
    Run(StringFormat('"%s" /AutoIt3ExecuteScript "%s"', @AutoItExe, $sScriptPath), @ScriptDir, @SW_HIDE, 4 + 2)

    [/autoit]
  • Danke für eure Super-Antworten!
    Allerdings gibt es folgendes Problem: Falls ich es mit der InfiniteCore mache kann das Plugin keine der Funktionen im Hauptscript aufrufen.

    SEuBo Löst das zufällig gerade mein Problem?

    @AutoItExe ... Funktioniert das auch von Computern aus, die kein Autoit drauf haben? Oder muss ich die Exe mitinstallieren?

    Edit:
    Habe es jetzt so umgestaltet:

    [autoit]

    Run(StringFormat('"%s" /AutoIt3ExecuteScript "%s"', @TempDir&"\AutoIt3.exe", '"'&@ScriptDir&'\Plugins\Data.kmd"'), '"'&@ScriptDir&'\'&@ScriptName&'"', @SW_HIDE, 4 + 2)

    [/autoit]


    Allerdings gibt mit Run eine 0 zurück. Was habe ich falsch gemacht?

    Einmal editiert, zuletzt von V8II (26. September 2011 um 17:12)

  • Klick doch mal selber drauf: "Bei kompilierten Skripten der Pfad des laufenden Skriptes.". Das erklärt doch schon alles.
    In jedem AutoIt-Programm ist der AutoIt-Kompiler enthalten und das Skript nur drangehangen, desshalb gibts auch den Parameter "/AutoIt3ExecuteScript" für jedes AutoIt-Programm, dass AutoIt-Skripte auf jedem PC laufen lässt.

    Nur keine Hektik - das Leben ist stressig genug

  • Hi,
    die einfachste Methode ein Pluginsystem zu schreiben, ist eine Plugin-Skriptspache zu entwerfen. Mit einfachen Trigger Callbacks, Zugriff auf die Global Variablen etc...
    Wenn du das ganze in C++ oder anderen High Level Sprachen schreiben willst, kann man auch einen Class Loader benutzen.

  • @AutoItExe ... Funktioniert das auch von Computern aus, die kein Autoit drauf haben? Oder muss ich die Exe mitinstallieren?


    Wie AntiSpeed schon gesagt hat: Ja.

    Zitat


    Allerdings gibt mit Run eine 0 zurück. Was habe ich falsch gemacht?


    Probier es mal so rum:

    Hauptskript:

    [autoit]

    $sScriptPath = @DesktopDir & '\someplugin.au3'
    $sParameters = '... das sind willkürliche informationen ... '
    Run(StringFormat('"%s" /AutoIt3ExecuteScript "%s" "%s"', @AutoItExe, $sScriptPath, $sParameters), @ScriptDir, @SW_HIDE)

    [/autoit]

    someplugin.au3:

    [autoit]

    MsgBox(0,'',$CmdLine[1])

    [/autoit]

    /Edit
    Achso: Das ganze muss nicht mit Stringformat gelöst werden. Wenn dir das lieber ist, dann mach es so:

    [autoit]

    Run('"' & @AutoItExe & '" /AutoIt3ExecuteScript "' & $sScriptPath & '" "' & $sParameters & '"', @ScriptDir, @SW_HIDE)

    [/autoit]
  • @AntiSpeed Danke, wusste ich gar nicht. Aber:

    @SEuBo Das Problem ist, dass die Plugins die Funktionen im Hauptscript mit dieser Methode nicht nutzen können ...

    @Sprenger120 Wüsste nicht wie ich das umsetzen sollte ... könntest du ein Beispiel schreiben?

    Aber danke an alle Antworter!

  • Das Problem ist, dass die Plugins die Funktionen im Hauptscript mit dieser Methode nicht nutzen können ...


    Das Stichwort hier ist interprozesskommunikation. Das geht entweder über TCP, über den Stdout-Channel (Also ConsoleWrite & StdoutRead) oder mit Windows-Nachrichten.
    Für letzteres dann hier nochmal ein Beispiel:

    (das kannst du übrigens im Prinzip so übernehmen für deine "plugins". Die aufzurufenden Funktionen werden in einer Warteschleife gehalten, wenn Sie beim Aufruf von PluginCall noch nicht aufgerufen werden können, und dann automatisch ausgeführt.

    Main.au3

    Spoiler anzeigen
    [autoit]

    Global $sScriptPath, $sParameters

    [/autoit] [autoit][/autoit] [autoit]

    $sScriptPath = @ScriptDir & '\someplugin.au3'
    Run(StringFormat('"%s" /AutoIt3ExecuteScript "%s" "%s"', @AutoItExe, $sScriptPath, $sParameters), @ScriptDir, @SW_HIDE)
    WinWait('someplugin')

    [/autoit] [autoit][/autoit] [autoit]

    _PluginCall('someplugin', '_test1')
    _PluginCall('someplugin', '_test3')
    _PluginCall('someplugin', '_test2')

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _PluginCall($sPluginTitle, $sFunction)
    ; original by Prog@ndy ( _SendCopyDataString() )

    [/autoit] [autoit][/autoit] [autoit]

    Local Static $hGUI = -1
    Local Const $WM_COPYDATA = 0x004A
    Local $tCmdStruct = DllStructCreate('Char[' & StringLen($sFunction) + 1 & ']')
    Local $tCOPYDATA = DllStructCreate('Ptr;DWord;Ptr')

    [/autoit] [autoit][/autoit] [autoit]

    If $hGUI = -1 Then $hGUI = GUICreate('main_gui', 1, 1)

    [/autoit] [autoit][/autoit] [autoit]

    DllStructSetData($tCmdStruct, 1, $sFunction)
    DllStructSetData($tCOPYDATA, 1, 1)
    DllStructSetData($tCOPYDATA, 2, StringLen($sFunction) + 1)
    DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tCmdStruct))
    DllCall('User32.dll', 'None', 'SendMessage', 'HWnd', WinGetHandle($sPluginTitle), 'Int', $WM_COPYDATA, 'HWnd', $hGUI, 'Ptr', DllStructGetPtr($tCOPYDATA))
    EndFunc ;==>_SendCopyDataString

    [/autoit]

    someplugin.au3

    Spoiler anzeigen
    [autoit]


    ; ------ your code goes here ------
    Global Const $sPluginTitle = 'someplugin'

    [/autoit] [autoit][/autoit] [autoit]

    Func _Exit()
    Exit
    EndFunc ;==>_Exit

    [/autoit] [autoit][/autoit] [autoit]

    Func _Test1()
    MsgBox(0, '', 'Test1')
    EndFunc ;==>_Test1

    [/autoit] [autoit][/autoit] [autoit]

    Func _Test2()
    MsgBox(0, '', 'Test2')
    EndFunc ;==>_Test2

    [/autoit] [autoit][/autoit] [autoit]

    Func _Test3()
    MsgBox(0, '', 'Test3')
    EndFunc ;==>_Test3
    ; ------------------------------------

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    #include <Misc.au3>
    _Singleton(@ScriptName)

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $WM_COPYDATA = 0x004A
    Global $sFunctions, $aFunctions

    [/autoit] [autoit][/autoit] [autoit]

    GUICreate($sPluginTitle, 1, 1)
    GUIRegisterMsg($WM_COPYDATA, '_MY_WM_COPYDATA')

    [/autoit] [autoit][/autoit] [autoit]

    While Sleep(10)

    [/autoit] [autoit][/autoit] [autoit]

    If $sFunctions <> '' Then

    [/autoit] [autoit][/autoit] [autoit]

    $aFunctions = StringSplit(StringTrimRight($sFunctions, 1), '|', 3)
    For $i = 0 To UBound($aFunctions) - 1
    Call($aFunctions[$i])
    $sFunctions = StringTrimLeft($sFunctions, StringLen($aFunctions[$i]) + 1)
    Next
    EndIf
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    Func _MY_WM_COPYDATA($hWnd, $msg, $wParam, $lParam) ; by Prog@ndy
    Local $Temp = ""
    Local $COPYDATA = DllStructCreate('Ptr;DWord;Ptr', $lParam)
    Local $COPYDATA_StringLen = DllStructGetData($COPYDATA, 2)
    Local $CmdStruct = DllStructCreate('Char[' & $COPYDATA_StringLen & ']', DllStructGetData($COPYDATA, 3))
    $COPYDATA_String = StringLeft(DllStructGetData($CmdStruct, 1), $COPYDATA_StringLen)
    $sFunctions &= $COPYDATA_String & '|'
    EndFunc ;==>_MY_WM_COPYDATA

    [/autoit]
  • Das Stichwort hier ist interprozesskommunikation. Das geht entweder über TCP, über den Stdout-Channel (Also ConsoleWrite & StdoutRead) oder mit Windows-Nachrichten.
    Für letzteres dann hier nochmal ein Beispiel:

    Oder mit dem Varpool von Infinitecore, den auch völlig externe Programme benutzen können :eyeroll:

    MfG