annuncio

Comprimi
Ancora nessun annuncio.

[EASYUO] Advanced Scripting Tecniques

Comprimi
X
 
  • Filtro
  • Ora
  • Visualizza
Elimina tutto
nuovi messaggi

  • [EASYUO] Advanced Scripting Tecniques

    Vorrei condividere un pò della mia conoscenza di EasyUO con il resto della comunità di UODreams. Invito altri scripters a fare altrettanto.
    Sia chiaro che questo non è un thread per assoluti principianti: si suppone che i lettori abbiano già una buona conoscenza delle caratteristiche più comuni del linguaggio.

    Iniziamo con un argomento che mi sta molto caro: la sub "safecall", e tutto ciò che gira intorno ad essa.

    Safecall

    Problema:
    Si vorrebbe fare in modo che ogni sub abbia un proprio namespace locale, nel quale poter allocare variabili in assoluta sicurezza (come succede in TUTTI i linguaggi di programmazione moderni).
    Tuttavia, la gestione dei namespace di EasyUO è ORRIBILE. E presto non se ne può più di scrivere
    codice:
    sub mySub
        namespace push
        namespace local mySub , #random , #systime
        [...]
        set #result xyz
        [...]
        namespace clear
        namespace pop
    return #result
    Oltre che essere noioso, tale approccio impedisce l'uso delle return anticipate, che sono estremamente comode.
    Per chi non fosse pratico di namespaces: un namespace permette di utilizzare variabili locali. Le variabili locali sono variabili scritte con il punto esclamativo (esempio: !variabile). Una variabile locale esiste solo all'interno del suo namespace, e viene distrutta quando si richiama "namespace clear". Questo serve ad evitare conflitti di nomi fra le variabili (da qui il termine "namespace", ovvero "spazio dei nomi").

    Soluzione:
    Anziché richiamare direttamente la sub interessata...
    codice:
    gosub mySub arg1 arg2 arg3
    ...si potrebbe richiamare una sub intermedia, che si occupi di settare correttamente il namespace e che, successivamente, richiami la sub interessata. Il nome della sub interessata diventerà il primo argomento della sub intermedia, dando luogo ad un codice di questo tipo:
    codice:
    gosub safecall mySub arg1 arg2 arg3
    safecall è la sub intermedia di supporto. Come è scritta questa sub? Eccovi un esempio:
    codice:
    sub safecall ; %0: numero_TOTALE_argomenti; %1: nome_sub; %2: argomento1; %3: argomento2;
        namespace push
        namespace local safecall , #systime , #random ; genera in namespace con un nome casuale
    
        set %0 %0 - 1 ; converte dal numero di argomenti passati a safecall
                      ; al numero di argomenti della sub da richiamare
    
        goto _safecall_ , %0 ; si sposta nella label contrassegnata da _safecall_<numero_argomenti_sub_da_richiamare>
    
        ; si arriva qui solo se la label non è riconosciuta (numero args errato o eccessivo)
        event exmsg #charid 3 0 ERRORE!
        goto _safecall_out
    
        _safecall_0: ; la sub viene richiamata con 0 argomenti
            gosub %1
            goto _safecall_out
    
        _safecall_1: ; la sub viene richiamata con 1 argomento, situato in %2
            gosub %1 %2
            goto _safecall_out
    
        _safecall_2: ; la sub viene richiamata con 2 argomenti, situati in %2 e %3
            gosub %1 %2 %3
            goto _safecall_out
    
        [...]
    
        _safecall_10: ; la sub viene richiamata con 10 argomenti, da %2 a %11
            gosub %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11
            goto _safecall_out
    
        _safecall_out:
        
        ; dopo che la sub è stata richiamata, provvedi a rimuovere il namespace
        namespace clear
        namespace pop
    return #result ; restituisci lo stesso #result della sub richiamata
    Utilizzando questa sub per richiamare indirettamente tutte le altre, vi sarà possibile EVITARE COMPLETAMENTE di utilizzare in namespace in maniera esplicita all'interno delle altre sub. Dovete solo prendere l'abitudine di scrivere sempre "safecall" subito dopo "gosub".

    Quando non funziona
    Purtroppo, per come è fatto EasyUO, non c'è modo di scrivere una safecall che accetti un numero illimitato di argomenti. Personalmente posso dirvi che il 99% delle sub non supera i 10 argomenti di input. E quell'1% potete sempre gestirlo "alla vecchia maniera".

    Un vantaggio curioso
    Come "effetto collaterale" di questo sistema, le vostre ricerche all'interno del codice saranno più semplificate!
    Il più diffuso dei miei script, per esempio, è lungo circa 10.000 righe! L'unico modo per spostarsi da una sub all'altra è usare Ctrl+F (Edit->Find). Ma se cerco "sub miaSub", trovo anche tutte le righe del tipo "gosub miaSub"!!!
    Da quando ho cominciato ad usare "safecall", questo problema è sparito da solo. Adesso, le righe con "gosub safecall miaSub" non vengono selezionate quando cerco "sub miaSub", così trovo il corpo della sub immediatamente!

    Codice Completo
    Il codice che segue costituisce l'inizio di tutti i miei script. Oltre a dichiarare la sub safecall, rende anche lo script una libreria. Chiunque voglia richiamare dall'esterno una sub all'interno dello script, può usare:
    codice:
    call NomeScript.txt nomeSub arg1 arg2 arg3
    Non c'è bisogno di inserire "safecall" prima di "nomeSub" ma, anche se lo si fà, non fa differenza. Affronteremo l'argomento della scrittura di librerie nel prossimo post, al quale vi rimando per la comprensione di certe sezioni del codice seguente:
    codice:
    ;===========================================================================================
    ;* @name     Intestazione standard di inizio script.
    ;* @purpose  Si assicura che lo script possa essere richiamato anche dall'esterno,
    ;*           e che configuri correttamente i namespaces.
    ;*
    ;* @example  call NomeScript.txt           ; richiama lo script in modalità "standalone"
    ;*           call NomeScript.txt main      ; richiama la procedura principale dello script
    ;*           call NomeScript.txt nomesub   ; richiama una particolare sub dello script
    ;*                                         ; (per finalità di testing o di libreria)
    ;*
    ;* @warning  E' importante terminare lo script con gosub quit, anzichè con halt o exit!
    ;*
    
    if %0 = !null
    {
       namespace push
       namespace local ScriptName_standalone
       set !lpc #lpc
       set #lpc 500
       gosub _indirectInvokeEx 0 main
    }
    else
    {
       namespace push
       namespace local ScriptName_reentrant
       set !lpc #lpc
       set #lpc 500
       if %0 = 0
       {
           gosub _indirectInvokeEx 0 main
       }
       else
       {
           set %0 ( %0 - 1 )
           gosub _indirectInvokeEx %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11
       }
    }
    gosub quit
    
    sub quit
        while ( #nsname <> ScriptName_standalone ) && ( #nsname <> ScriptName_reentrant )
        {
            namespace clear
            namespace pop
        }
        if #nsname = ScriptName_standalone
        {
            halt
        }
        set #lpc !lpc
        namespace clear
        namespace pop
    exit #result
    
    ;===============================================================================
    ;* @name     _indirectInvokeEx
    ;* @ver      1.0 (09/04/2009)
    ;* @author   AG
    ;* @purpose  Indirectly calls a specified sub, creating a namespace for it.
    ;*
    ;* @params   %1       req  number of arguments passed to the given sub
    ;* 				   %2       req  name of the sub to call
    ;* 				   %3...%12 opt  arguments to pass to the given sub (maximum of 10)
    ;* @returns                #result of the sub
    ;*
    ;* @example  gosub _indirectInvokeEx 3 mysub arg1 arg2 arg3
    ;* @status   Tested and working.
    ;*
    
    sub _indirectInvokeEx ; %argc(of given sub) %subname %args[10]
        namespace push
        namespace local indirectInvoke_ , #systime , #random
    
        goto _indirectInvokeEx_ , %1
    
        ; si arriva qui solo se la label non è riconosciuta (numero args errato)
        event exmsg #charid 3 0 ATTENZIONE! Il numero degli argomenti di _indirectInvokeEx è errato ( %1 )! Lo script andrà in pausa.
        pause
        goto _indirectInvokeEx_out
    
        _indirectInvokeEx_0:
            gosub %2
            goto _indirectInvokeEx_out
        _indirectInvokeEx_1:
            gosub %2 %3
            goto _indirectInvokeEx_out
        _indirectInvokeEx_2:
            gosub %2 %3 %4
            goto _indirectInvokeEx_out
        _indirectInvokeEx_3:
            gosub %2 %3 %4 %5
            goto _indirectInvokeEx_out
        _indirectInvokeEx_4:
            gosub %2 %3 %4 %5 %6
            goto _indirectInvokeEx_out
        _indirectInvokeEx_5:
            gosub %2 %3 %4 %5 %6 %7
            goto _indirectInvokeEx_out
        _indirectInvokeEx_6:
            gosub %2 %3 %4 %5 %6 %7 %8
            goto _indirectInvokeEx_out
        _indirectInvokeEx_7:
            gosub %2 %3 %4 %5 %6 %7 %8 %9
            goto _indirectInvokeEx_out
        _indirectInvokeEx_8:
            gosub %2 %3 %4 %5 %6 %7 %8 %9 %10
            goto _indirectInvokeEx_out
        _indirectInvokeEx_9:
            gosub %2 %3 %4 %5 %6 %7 %8 %9 %10 %11
            goto _indirectInvokeEx_out
        _indirectInvokeEx_10:
            gosub %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12
            goto _indirectInvokeEx_out
        _indirectInvokeEx_out:
    
        namespace clear
        namespace pop
    return #result
    
    ;==================================================================================
    ;* @name     safecall
    ;* @author   AG
    ;* @purpose  Simplified version of _indirectInvokeEx, for internal use by the script.
    ;*           Wraps a namespace around the given sub.
    ;*
    ;* @params   %1       req  name of the sub to call
    ;* 				   %2...%11 opt  arguments to pass to the given sub (maximum of 10)
    ;*
    ;* @returns  #result of the sub
    ;*
    ;* @example  gosub safecall mysub arg1 arg2 arg3
    ;* @status   Tested and working.
    
    sub safecall
        set %0 %0 - 1
        gosub _indirectInvokeEx %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11
    return #result
    
    
    
    ;==================================================================================
    ;* @name     main
    ;* @purpose  Application entry-point.
    
    sub main
        ; Questo è il punto d'ingresso dello script.
    return
    Se non volete mal di testa, fate semplicemente copia-ed-incolla di questo codice all'INIZIO del vostro script, ed usate "safecall" esattamente come ho spiegato.
    Ultima modifica di kusanagi97; 07-07-2009, 04:10.

  • #2
    Tutto molto interessante!

    Originariamente inviato da kusanagi97 Visualizza il messaggio

    Un vantaggio curioso
    Come "effetto collaterale" di questo sistema, le vostre ricerche all'interno del codice saranno più semplificate!
    Il più diffuso dei miei script, per esempio, è lungo circa 10.000 righe! L'unico modo per spostarsi da una sub all'altra è usare Ctrl+F (Edit->Find). Ma se cerco "sub miaSub", trovo anche tutte le righe del tipo "gosub miaSub"!!!
    Da quando ho cominciato ad usare "safecall", questo problema è sparito da solo. Adesso, le righe con "gosub safecall miaSub" non vengono selezionate quando cerco "sub miaSub", così trovo il corpo della sub immediatamente!
    lol, questa è stupenda, non ci avevo ancora fatto caso.

    Commenta


    • #3
      ehm. i realy don't think, that many ppl develop libs on euox )
      if u began describing namespace, i suppose, guide for using common namespace in several scripts is of greater use.
      Ultima modifica di fnurov; 08-07-2009, 14:33.

      Commenta


      • #4
        This *IS* a guide on how to use namespaces! Just a guide on how to use them in a more safe/quick/convenient way. *WEIRDEST* bugs come from bad namespace push/pop (most of the time, people just forget to "namespace pop" due to an early "return" into the sub). This way, you just CANNOT make errors when using namespaces!
        Not to mention the *great* amount of time you save from all that "namespace push/local/clear/pop" writing!

        About the general-purpose namespace guide: it bores me a lot to write it, and I'm sure there are lots of people out there who already wrote one. (by the way... if you'd like to help, you're welcome! )

        Commenta


        • #5
          Grande, grazie! (una nerdata pazzesca)

          Commenta

          Sto operando...
          X