O principal interesse é poder executar ações externas de acordo com as ações do usuário no EmulationStation.
Claro, é principalmente no Raspberry Pi e outras placas que permitem uma fácil controle de hardware, que esta funcionalidade revelará todo o seu potencial.
Em um determinado número de eventos do EmulationStation, seremos capazes de iniciar um script (ou um executável), ou enviar uma mensagem MQTT que os scripts irão esperar e processar.
A cada evento, um arquivo é preenchido com um monte de informações que os scripts podem ou não usar.
Começamos com a lista exaustiva de eventos, quando são acionados e se expõem informações adicionais que serão passadas como parâmetros aos scripts.
Evento | Quando? | Parâmetros |
---|---|---|
Start | Iniciando ou reiniciando o EmulationStation | Número de inícios |
Stop | Desligamento do EmulationStation | Número de inícios |
Shutdown | Desligamento completo do sistema | |
Reboot | Reinicialização do sistema | |
Quit | Parando o EmulationStation seguindo uma solicitação externa (parando a case GPI pelo botão liga / desliga, por exemplo) | |
Relaunch | Reiniciando o EmulationStation (por exemplo, gamelist.xml modificado externamente ou atualizando as listas de jogos) | |
SystemBrowsing | O usuário está na lista de sistemas e um novo sistema acaba de ser selecionado. | Nome abreviado do sistema |
GamelistBrowsing | O usuário está em uma lista de jogos e um novo jogo (ou pasta) acaba de ser selecionado. | Caminho do arquivo ROM |
RunGame | Um jogo será iniciado | Caminho do arquivo ROM |
RunDemo | Um jogo será iniciado em modo de demonstração | Caminho do arquivo ROM |
EndGame | Um jogo acabou de ser encerrado | Caminho do arquivo ROM |
EndDemo | A demonstração de um jogo acaba de terminar | Caminho do arquivo ROM |
Sleep | Iniciando o protetor de tela | |
WakeUp | Saindo do protetor de tela | |
ScrapStart | Uma sessão de scrape de vários jogos foi iniciada | |
ScrapStop | Uma sessão de scrape de vários jogos acabou | Número de jogos que fizeram scrape |
ScrapGame | O Scrape de um jogo acaba de ser feito. | Caminho do arquivo ROM |
ConfigurationChanged | Algo mudou na configuração |
Os scripts devem ser colocados em /recalbox/share/userscripts
ou \\recalbox\share\userscripts
ou em subpastas se você quiser organizá-los.
O próprio EmulationStation escolhe o melhor inicializador, se necessário, dependendo da extensão dos scripts:
Todas as outras extensões são consideradas executáveis, e os arquivos são iniciados diretamente.
No momento em que este documento foi escrito, Python3 ainda não era totalmente compatível com o Recalbox!
Cada script/executável é iniciado com os seguintes argumentos:
script -action ação -statefile arquivo_de_estado [-param parâmetro]
Por padrão, os scripts são iniciados para cada evento.
Para filtrar e apenas iniciar o script para determinados eventos direcionados, tudo o que você precisa fazer é indicá-los entre colchetes e separados por vírgulas. Não é sensível a maiúsculas e minúsculas.
Por exemplo: /recalbox/share/userscripts/marquee[start,stop].sh
só será iniciado quando o EmulationStation iniciar ou parar.
Ou: /recalbox/share/userscripts/gamesinfo[browsinggamelist,rungame,rundemo,scrapgame].sh
será iniciado apenas para eventos que dizem respeito diretamente a jogos, para exibir informações do jogo em uma tela secundária, por exemplo.
Todos os scripts são iniciados de forma assíncrona. Ou seja, o EmulationStation continua a ser executado enquanto o script é executado em paralelo.
Na maioria dos casos, não importa, mas há momentos em que queremos bloquear o EmulationStation até que nosso script seja concluído. Um caso de uso típico é um script que seria executado em eventos de reinicialização ou desligamento do sistema.
Nesse caso, precisamos nos certificar de que nosso script seja executado corretamente antes que o sistema inicie o procedimento de desligamento, caso contrário, poderemos perder informações ou coisa pior.
Para que um script rode de forma síncrona, basta colocar (sync)
no nome do arquivo.
Por exemplo: /recalbox/share/userscripts/backup[reboot,shutdown](sync).sh
será executado no desligamento ou reinicialização do sistema. O EmulationStation será bloqueado até o final de sua execução e o desligamento do sistema começará apenas após isso.
Alguns scripts podem precisar ser executados continuamente. Principalmente se forem usados para interceptar mensagens MQTT que veremos um pouco a seguir.
Basta colocar (permanent)
no nome do arquivo de um script para que ele seja iniciado pelo EmulationStation na inicialização.
Se o EmulationStation for reiniciado, os scripts permanentes continuarão a ser executados e não serão reiniciados.
Recalbox incorpora um mini servidor MQTT (Mosquitto) que permite que você faça "publish/subscribe".
Sempre que o EmulationStation executa scripts em um evento, ele também publica o evento (em letras minúsculas) no tópico /Recalbox/EmulationStation/Event
.
Um programa que escuta este tópico pode, portanto, interceptar todos os eventos por um custo quase zero no uso do processador.
Mosquitto fornece 2 pequenos executáveis mosquitto_pub
e mosquitto_sub
que respectivamente permitem publicar uma mensagem em um tópico ou esperar por uma mensagem de um tópico.
Portanto, você pode usar mosquitto_sub
em um script, para ouvir e aguardar os eventos do EmulationStation, da seguinte maneira:
event = $(mosquitto_sub -h 127.0.0.1 -p 1883 -q 0 -t /Recalbox/EmulationStation/Event -C 1)
Este comando bloqueia a execução do script até que um evento seja postado. O script então recupera o tipo de evento na variável event
.
Compreendemos rapidamente o valor dos scripts permanentes: em vez de iniciar um script a cada vez, para cada evento, podemos iniciar um script permanente e fazer com que ele intercepte todos os eventos em um loop. É uma solução muito menos onerosa para o sistema.
O Mosquitto é iniciado explicitamente no loop local IP 127.0.0.1, o que impede seu acesso e uso fora do Recalbox, por razões de segurança óbvias.
Se você quiser mudar sua configuração, verifique aqui: https://mosquitto.org/man/mosquitto-conf-5.html
Em cada evento, o EmulationStation grava um pequeno arquivo no disco ram: /tmp/es_state.inf
.
Este arquivo é um arquivo do tipo ini simples, contendo associações chave=valor.
Este arquivo já está na versão 2.0. Este arquivo continha várias chaves fixas, com valores vazios dependendo do contexto.
A versão 2.0 mantém a compatibilidade com 1.0, mas adiciona chaves fixas e opcionais dependendo do contexto.
Aqui está a lista de chaves / valores disponíveis desde a versão 1.0:
Chave | Valor | Pode estar vazio? |
---|---|---|
System | Nome completo do sistema afetado pelo evento. | Sim |
SystemId | Nome abreviado do sistema afetado pelo evento. | Sim |
Game | Nome completo do jogo afetado pelo evento. | Sim |
GamePath | Caminho completo da rom envolvido no evento. | Sim |
ImagePath | Caminho completo da imagem do jogo afetado pelo evento. | Sim |
State | Contém um dos seguintes valores: playing : um jogo está em andamento demo : um jogo está em demonstração selected : todos os outros casos. |
Não |
E a lista do que está disponível além disso, desde 2.0:
Chave | Valor | Eventos |
---|---|---|
Action | O nome do evento que gerou a gravação do arquivo de estado. | Tudo |
ActionData | Parâmetros do evento, possivelmente vazios | Tudo |
Emulator | Emulador padrão para o sistema em questão. | BrowsingSystem |
Core | Núcleo usado por padrão para o sistema em questão. Pode ter o mesmo valor do emulador para emuladores sem plug-ins como o Amiberry. | BrowsingSystem |
Emulator | Emulador usado para iniciar este jogo. | Jogos (*) |
Core | Núcleo usado para executar este jogo. O mesmo que o núcleo do sistema. | Jogos (*) |
IsFolder | É igual a 1 se uma pasta for selecionada na lista de jogos. 0 se for um jogo. | Jogos (*) |
ThumbnailPath | Caminho completo para a miniatura correspondente ao jogo. (**) | Jogos (*) |
VideoPath | Caminho completo para o vídeo do jogo. (**) | Jogos (*) |
Developer | Nome do desenvolvedor ou estúdio de desenvolvimento. (* *) | Jogos (*) |
Publisher | Nome do editor. (* *) | Jogos (*) |
Players | Número de jogadores. (* *) | Jogos (*) |
Region | Região do jogo. (**) | Jogos (*) |
Genre | Gênero do jogo. (**) | Jogos (*) |
GenreId | Identificador numérico do gênero do jogo. (* *) | Jogos \ (*) |
Favorite | É igual a 1 se o jogo estiver nos favoritos, caso contrário, 0. (**) | Jogos (*) |
Hidden | É igual a 1 se o jogo estiver oculto, caso contrário, 0. (**) | Jogos (*) |
Adult | É igual a 1 se o jogo for classificado como adulto, caso contrário, 0. (**) | Jogos (*) |
(*) Significa em detalhes: GameBrowsing, RunGame, RunDemo, EndGame, EndDemo e GameScrap.
(**) Cada uma dessas informações provém dos metadados associados ao jogo em questão. Ele pode estar vazio se não tiver sido feito scrape no jogo.
Este arquivo é escrito antes que os scripts sejam iniciados e a mensagem MQTT seja enviada. Portanto, ela é válida quando os scripts são executados. No entanto*... Como alguns eventos estão extremamente próximos, é possível que este arquivo já tenha sido alterado por um segundo evento quando você vai lê-lo após um primeiro evento. É, portanto, aconselhável :
- Verificar o valor da chave
action
para ter certeza de que ela corresponde ao evento desejado.- Não assumir que uma chave opcional estará necessariamente presente ou que uma chave fixa terá necessariamente um valor.
Aqui está uma série de dicas para escrever seus scripts. Você é livre para ignorá-las, mas esteja ciente de que a execução de scripts em cada evento do EmulationStation pode ter um impacto no sistema:
Se possível, mantenha seus scripts em um nível mínimo.
Evite scripts que não filtrem eventos. Se o Recalbox acrescentar eventos em versões futuras, seus scripts serão ainda mais solicitados.
Evite scripts síncronos se não for estritamente necessário (durante a fase de desligamento).
Use o ASH
o máximo possível em vez de SH
, é muito mais rápido e otimizado. Por outro lado, tem algumas ligeiras diferenças com SH
. https://pt.wikipedia.org/wiki/Almquist_shell
Se você precisar lidar com muitos eventos, use um único script permanente em conjunto com mosquitto_sub
, você economizará muitos recursos do sistema.