Servidores de jogos por hora. Pague só quando jogar.

Como rodar um game server só para Windows no Linux

Alguns jogos da Steam só trazem um servidor dedicado para Windows. Não há binário Linux no depot, então o conselho de sempre é "rode em uma máquina Windows." Você não precisa. Com o Wine, você pode rodar o servidor Windows em uma máquina Linux normal, e ele vai se conectar à Steam e aceitar jogadores reais. Esta é a receita exata que funcionou para nós, usando Alien Swarm: Reactive Drop (um jogo co-op Source gratuito) como exemplo. A mesma abordagem deve valer para outros servidores dedicados Source só para Windows.

Primeiro, confira se realmente não existe um servidor Linux

Não presuma. Pergunte direto à Steam antes de recorrer ao Wine. Aponte o steamcmd para o app e leia a lista de plataformas dele:

steamcmd +login anonymous +app_info_print <appid> +quit \
  | grep -iE "oslist|name"

Se o app do servidor dedicado for só "oslist windows", não há build Linux nativo para recorrer. Você também pode tentar forçar uma instalação Linux; quando realmente não há depot Linux, ela falha na hora:

steamcmd +@sSteamCmdForcePlatformType linux \
  +login anonymous +app_update <appid> validate +quit
# ERROR! Failed to install app '<appid>' (Missing configuration)

Esse erro, e nenhum arquivo ELF baixando, confirmam. Agora o Wine é o caminho. Instale o servidor E o app 1007 (os Steamworks Common Redistributables) na mesma pasta, com o tipo de plataforma windows. O app 1007 traz as DLLs da Steam de que o servidor precisa, e você vai ver por quê no Passo 2:

steamcmd +@sSteamCmdForcePlatformType windows +login anonymous \
  +force_install_dir /path/to/server \
  +app_update <appid> validate \
  +app_update 1007 validate \
  +quit

O servidor também precisa do runtime Microsoft Visual C++ 2019 dentro do prefixo do Wine. Instale uma vez com winetricks (ele precisa de display, então envolva com xvfb-run):

WINEPREFIX=/wine xvfb-run -a winetricks -q vcrun2019
wineserver -w

Por que a tentativa ingênua com Wine falha

Rodar o servidor direto do Wine bate em duas paredes seguidas:

  • Sem console: o servidor espera um terminal de verdade. Redirecionar de /dev/null dá CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents e ele nunca inicia.
  • Não foi possível carregar a biblioteca da Steam: mesmo depois de subir, ele imprime "Unable to load Steam support library" e nunca se registra na Steam, então ninguém consegue encontrá-lo nem entrar.

Ambas têm conserto. Aqui vai a receita completa.

Passo 1: dê a ele um terminal de verdade (PTY)

Uma máquina headless não tem terminal, e o servidor de console do Windows se recusa a rodar sem um. Ele precisa de um pseudo-terminal (PTY) cujo lado de entrada fique aberto, que é o que docker run -it te dá. A dica comum é envolver a inicialização com script. Numa máquina sem terminal, esse é o bug: a própria entrada do script chega ao fim do arquivo, a leitura do console do servidor também vê isso, e o loop principal trava. O mapa pode até carregar, mas o servidor nunca responde nos sockets. Em vez disso, inicie por um pequeno wrapper em Python que abre um PTY e nunca o fecha, e use Xvfb como display falso:

Xvfb :99 -screen 0 1024x768x24 &
export DISPLAY=:99 WINEPREFIX=/wine

cat > launch_pty.py <<'EOF'
import os, sys
cmd = ["wine", "srcds_console.exe", "-console", "-condebug",
       "-game", "<gamedir>", "+map", "<map>", "-port", "27015"]
pid, master = os.forkpty()
if pid == 0:
    os.execvp(cmd[0], cmd)
# Parent: never close master, so the server's input stays open.
# Copy the server output to our own stdout.
while True:
    try:
        data = os.read(master, 4096)
    except OSError:
        break
    if not data:
        break
    sys.stdout.buffer.write(data)
    sys.stdout.flush()
_, status = os.waitpid(pid, 0)
sys.exit(os.waitstatus_to_exitcode(status))
EOF
python3 launch_pty.py

Só isso já faz o servidor carregar um mapa e bindar sua porta de jogo. Mas ele ainda não consegue falar com a Steam.

Passo 2: duas DLLs da Steam, duas fontes diferentes

