224 lines
8.4 KiB
Markdown
224 lines
8.4 KiB
Markdown
---
|
|
title: "SFINV"
|
|
layout: default
|
|
root: ../..
|
|
idx: 4.7
|
|
description: una mod per rendere più semplice la creazione di un inventario complesso
|
|
redirect_from: /it/chapters/sfinv.html
|
|
---
|
|
|
|
## Introduzione <!-- omit in toc -->
|
|
|
|
Simple Fast Inventory (SFINV) è una mod presente in Minetest Game, usata per creare il [formspec](formspecs.html) del giocatore.
|
|
SFINV ha un'API che permette di aggiungere e altresì gestire le pagine mostrate.
|
|
|
|
Mentre SFINV di base mostra le pagine come finestre, le pagine sono chiamate tali in quanto è assolutamente possibile che una mod o un gioco decidano di mostrarle in un altro formato.
|
|
Per esempio, più pagine possono essere mostrate nel medesimo formspec.
|
|
|
|
- [Registrare una pagina](#registrare-una-pagina)
|
|
- [Ricevere eventi](#ricevere-eventi)
|
|
- [Condizioni per la visualizzazione](#condizioni-per-la-visualizzazione)
|
|
- [Callback on_enter e on_leave](#callback-onenter-e-onleave)
|
|
- [Aggiungere a una pagina esistente](#aggiungere-a-una-pagina-esistente)
|
|
|
|
## Registrare una pagina
|
|
|
|
SFINV fornisce la funzione chiamata `sfinv.register_page` per creare nuove pagine.
|
|
Basta chiamare la funzione con il nome che si vuole assegnare alla pagina e la sua definizione:
|
|
|
|
```lua
|
|
sfinv.register_page("miamod:ciao", {
|
|
title = "Ciao!",
|
|
get = function(self, player, context)
|
|
return sfinv.make_formspec(player, context,
|
|
"label[0.1,0.1;Ciao mondo!]", true)
|
|
end
|
|
})
|
|
```
|
|
|
|
La funzione `make_formspec` circonda il formspec con il codice di SFINV.
|
|
Il quarto parametro, attualmente impostato a `true`, determina se l'inventario del giocatore è mostrato.
|
|
|
|
Rendiamo le cose più eccitanti; segue il codice della generazione di un formspec per gli admin.
|
|
Questa finestra permetterà agli admin di cacciare o bannare i giocatori selezionandoli da una lista e premendo un pulsante.
|
|
|
|
```lua
|
|
sfinv.register_page("mioadmin:mioadmin", {
|
|
title = "Finestra",
|
|
get = function(self, player, context)
|
|
local giocatori = {}
|
|
context.mioadmin_giocatori = giocatori
|
|
|
|
-- Usare un array per costruire un formspec è decisamente più veloce
|
|
local formspec = {
|
|
"textlist[0.1,0.1;7.8,3;lista_giocatori;"
|
|
}
|
|
|
|
-- Aggiunge tutti i giocatori sia alla lista testuale che a quella - appunto - dei giocatori
|
|
local primo = true
|
|
for _ , giocatore in pairs(minetest.get_connected_players()) do
|
|
local nome_giocatore = giocatore:get_player_name()
|
|
giocatori[#giocatori + 1] = nome_giocatore
|
|
if not primo then
|
|
formspec[#formspec + 1] = ","
|
|
end
|
|
formspec[#formspec + 1] =
|
|
minetest.formspec_escape(nome_giocatore)
|
|
primo = false
|
|
end
|
|
formspec[#formspec + 1] = "]"
|
|
|
|
-- Aggiunge i pulsanti
|
|
formspec[#formspec + 1] = "button[0.1,3.3;2,1;caccia;Caccia]"
|
|
formspec[#formspec + 1] = "button[2.1,3.3;2,1;banna;Caccia e Banna]"
|
|
|
|
-- Avvolge il formspec nella disposizione di SFINV
|
|
-- (es: aggiunge le linguette delle finestre e lo sfondo)
|
|
return sfinv.make_formspec(player, context,
|
|
table.concat(formspec, ""), false)
|
|
end,
|
|
})
|
|
```
|
|
|
|
Non c'è niente di nuovo in questa parte di codice; tutti i concetti sono già stati trattati o qui in alto o nei precedenti capitoli.
|
|
|
|
<figure>
|
|
<img src="{{ page.root }}//static/sfinv_admin_fs.png" alt="Pagina per gli amministratori">
|
|
</figure>
|
|
|
|
## Ricevere eventi
|
|
|
|
Puoi ricevere eventi formspec tramite l'aggiunta della funzione `on_player_receive_fields` nella definizione SFINV.
|
|
|
|
```lua
|
|
on_player_receive_fields = function(self, player, context, fields)
|
|
-- TODO: implementarlo
|
|
end,
|
|
```
|
|
|
|
`on_player_receive_fields` funziona alla stessa maniera di `minetest.register_on_player_receive_fields`, con la differenza che viene richiesto il contesto al posto del nome del form.
|
|
Tieni a mente che gli eventi interni di SFINV, come la navigazione tra le varie finestre, vengono gestiti dentro la mod stessa, e che quindi non verranno ricevuti in questo callback.
|
|
|
|
Implementiamo ora `on_player_receive_fields` nella mod:
|
|
|
|
```lua
|
|
on_player_receive_fields = function(self, player, context, fields)
|
|
-- evento della lista testuale: controlla il tipo di evento e imposta il nuovo indice se è cambiata la selezione
|
|
if fields.lista_giocatori then
|
|
local event = minetest.explode_textlist_event(fields.lista_giocatori)
|
|
if event.type == "CHG" then
|
|
context.mioadmin_sel_id = event.index
|
|
end
|
|
|
|
-- Al premere "Caccia"
|
|
elseif fields.caccia then
|
|
local nome_giocatore =
|
|
context.myadmin_players[context.mioadmin_sel_id]
|
|
if player_name then
|
|
minetest.chat_send_player(player:get_player_name(),
|
|
"Cacciato " .. nome_giocatore)
|
|
minetest.kick_player(nome_giocatore)
|
|
end
|
|
|
|
-- Al premere "Caccia e Banna"
|
|
elseif fields.banna then
|
|
local nome_giocatore =
|
|
context.myadmin_players[context.mioadmin_sel_id]
|
|
if player_name then
|
|
minetest.chat_send_player(player:get_player_name(),
|
|
"Banned " .. player_name)
|
|
minetest.ban_player(nome_giocatore)
|
|
minetest.kick_player(nome_giocatore, "Banned")
|
|
end
|
|
end
|
|
end,
|
|
```
|
|
|
|
C'è, tuttavia, un problema abbastanza grande a riguardo: chiunque può cacciare o bannare i giocatori!
|
|
C'è bisogno di un modo per mostrare questa finestra solo a chi ha i privilegi `kick` e `ban`.
|
|
Fortunatamente, SFINV ci permette di farlo!
|
|
|
|
## Condizioni per la visualizzazione
|
|
|
|
Si può aggiungere una funzione `is_in_nav` nella definizione della pagina se si desidera gestire quando la pagina deve essere mostrata:
|
|
|
|
```lua
|
|
is_in_nav = function(self, player, context)
|
|
local privs = minetest.get_player_privs(player:get_player_name())
|
|
return privs.kick or privs.ban
|
|
end,
|
|
```
|
|
|
|
Se si ha bisogno di controllare un solo privilegio o si vuole eseguire un `and`, si bisognerebbe usare `minetest.check_player_privs()` al posto di `get_player_privs`.
|
|
|
|
Tieni a mente che `is_in_nav` viene chiamato soltanto alla generazione dell'inventario del giocatore.
|
|
Questo succede quando un giocatore entra in gioco, si muove tra le finestre, o una mod richiede a SFINV di rigenerare l'inventario.
|
|
|
|
Ciò significa che hai bisogno di richiedere manualmente la rigenerazione del formspec dell'inventario per ogni evento che potrebbe cambiare il risultato ti `is_in_nav`.
|
|
Nel nostro caso, abbiamo bisogno di farlo ogni volta che i permessi `kick` o `ban` vengono assegnati/revocati a un giocatore:
|
|
|
|
```lua
|
|
local function al_cambio_privilegi(nome_target, nome_garante, priv)
|
|
if priv ~= "kick" and priv ~= "ban" then
|
|
return
|
|
end
|
|
|
|
local giocatore = minetest.get_player_by_name(nome_target)
|
|
if not giocatore then
|
|
return
|
|
end
|
|
|
|
local contesto = sfinv.get_or_create_context(giocatore)
|
|
if contesto.page ~= "mioadmin:mioadmin" then
|
|
return
|
|
end
|
|
|
|
sfinv.set_player_inventory_formspec(giocatore, contesto)
|
|
end
|
|
|
|
minetest.register_on_priv_grant(al_cambio_privilegi)
|
|
minetest.register_on_priv_revoke(al_cambio_privilegi)
|
|
```
|
|
|
|
## Callback on_enter e on_leave
|
|
|
|
Un giocatore *entra* in una finestra quando la finestra è selezionata e *esce* dalla finestra quando un'altra finestra è prossima a essere selezionata.
|
|
Attenzione che è possibile selezionare più pagine alla volta se viene usata un tema personalizzato.
|
|
|
|
Si tenga conto, poi, che questi eventi potrebbero non essere innescati dal giocatore, in quanto potrebbe addirittura non avere un formspec aperto in quel momento.
|
|
Per esempio, `on_enter` viene chiamato dalla pagina principale anche quando un giocatore entra in gioco, ancor prima che apri l'inventario.
|
|
|
|
Infine, non è possibile annullare il cambio pagina, in quanto potrebbe potenzialmente confondere il giocatore.
|
|
|
|
```lua
|
|
on_enter = function(self, player, context)
|
|
|
|
end,
|
|
|
|
on_leave = function(self, player, context)
|
|
|
|
end,
|
|
```
|
|
|
|
## Aggiungere a una pagina esistente
|
|
|
|
Per aggiungere contenuti a una pagina che già esiste, avrai bisogno di sovrascrivere la pagina e modificare il formspec che viene ritornato:
|
|
|
|
```lua
|
|
local vecchia_funzione = sfinv.registered_pages["sfinv:crafting"].get
|
|
sfinv.override_page("sfinv:crafting", {
|
|
get = function(self, player, context, ...)
|
|
local ret = vecchia_funzione(self, player, context, ...)
|
|
|
|
if type(ret) == "table" then
|
|
ret.formspec = ret.formspec .. "label[0,0;Ciao]"
|
|
else
|
|
-- Retrocompatibilità
|
|
ret = ret .. "label[0,0;Ciao]"
|
|
end
|
|
|
|
return ret
|
|
end
|
|
})
|
|
```
|