1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen
  2. Bitte schaltet eure Ad Blocker aus. SLinfo kann nur betrieben werden, wenn es durch Werbung Einnahmen erzielt. Vielen Dank!!
    Information ausblenden
  3. Wir freuen uns, wenn du dich in unserem Forum anmeldest. Bitte beachte, dass die Freigabe per Hand durchgeführt wird (Schutz vor Spammer). Damit kann die Freigabe bis zu 24 Stunden dauern.
    Information ausblenden

Auslagern von Funktionen. Wenn ja, wie am besten? Oder sollte man es sein lassen?

Dieses Thema im Forum "Fragen zu Scripting" wurde erstellt von Anluin Resident, 20. April 2015.

  1. Anluin Resident

    Anluin Resident Nutzer

    Beiträge:
    24
    Zustimmungen:
    0
    Punkte für Erfolge:
    1
    Guten Morgen,
    Es ist ja möglich eine Funktion in ein anderes Skript auszulagern...
    Ich würde nun gern zwei sagen von euch wissen:
    1. Wie würdet ihr dieses Problem lösen?
    2. Würdet ihr sowas überhaupt benutzen oder findet ihr das zu Un-Performance?

    Ich finde schade das LSL sowas nicht von Haus aus anbietet ( wäre mir jedenfalls neu ).
    Da man damit nicht nur insgesamt größere Projekte aufbauen könnte, sondern auch Librarys erstellen und verkaufen könnte,
    ohne ein Script Full-Perm weiter geben zu müssen.

    Es wäre interessant, wenn wir ein paar unterschiedliche Lösungsansätze bekommen würden!
    Ich für meinen Teil habe habe mir schon Gedanken dazu gemacht und auch ein Funktionierenden Ansatz erstellt,
    diesen würde ich dann auch später einmal Posten - da ich gerne mal sehen würde wie euer Lösungsansätze aussehen,
    welche Probleme dieser verursacht und ob ihr schon Lösungen für die entstehenden Probleme habt... ^^

    Hier ein kleines Beispiel, wie ein solches Konzept aussehen könnte:
    (Soll das einlesen einer Notecard in "einer Zeile" ermöglichen)

    Example.lsl
    Code:
    list request(key target,list arguments)
    {
    	
    	llMessageLinked(LINK_THIS, (integer)("0x"+(string)llGetInventoryKey(llGetScriptName())),llList2Json(JSON_ARRAY,arguments), llGetInventoryKey(target));
    	//...
    	// Code um den Rückgabewert des anderen Skriptes zu bekommen.
    	//...
    	return ...;
    }
    
    string inline_read_notecard(string name)
    {
    	return llList2String(request("notecard_reader.lsl",[name]),0);		
    }
    
    default
    {
    	state_entry()
    	{
    		llSay(0,inline_read_notecard("example_notecard"));
    	}
    }
    
    notecard_reader.lsl
    Code:
    integer response(list data)
    {
    	//Code zum übergeben des Zückgabewertes (Notecard innhalt) an das Sender-Skript
    	return TRUE;
    }
    key this;
    string notecard_name; 
    integer notecard_line; 
    string data;
    default
    {
    	state_entry()
    	{
    		this=llGetInventoryKey(llGetScriptName());		
    	}
    	link_message(integer link, integer sender, string _arguments, key target)
    	{
    		if(link==LINK_THIS&&target==this)
    		{
    			list arguments = llJson2List(_arguments);
    			notecard_name=llList2String(arguments,0); 
    			llGetNotecardLine(notecard_name,notecard_line);
    		}
    	}
    	dataserver(key id, string line)
    	{  
    		if (line == EOF)
    			response([data]);
    		else
    		{
    			data+=line;
    			notecard_line++;
    			llGetNotecardLine(notecard_name,notecard_line);
    		} 
    	}
    }
    Desweiteren hoffe ich mal wieder das ich hier im richtigen Unterforum gelandet bin und hoffe ihre verzeiht mir falls nicht.
    Liebe Grüße, Anluin
     
  2. Mareta Dagostino

    Mareta Dagostino Superstar

    Beiträge:
    1.315
    Zustimmungen:
    99
    Punkte für Erfolge:
    49
    Hallo Anluin,

    ein in ein zweites Script "ausgelagerter Scriptteil" ist technisch gesehen ein zweites Programm. Also läuft es in einem separaten Prozess und Du kannst notwendigerweise nur über Schnittstellen kommunizieren, die für externe Programme offen sind. Je nach Umfang der zu übertragenden Daten kannst Du dies als Message rübergeben (wie in Deinem Beispiel), oder auf einem unsichtbaren Kanal (negative Nummer z.B. -4826) mit llSay übermitteln, oder für sehr komplexe Dinge auch per http "verschicken". Letzteres ist ziemlicher Aufwand, dafür kann man damit sogar auf externe Server auslagern.

    Wenn Du die Schnittstellen zu Deinem Script offenlegst, kannst Du das auch "no mod" verkaufen. Die sogenannten Applier für Mesh-Mode sind ein typisches Alltagsbeispiel, wo andere Scripte (die HUDs) auf fremde Scripte der Klamottenhersteller zugreifen.

    Liebe Grüße,
    Mareta
     
    Zuletzt bearbeitet: 20. April 2015
  3. Johanna Burnstein

    Johanna Burnstein Freund/in des Forums

    Beiträge:
    749
    Zustimmungen:
    0
    Punkte für Erfolge:
    0
    Das exportieren von Funktionen aus einem Script wäre im Sinne der Übersichtlichkeit beim Programmieren schon eine schöne Sache.

    Man könnte eine externe Entwicklungsumgebung verwenden, die einem bei der Entwicklung unterstützt. z.B. um Code effektiver wiederzuverwenden. Ich habe früher mit einem Eclipse Plugin gearbeitet. lslPlus, wird aber anscheinend seit 5 Jahren nicht mehr weiterentwickelt (iirc hatte er keine Zeit und niemanden gefunden der so fit in Haskell war um es weiterzuführen :) ) lslforge gibt es auch nocht, das letzte Update ist aber auch von 2013.

    Ansonsten gibt es hier eine Übersicht über externe Werkzeuge. Von Syntax Highlighting bis zu Eigenständigen Editoren. http://wiki.secondlife.com/wiki/LSL_Alternate_Editors

    Closed Source Bibliotheken kann man damit aber nicht in der Form Inworld anbieten. Alternativ, den Bibliothekscode mit entsprechender Lizenz gegen Bezahlung bereitstellen, oder ein Interface mit Message oder LinkMessage bereitstellen.

    --

    Ich habe vor einiger Zeit auch das bereitstellen eines Interfaces mit llMessageLinked(...) probiert. Ich habe das dann aber nicht weiter verfolgt. Von daher kann ich nichts zur Performance in Projekten sagen. (Wird aber Unterirdisch sein.)

    Ich habe dazu den integer 'num' kodiert um entsprechend reagieren zu können. iirc So, dass das Bibliotheksscript entscheiden konnte ob es sich um den Funktionsaufruf oder die Rückanwort handelt. (Um somit zu verhindern, dass das Script auf seine eigene Antwort reagiert, da die Scripte sich selber hören.) Dazu eine fortlaufende Nummer, die die Funktion identifiziert und ggf. danach den Rückgabewert mit einen eigenen llMessageLinked(...) zurückgeben.

    Quick'n'Dirty, sowas in der Art:
    Code:
    default
    {
    	state_entry() { }
    	
    	touch_start(integer n)
    	{
    		llMessageLinked(LINK_THIS, 0xAA010001, "2|3", NULL_KEY);
    	}
    	
    	link_message(..., integer num, string param, ...)
    	{
    		if(num & 0xFFFF0000 == 0xAA020000) {
    			llOwnerSay(":: " + param);
    		}
    	}
    }
    
    --
    
    integer MyTest(integer a, integer b)
    {
    	return a + b;
    }
    
    default
    {
    	state_entry() { }
    
    	link_message(..., integer num, string param, ...)
    	{
    		if(num & 0xFFFF0000 == 0xAA010000) {
    			integer fid = num & 0x0000FFFF;
    			if(fid == 1) {
    				list param_list = llParseString2List(param, ["|"], [""]);
    				integer result = MyTest(llList2Integer(param_list, 0), llList2Integer(param_list, 1));
    				llMessageLinked(LINK_THIS, 0xAA02001, (string)result, NULL_KEY);
    			} else if(fid == 2) {
    				...
    			}
    		}
    	}
    }
    
     
    Zuletzt bearbeitet: 20. April 2015
  4. Anluin Resident

    Anluin Resident Nutzer

    Beiträge:
    24
    Zustimmungen:
    0
    Punkte für Erfolge:
    1
    Huhu,
    Glaube du hast mich da etwas missverstanden (oder ich dich jetzt) ^^
    Ich hatte vor Lösungsansätze zu sammeln, wie man solch eine Kommunikation zwischen zwei Skripten Umsätzen kann, ohne das eine art "callback" nötig ist.
    (Sprich das eigentliche Skript wartet solange bis es eine Antwort bekommt, um diese dann als Rückgabewert der eigentlichen Funktion zu benutzen)
     
  5. Johanna Burnstein

    Johanna Burnstein Freund/in des Forums

    Beiträge:
    749
    Zustimmungen:
    0
    Punkte für Erfolge:
    0
    Das wird in der Kommunikation mit externen Modulen nicht funktioniern. LSL ist eine State Machine (die auf Events reagiert) Man muss sich von der Vorstellung einer 'linearen' Programmierung verabschieden, bei der ein Script "Hier anfängt" und "Da aufhört". (Und dazwischen Funktionen aufruft und auf diese wartet.)

    Man muss anders denken beim Entwickeln. Weg von dem was man von C, Pascal, Basic oder "was weiss ich" her kennt. Man könnte das Script eher als abstraktes Objekt betrachten das auf Ereignisse reagiert. *) Und im zuge dessen muss man zur Kommunikation Interfaces verwenden, die besser dazu passen. Exportierte Funktionen sind es in dem Fall nicht, sondern eher dedizierte Callbacks/Eventhandler.



    *) lol, das hört sich ja fast so an wie der Einleitungstext aus dem Turbo Pascal irgendwas Handbuch, wo Mithilfe von Bäumen, Blättern und Häusern versucht wurde eine Einführung in OOP zu geben. :)
     
    Zuletzt bearbeitet: 20. April 2015
  6. Anluin Resident

    Anluin Resident Nutzer

    Beiträge:
    24
    Zustimmungen:
    0
    Punkte für Erfolge:
    1
    Damit hast du schon recht, aber man kann es ja dennoch mit Tricks "erzwingen" ^^
    ( Ob das nun so gut ist, ist ja eine anderer Frage )

    Habt ihr denn keine ( auch wenn es für LSL untypische ist ) Idee zur Umsetzung solch einer Kommunikation? ^^
     
    Zuletzt bearbeitet: 20. April 2015
  7. Archon Short

    Archon Short Administrator Mitarbeiter

    Beiträge:
    5.153
    Zustimmungen:
    736
    Punkte für Erfolge:
    124
    Ich für meinen Teil biete Waffenscripte an.
    Da müssen meine Kunden einen gewissen Zugriff drauf haben.
    Also es müssen Dinge getriggert werden.
    Sounds und Animationen.
    Dies habe ich in einem zweiten Script realisiert, das meine Kunden full perm bekommen.

    Auch hab ich für alle unterschiedlichen Waffen nur ein einziges Kern-Script(no mod/yes copy/yes trans) und ein Initialisierungsscript(no mod/yes copy/no trans).
    Das Ganze ermöglicht es mir dieses Scriptpaket so zu verkaufen, daß nur meine Kunden es benutzen können und deren Kunden wiederum nichts mit herauskopierten Scripten anfangen können.

    Linkmessage bietet sich bei sowas an.

    Ein anderes Großprojekt hab ich auf einer Gor-Sim realisiert.
    Ich hab das komplette Tür-System (CoolDoors) durch ein eigenes System ersetzt.
    Fast der selbe Funktionsumfang, aber nur 3-5% der Scriptlast für die Sim.
    Da die Türen alle die selben Scripte enthalten muss eine Auswertung auf einem zentralen Server auf der Sim erfolgen.
    Früher nutzte man llRegionSay(Kanal,Nachricht) dafür und heute llRegionSayTo(ZielKey,Kanal,Nachricht).
    Klappt wunderbar und so kann ich die Einzelscripte schön klein und schnell halten.
     
  8. Daemonika Nightfire

    Daemonika Nightfire Forumsgott/göttin

    Beiträge:
    7.219
    Zustimmungen:
    151
    Punkte für Erfolge:
    63
    Also mir faellt auch keine andere Moeglichkeit zur kommunikation als die angesprochenen ein.
    Channel Kommunikation bei nicht verlinktem, Linked Message bei verlinkten Objecten und http request fuer gridweite Kommunikation oder externe Datenbanken.

    Bei diesem Beispiel *DS* Visitor Greeter verwende ich auch ein zweites Script um eine kleine Datenbank und Funktionen auszulagern. Das Script gibt unter bestimmten voraussetzungen an das Main-Script einen entsprechenden Wert zurueck, woraus es wiederum reagiert.

    LG
    Dae
     
  9. Anluin Resident

    Anluin Resident Nutzer

    Beiträge:
    24
    Zustimmungen:
    0
    Punkte für Erfolge:
    1
    Huhu,
    ich wollte mal meinen Lösungsansatz "präsentieren".
    Ich nenne das ganze NCR ( Non-Callback-Request ) in der Hoffnung, das es so was noch nicht gibt :p
    Wie der Name schon sagt beschreibt es eine Möglichkeit eine Anfrage an ein Skript zu senden, ohne ein Event als Callback-Funktion zu nutzen.
    Sprich man kann Funktionen mit einem Rückgabewert erstellen, dessen eigentlicher Code in einem anderen Skript ausgelagert und ausgeführt wird.
    Dazu habe ich Linked-Message und die Objektbeschreibung verwendet.

    Ich habe hier versucht, dieses Vorgehen, etwas genauer zu beschreiben:
    [LSL] Auslagern von Funktionen
     
  10. Dianna Loxely

    Dianna Loxely Superstar

    Beiträge:
    1.625
    Zustimmungen:
    2
    Punkte für Erfolge:
    0
    und du bist dir absolut sicher, das linkmessage in script B nicht auch ein Callback/EventHandler ist?

    durch die vielen Stringoperationen wird es leider auch nicht in die Top 10 der Reaktionshitparade kommen.

    Die Einzige, mit SL Mitteln machbare, nicht eventbasierte Ereignisbehandlung, dürfte timergesteuertes Polling (der Objekteigenschaft) sein.
    Und das wäre wohl ziemlich das Ungeeignetste, was mir einfällt.


    MessageLinking ist ansich schon recht flott (definitiv besser als ein offener Listener oder HTTP-Response) aber beschränkt sich in der Reichweite auf das Linkset, was hier ja völlig ok wäre.

    Mit einer geeigneten API (Integersprungliste mit definierter Parameterliste für Aufruf und Antwort) und ebenjenen Messagelinks kommst du schon recht weit mit Auslagern von Funktionen,
    die ja oft benötigt werden, um zB das Speicherlimit abzufangen oder ein zentraler Touchhandler usw usw



    ETA: Willkommen im Forum und im Kreis der Erwachsenen (die sind irgendwie auch Kinder und sogar nicht wie Mr. Miyagi)
     
    Zuletzt bearbeitet: 22. April 2015
  11. Anluin Resident

    Anluin Resident Nutzer

    Beiträge:
    24
    Zustimmungen:
    0
    Punkte für Erfolge:
    1
    Naja, so wie ich linkmessage in meinem Beispiel verwendet habe ( und ich grade keinen mist rede weil ich noch Todmüde bin )
    Ist es ein stinknormaler Event Aufruf und kein Callback-Event Aufruf.
    [​IMG]
    Im Blog habe ich zwei beispiele gebracht:
    1. Wie man es sonst machen würde.
    2. Ohne eigentlichen Callback ( NCR *grins* ).

    Wenn wir nun das Bild oben (quelle ist übrigens Wikipedia ^^) betrachten und die beiden Beispiele damit "Visualisieren" sieht man glaub ich den unterschied, bz. was ich meinte ganz gut.

    Beispiel 1:
    • Skript-A ( state_entry ) : Main program
    • Skript-B ( link_message ) : Library function
    • Skript-A ( link_message ) : Callback function
    Der Rückgabewert geht zwar an das "Absender"-Skript, jedoch nicht an die "Absender"-Funktion sondern wird an ein anderes Event übergeben und erst dort ausgewertet.

    Beispiel 2:
    • Skript-A ( request ) : Main program
    • Skript-B ( link_message ) : Library function
    • Skript-A ( request ) : Main program
    Der Rückgabewert gelangt an das "Absender"-Skript und gelangt wieder in das "Main program" - kann zbs. weiterverarbeitet werden bevor es das "Main program" via return "verlässt".
    Die Response-Funktion im Skript-B ist hier gleichzusetzen mit dem allzeit bekannten return ...;

    Der "vorteil" bei NCR liegt nun darin, das man sich im eigentlichen Skript nicht mehr um Handler kümmern muss ( bei mehreren anfragen z. b. ) sondern dies alles in der Library gemacht werden kann & soll.

    _____
    Ich hoffe ich habe nun nicht zu großen Mist in meiner morgendlichen Müdigkeits-Phase gelabert.
    Und danke für dieses herzliche willkommen heißen ^^
     
  12. Brigitt Loening

    Brigitt Loening Superstar

    Beiträge:
    1.582
    Zustimmungen:
    151
    Punkte für Erfolge:
    63
    Also die beiden Warteschleifen "while(llGetObjectDesc()!=...){}" in Skript A und "while(llGetObjectDesc()!=""){}" in Skript B sind nicht anderes als Dauerpolling ohne Wartezeit zwischendurch. Ich hab es nicht probiert, aber es dürfte ziemlich lastintensiv sein, wenn dann auch noch mehr Skripte so warten.

    Grundsätzlich find ich die Idee nicht schlecht, aber diese Lösung finde ich "suboptimal". Allerdings kann ich zur Zeit auch keine bessere anbieten.
     
  13. Johanna Burnstein

    Johanna Burnstein Freund/in des Forums

    Beiträge:
    749
    Zustimmungen:
    0
    Punkte für Erfolge:
    0
    Wenn ich das richtig verstanden habe benutzt du das Description Feld des Prims zum Datenaustausch.

    Ein Problem, das ich sehe, ist dass das Feld für die Beschreibung in der Länge beschränkt ist. (iirc 128 Zeichen.)

    Für kleine Werte mag das reichen, aber ich könnte mir vorstellen, dass man doch schnell an eine Grenze stößt. Zumal, in deinem Beispiel, die Datenmenge durch das Base64 auch noch mal aufgebläht wird.

    Könnte man vielleicht mit einem llSleep(...) abfangen. (Ist llSleep(0.0) nicht auch sowas wie ein yield()?)


    Apropos IPC und Synchronisation: Der Zugrif auf das Description Feld müsste eigentlich Synchronisiert werden. Gibt es irgendwas in der LSL Welt, was man z.B. als Mutex/Lock (oder ähnlichem) verwenden könnte? (Bei zwei einfachen Scripten, wie in dem Beispiel, fallen solche Fallstricke ja nicht auf. Was aber wenn mehrere Scripte beteiligt sind?)

    Vielleicht könnte man das erste Zeichem im Description Feld codieren. Um so eine (pseude) Synchronistation zu erreichen, die den Anforderungen der meisten Scripte in Second Life genügt.
     
    Zuletzt bearbeitet: 22. April 2015
  14. Anluin Resident

    Anluin Resident Nutzer

    Beiträge:
    24
    Zustimmungen:
    0
    Punkte für Erfolge:
    1
    Huhu, alle eure bedenken sind gerechtfertigt - treffen nur nicht zu bz. kann man leicht umgehen!
    Im Blog habe ich nur ein kleines einfaches beispiel genannt - für meine momentanen "Experimente" habe ich fortgeschrittenere Funktionen entworfen.

    Wenn sie länger laufen hast du das schon rech - jedoch sind es maximal wenige Sekunden die benötigt werden, im Grunde würde auch ein einfaches llSleep ausreichen, ich habe mich jedoch für diesen weg des Dauerpollings entschieden - da man somit z.b. auch Zeit intensiveren Code im Library-Skript ausführen kann ( z.b. dataserver abfrage oder ein httprequest ).

    Im meinem Blog präsentiere ich ein Sehr simples Konstrukt, das wie du schon sagtest nur einen Rückgabewert mit Maximal 128 Zeichen erlaubt.
    Dieses problem ist mir jedoch selbst schon frühzeitig aufgefallen und wurde relativ simpel in späteren generationen der beiden Funktionen ( request, response ) gelöst,
    indem ich den Rückgabewert in 128Zeiche-Stücken zerstückel, diese an das Request-Skript übergebe, das Request-Skript diese dann nacheinander in einen Buffer-String steckt und erst am ende "freigibt".

    Response-Skript:
    Code:
    for(index=0;index<length;index+=127)
    {	
        while(llGetObjectDesc()!=""){} 
        sub = llGetSubString(source,index,index+126);
        llSetObjectDesc(sub); 
    }
    Request-Skript:
    Code:
    while(TRUE)
    {
        llSetObjectDesc("");
        while(desc==""){desc=llGetObjectDesc();}
        if(desc=="_"+(string)this)
        {
            llSetObjectDesc("");
            return llJson2List(llBase64ToString(buffer));
        }
        else    
        {
            desc=llGetObjectDesc();
            buffer+=desc;
        }
    }
    (die beiden Code-Schnipsel stammen aus einen dahingeschmierten versuch, also einfach nur überfliegen... ^^)
    Muss man ja auch nicht nutzen - war nur just 4 Fun. ^^

    Der Idee gehe ich bereits nach - ich bin grade dabei einen Timeout einzubauen der zugleich mit einem llSleep in den while-Schleifen korrespondiert um bei einem unerwarteten Abbruch,einem Fehler im Skript oder gar bei falscher Handhabung des Kommunikations-Protokolls das Skript nicht unendlich lange Laufen zu lassen und um dem Server etwas Last von den Schultern zu nehmen.

    Nette Idee - werde ich mal ausprobieren.
    Bis jetzt hatte ich einfach alle anderen Skripte im Prim-Inventar auf Standby geschaltet bis die Übertagung beendet war - deine Idee scheint da besser zu sein, dank dir! ^-^