A engine Source precisa das DUAS, steamclient.dll e steam.dll, ao lado do exe do servidor, e aqui está a armadilha que nos custou uma rodada inteira a mais de depuração: elas vêm de lugares DIFERENTES, e o app 1007 só te dá UMA delas.

  • steamclient.dll: vem do app 1007 (Steamworks Common Redistributables), que você instalou no Passo 1. Canônica, com versão compatível, mantida pela Valve.
  • steam.dll: NÃO está no app 1007. Ela faz parte do CLIENTE Steam, e a única forma limpa de obtê-la é rodar o steamcmd.exe do Windows uma vez sob o Wine, que se autoatualiza e deposita steam.dll (mais steamclient.dll) na própria pasta.

Então dê bootstrap na steam.dll com steamcmd.exe sob o Wine, e depois copie as duas DLLs ao lado do exe do servidor:

# steam.dll: run Windows steamcmd once under Wine (needs Xvfb for a display)
cd "$WINEPREFIX/drive_c/steamcmd"
curl -fsSLO https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip
unzip -o steamcmd.zip
DISPLAY=:99 wine steamcmd.exe +quit    # lays down steam.dll + steamclient.dll

# place both next to the server exe
cd /path/to/server
cp "$WINEPREFIX/drive_c/steamcmd/steam.dll" .
[ -f steamclient.dll ] || cp "$WINEPREFIX/drive_c/steamcmd/steamclient.dll" .

Passo 3: por que steam.dll é a peça que importa

Esta é a parte que quase ninguém anota. O erro diz "Unable to load Steam support library," e a biblioteca de suporte a que ele se refere não é a steamclient.dll, é a steam.dll. A engine Source procura a steam.dll ao lado do próprio executável para dar bootstrap em toda a cadeia da Steam. Só com a steamclient.dll presente (que é tudo o que o app 1007 te dá) ainda falha; no momento em que a steam.dll fica ao lado do .exe, o log vira de falha para sucesso:

Connection to Steam servers successful.
VAC secure mode is activated.

O VAC não ativa a menos que o servidor seja um gameserver corretamente logado, então essa linha é sua prova de que ele se registrou. A maioria dos servidores Source loga de forma anônima aqui e não precisa de nenhum Game Server Login Token (GSLT). Só alguns jogos (como CS2) exigem um.

A armadilha: o A2S fica mudo

Esta é a pegadinha que mais nos custou tempo. Com o wrapper script, o servidor NÃO respondia ao A2S_INFO, a consulta UDP que as ferramentas usam para ler o nome, o mapa e o número de jogadores de um servidor. Toda consulta dava timeout, até da mesma máquina, então o servidor parecia completamente morto. Primeiro culpamos o Wine. Era o loop principal travado do Passo 1: o servidor nunca lia os sockets.

Com o PTY mantido aberto, o servidor responde ao A2S normalmente e jogadores reais conseguem entrar. O teste final ainda é um cliente conectando, não uma consulta respondida. Com o PTY certo, um jogador real conectou e jogou:

Client "player" connected (10.0.0.5:27005).
1/ 8 on map <mapname>

A lição prática: se um servidor rodando no Wine carrega o mapa mas o A2S fica mudo, não culpe o Wine primeiro. Veja como o console está conectado. Uma porta UDP aberta não prova que o servidor está vivo, porque um servidor travado mantém a porta aberta enquanto os pacotes se acumulam sem leitura.

Isso funciona para todo jogo de Windows?

Sejamos honestos sobre o escopo. Provamos isso num servidor da engine Source, e a mesma receita (PTY, um runtime montado pelo steamcmd, steam.dll ao lado do exe) deve funcionar para outros servidores dedicados Source só de Windows. Jogos em outras engines integram a Steam de outro jeito, então trate-os como experimentos separados. Uma coisa para esperar de qualquer forma: os boots são mais lentos do que no Linux nativo (Wine, mais o passo do steamcmd, mais o login na Steam).

Ou pule tudo isso

É o tipo de trabalheira que come uma tarde. Se você só quer o servidor, nós cuidamos da frota Linux e do encanamento do Wine para você, por hora, com IP público desde o primeiro minuto. Alien Swarm: Reactive Drop ainda não está nas nossas páginas públicas de deploy, mas outros servidores só de Windows estão, como Wreckfest e rFactor 2. Veja tudo o que nós hospedamos na 2hours.gg.

Veja também

← Voltar para todos os guias