Projeto: codename [Mujk the Gutherin]


#1

Codename [mujk the gutherin]

Projeto de jogo de coleção e troca de cartas multiplayer online estilo “Magick the Gathering: Arena”.

Pessoas: @Jose, @Kazuo, @JoaoG.
Encontros: thread

Sobre

Jogos são 1v1 representam uma Guerra entre facções. Cada lado usa de três recursos para invocar cartas (permanentes ou otherwise) que simbolizam Força de Trabalho; Recursos Básicos (Dinheiro/Comida) e Recursos Especializados (Infraestrutura/Peças Especiais).

As cartas permanentes se organizam em unidades ou platoons, que podem ter uma carta especial de comandante, e uma composição de artilharia, infantaria e cavalaria.

Só tem uma Main phase onde tudo acontece, por turno de jogador. Cada combate é entre platoons, os exércitos são montados, o atacante joga N_0 cartas de efeito ou táticas, o defensor joga M_0 cartas de mesmo tipo, … daí resolve a pilha.

Condições de Vitória:

  • Acabar com os recursos do oponente.
  • Sobreviver X turnos após acabado o baralho do oponente.
  • Cartas de Objetivo
    • Estas cartas determinam um par de condições, de vitória e derrota, alternativas para o jogador que a invoca. Cada jogador só pode escolher um objetivo apenas, que uma vez escolhido, fica revelado.

Technical Details

  • Repositórios
  • Frontend/Cliente: Godot
  • Partida/Instância: C++17
  • Backend/Server : Um dockerzão com um server C++?

Formas de Fazer TCP com SSL para o Projeto:

  • Usar GDNative para criar soquetes etc.
    • CPP and GDNative part I.
    • “we chose to have a C API which is wrapping the C++ calls”
      • [cries in calling-convention]
    • força muito código ir para dentro da Godot, I am afraid. Não sei, mas é a impressão.
  • Usar o sys/socket.h e deixar o UwUindows chorar por não ser POSIX.
    • Vantagem: punir usuários de OwOindows e não POSIX compliants.
  • Aproveitar que nim compila para C++ ou C, e fazer um mini módulo na língua usando a biblioteca padrão net. Daí a gente exporta os símbolos e só linka com o resto do sistema. Deveria ser fácil e portável.
    • “OpenSSL can also be statically linked using --dynlibOverride:ssl”.
    • Desvantagem: O compilador não vai poder aggressively otimizar as chamadas via externs, unless we transpile to C++ antes de linkar… É uma otimização precoce? Sim.

#2

@Kazuo vamos marcar um dia para conversar?


#3

Bora! Quer já marcar pra semana que vem?

Also, era o @JoaoG que tava interessado também?


#4

Pode ser, qualquer hora semana que vem ─ quando fica bom para vocês?


#5

Eu mesmo o/

Se vcs estão falando da semana do carnaval, em teoria eu posso em qualquer horário e qualquer dia a partir de quarta, mas meus compromissos vão ser meio nebulosos nela. Pra garantir acho que seria melhor se fosse depois das 16h ou exatamente as 8h.


#6

Por mim tudo bem, 4ª feira 26/02 às 16h? Parece bom?


#7

Parece bom!


#8

Fotos da reunião 0x00


#9


#10

Vamos fazer um Repo? Onde ceis querem fazer? Github para milkar aquela camiseta anual ou gitlab?
Temos que fazer um projeto/subgrupo para este projeto porque são duas aplicações independentes:

  • O Cliente Godot
  • A instância de um Jogo

Potencialmente tem mais uma:

  • O Servidor per se que sobe instâncias de Jogo, que autentica usuários e seus decks etc.

#11

Eu estava testando esta possibilidade, consegui produzir objetos para linkagem e parece que funciona. Os únicos problemas são: eu não testei o SSL ainda, só os soquetes; muito provavelmente a gente vai querer que o connect seja async e usar altos Futures… isso não é trivial nem impossível. O nim tem a asyncnet e asyncdispatch como bibliotecas padrão.

O que me dá medo é interagir as coisas async de C++ com as coisas async de NIM, porque tem uns gotchas nessa história de interoperação deles, e o código pode ficar carcinogênico em geral.

A parte boa é que, ao que parece, uma vez que você tenha os soquetes, é trivial usar SSL neles. A parte ruim é que a documentação é péssima em várias partes da língua.


#12

Link: SOCKet ME
Servidor assíncrono de soquetes TCP em NIM. Não faz nada, mas escuta na porta, e sabe contar. Para dar recv e send deveria ser medianamente trivial também.

Eu só queria marcar aqui que eu odiei todo segundo que eu usei esta línugua mal documentada e mau documentada. Sério. Tem coisas na documentação que o compilador não te deixa fazer. Metade dos métodos não tem nem explicação de como usa. Foi o satanais que escreveu, e ainda se certificou que tem 0 coisas relevantes para a minha pesquisa no stackoverflow.

Tem keywords que não tem explicação nenhuma em nenhuma parte da documentação.

image

Enfim, acho que dá para fazer com mínimo sofrimento agora que eu achei uma forma de registrar clientes de maneira não-blocking.

Tem um senão: não sei quão bem testada é a biblioteca de ssl deles…


#13

A gente desistiu da ideia de fazer com godot ou vc tá só estudando como uma alternativa mesmo?


#14

Eu desisti de estudar a alternativa da Godot porque eu vi mais potencial no Nim, kek.
Mas como a gente não discutiu isso, eu assumo que ainda está na mesa se alguém quiser tentar.

Mas assim, deixa eu dizer porque eu larguei de tentar a ideia da Godot e a gente vê se tentamos fazer um teste ou não.

A primeira coisa que me parece meio chata é que precisa de muito boilerplate code só para usar uma classe: você precisa herdar de GodotScript<Reference>, chamar uma macro dentro do body da tua classe, e registrar seus método, definir umas funções calling convention de C: godot_gdnative_init, godot_gdnative_terminate e godot_nativescript_init onde você chama rolês internos da Godot e, finalmente, registra sua classe.

Primeiro motivo, sub-item A) A única coisa que não escala com o número de coisas que a gente vai deferir para a Godot é a declaração das funções de C, mas a godot_nativescript_init precisa chamar register_class<T>() em todas as classes que a Godot vai ver, e isso significa que a unidade de tradução onde ela estiver implementada vai ter que incluir todas as classes que vierem da Godot, ou algo equivalentemente triste.

A segunda coisa é que o GDNative não deixa (simplesmente, afaik) você chamar as funções da Godot de dentro do C/C++, ela é boa para fazer coisas em C/C++ serem chamadas dentro da Godot. O que significa que o game loop e as coisas que queremos que sejam rápidas, estarão sendo chamadas num _process de um GDscript. Diferente do Nim, que com todos seus defeitos, é trivial de chamar C++ dentro de si e ele dentro de C++. O código vai ficar com esta cara:

main.gd:

_process():
  do_stuff_in_cpp()
  var res = get_resource_in_godot()
  cpp_method_that_uses(res)
  ...

A terceira coisa é que os métodos de C++ vão ter que, necessariamente, incluir, ou predeclarar coisas internas da Godot para receber o, digamos, nó do soquete, ints da Godot, etc. Fazendo que o código da Godot sangre para dentro do nosso código. E sangre mesmo.

O quarto item é que mesmo headless, a Godot muito possivelmente vai ter um senhor overhead em performance quando comparado a rodar coisa que foi compilada para C++ e está estaticamente linkada com o nosso programa e cujo comportamento nós controlamos diretamente. Isso e o executável resultante vai ser relativamente pesado.

O que vocês acham? Estava errado na minha avaliação?


#15

Só algumas correções

O nativescript da Godot tem duas versões, a 1.0, que é usada até a Godot 3.0, e a 1.1, que é usada da Godot 3.1 em diante. Na versão 1.1 não é mais necessário herdar de alguma variação da classe GodotScript<>.

O macro do body da classe, o GodotScript<> e registrar os métodos só servem para o GDScript saber os nomes das coisas.

É possível sim chamar funções da Godot pelo C++ (Obs.: o conjunto de coisas que eu já usei inclui funções globais da Godot, classes de nós e tipos “primitivos” como Dictionary e Array). É possível também criar os métodos _init, _process, etc. na classe C++ e eles são usados sem precisar de implementação em GDScript.

Não sei se eu entendi direito, mas não existe "int da Godot". Quando precisar usar int usamos o do C++, quando tiver que passar para a Godot (como é o caso dos elementos de dicionário ou de array) ela sabe converter int para o tipo Variant sozinha. Inclusive, os tipos recebidos e devolvidos pelas funções C++ não precisam necessariamente ser do tipo Variant, eles podem ser de tipos que podem ser transformados para/a partir de Variant e a Godot sabe tratar isso.

Mas de fato vai ter um overhead por conta do GDScript e da biblioteca dinâmica, o que é um problema se formos escalar o servidor algum dia.

Algumas outras desvantagens que achei no GDNative:

  1. A interação entre o tipo String da Godot com std::string é péssima.
  2. Dependendo das bibliotecas C++ que vamos usar e de como vamos usá-la, talvez teremos que ficar transformando tipos recebidos e devolvidos em quase todas as funções, tipo
Array f(String arg) {
  std::string s = String_to_string(arg);
  vector<int> v = library_func(s);
  return vector_to_Array(v)
}
  1. O error handling para algumas coisas em nativescript é nulo. Por exemplo, o jogo crasha sem nenhum aviso quando passamos em GDSCript o número de argumentos errado ou tipos errados para uma função implementada em C++ (isso em nativescript 1.0).
  2. C++ puro e GDScript têm default argument, mas uma função implementada em C++ e passada para GDScript não pode ter default argument (Ex: se ela tem 3 argumentos e um opcional em C++, quando registrar com register_method ela vai aparecer com 4 argumentos obrigatórios no GDScript).
  3. Não dá para fazer um GDScript extender de uma classe implementada em C++.

#16

Acho que o negocio e fazer em LOVE e usar o framework do @Kazuo (que já usamos em prog para joguinhos).


#17

A gente queria algo com tipagem estática XD


#18

O @JoaoG já fez uma ótima análise dos prós e cons de usar a godot então não sei se ainda é pertinente, mas aqui tá o demo que fiz pra godot meetup:

EDIT

Conversei com uns amigos e sugeriram olhar a Boost.Beast.

Além disso, investigando mais um pouco fui capaz de comprovar minha ignorância sobre tecnologias modernas, pois parece que WebSockets podem ser perfeitamente usados em aplicações que não são navegadores. A Godot, em particular, implementa um que podemos usar (inclusive por NativeScript). Mas, caso queiramos outras opções, aqui tem algumas.