Hice funcionar un servidor de Space Engineers en macOS. El bloqueo que todos citan no es el correcto.

Lo que todo el mundo responde, y por qué no da en el blanco
Quería jugar a Space Engineers con un amigo que juega en consola. Yo tengo un Mac. Cada respuesta que encontraba decía lo mismo: consíguete un PC con Windows, o alquila un servidor.
Ninguna de las dos me convencía, así que comprobé si la afirmación era cierta. No lo es. El servidor funciona en macOS, con un mundo con mods y un jugador de consola conectado.
Aquí está el método, y sobre todo las tres trampas que más tiempo me costaron.
El servidor no es el juego
Busca "Space Engineers en Mac" y encontrarás una avalancha de resultados explicando que el juego no funciona en macOS. Esos resultados son correctos, y responden a otra pregunta.
El servidor dedicado es un binario aparte, SpaceEngineersDedicated.exe, que se distribuye junto al cliente. No tiene motor de renderizado. Nunca abre una ventana, ni carga un shader, ni toca la GPU. Simula física, mantiene el estado del mundo y habla con los clientes por la red.
Toda la historia está en esa distinción. El cliente es difícil de portar porque es una aplicación DirectX 11 haciendo trabajo gráfico real. El servidor es una aplicación .NET sin interfaz haciendo cálculo. Bajo Wine, uno es un proyecto de investigación y el otro es una tarde.
Yo mismo perdí una hora con esta confusión. Había leído "el juego no funciona" y dejé de pensar.
Conseguir los archivos sin cuenta de Steam
No hace falta Windows para obtener los binarios, y tampoco hace falta una cuenta de Steam. SteamCMD tiene una versión nativa para macOS, y el servidor dedicado se descarga de forma anónima:
steamcmd +force_install_dir "$PWD/game" +login anonymous \
+app_update 298740 validate +quit
La app 298740 es el servidor dedicado. El +login anonymous no es un truco: así es como Valve publica los paquetes de servidor dedicado. Sin cuenta, sin licencia del juego en la máquina. Tus amigos sí necesitan tener el juego. El servidor no.
La trampa que se come la tarde: .NET Framework
El servidor apunta a .NET Framework 4.8. El consejo estándar, para cualquier aplicación .NET bajo Wine, es instalar el runtime real de Microsoft con winetricks dotnet48.
No lo hagas en Wine 11.
El instalador de .NET Framework 4.8 es un ejecutable de 32 bits. Wine 11 incorporó una capa WoW64 reescrita, la pasarela que ejecuta código Windows de 32 bits dentro de un proceso Wine de 64 bits. El instalador y esa capa nueva no se llevan bien. No falla. No da error. Se queda ahí, sujetando un bloqueo, indefinidamente.
La solución es no instalarlo en absoluto, y además es el comportamiento por defecto. Wine Mono, la implementación .NET del propio Wine, viene dentro del cask wine-stable y se instala sola al crear el prefijo:
export WINEPREFIX="$PWD/prefix"
wineboot --init
wineserver -w
Eso es todo. Wine Mono es de 64 bits, tarda segundos, y al servidor le da igual con qué implementación de .NET está hablando.
Un detalle convierte la tarde perdida en noche perdida. El verbo dotnet48 de winetricks borra Wine Mono lo primero, antes de instalar nada. Así que si pruebas esa vía y se bloquea, no vuelves al punto de partida: te quedas con un prefijo sin ningún runtime .NET, y un servidor que ahora falla por un motivo completamente distinto del que perseguías. Borra el prefijo y vuelve a crearlo.
El único componente de Microsoft que realmente necesitas es el redistribuible de Visual C++ 2015-2019 x64, que es un instalador de 64 bits y se porta bien.
El crossplay con consola, y su precio
Dos ajustes en la configuración del servidor:
<NetworkType>EOS</NetworkType>
<ConsoleCompatibility>true</ConsoleCompatibility>
EOS cambia el transporte de la red de Steam a Epic Online Services, que es lo que hablan los clientes de consola. ConsoleCompatibility restringe el mundo a lo que las consolas pueden soportar.
El precio son los mods. Con la compatibilidad de consola activada, el Workshop de Steam deja de estar disponible. Toda la lista de mods tiene que venir de mod.io, cuyo catálogo es más pequeño y con identificadores distintos para un mismo mod. Si tenías una colección de Workshop cuidadosamente montada, vas a rehacerla.
Calcula tú mismo el hash de la contraseña
La configuración no guarda la contraseña, guarda un hash, y el formato no está documentado en ningún sitio oficial. Varias webs se ofrecen a generarlo por ti.
No las uses: estás escribiendo una contraseña en el formulario de un desconocido.
El formato es estándar y cabe en diez líneas en local. PBKDF2 con HMAC-SHA1, 10000 iteraciones, clave de 20 bytes, sal aleatoria de 16 bytes. Ojo, son dos campos distintos en la configuración, no un único valor concatenado:
import base64, hashlib, os
contrasena = b"changeme"
sal = os.urandom(16)
clave = hashlib.pbkdf2_hmac("sha1", contrasena, sal, 10000, 20)
print("<ServerPasswordSalt>" + base64.b64encode(sal).decode() + "</ServerPasswordSalt>")
print("<ServerPasswordHash>" + base64.b64encode(clave).decode() + "</ServerPasswordHash>")
Y una <ServerPassword> en texto plano no existe y no hace nada.
Tres lecciones sobre la vida de los procesos
Aquí es donde una instalación que funciona pasa a ser una instalación usable, y donde cometí todos los errores disponibles.
El servidor muere con la terminal. Evidente a posteriori. La solución es una sesión desacoplada con tmux, y a partir de ahí vive por su cuenta.
Que el Mac se duerma se lleva el servidor por delante. caffeinate resuelve eso, atado al PID del propio servidor para soltarse solo cuando este se detiene. Y cometí exactamente el mismo error otra vez: había escrito esa línea dentro del script de arranque, así que caffeinate era hijo del script, así que moría en el segundo en que el script terminaba. No había funcionado ni una sola vez desde que lo escribí. El arreglo es el mismo principio que con el servidor: desacoplarlo de verdad, con start_new_session=True.
Y cuando aun así se cayó, la culpa no era de la suspensión. El servidor se cayó dos veces la misma tarde. Acababa de escribir la protección contra la suspensión, así que pasé una hora demostrando que funcionaba. Funcionaba. El servidor seguía muriendo.
La causa real era un proceso de fondo sin ninguna relación, en la misma máquina, que tocaba la instalación. Lo que la ocultó fueron las marcas de tiempo: el servidor escribe en UTC, mi reloj es local, y dos horas de desfase bastaron para que la correlación nunca saltara a la vista. En cuanto llevé ambas al mismo huso horario, los eventos encajaron al segundo.
La lección va más allá de este juego. Cuando acabas de construir un arreglo para el problema X y X sigue ocurriendo, tu certeza sobre la causa es lo menos fiable de la sala. Normaliza todas tus marcas de tiempo antes de teorizar.
El apagado limpio que todo el mundo cree imposible
Busca cómo parar limpiamente un servidor dedicado de Space Engineers y encontrarás la misma respuesta en todas partes: no existe. Ni comando de "guardar y salir", ni señal que gestione, matas el proceso y pierdes todo lo construido desde el último guardado automático.
Es falso. El apagado limpio existe, guarda, y cabe en dos líneas:
tmux send-keys -t se C-c # el byte 0x03, en la consola
kill -INT "$PID" # la señal, al PID del JUEGO
Las dos juntas, y ahí está la clave: ninguna hace nada por su cuenta. Es exactamente lo que produce un Ctrl+C tecleado a mano en una terminal conectada, donde la disciplina de línea entrega el byte a la aplicación y levanta la señal. send-keys solo hace lo primero, kill -INT solo lo segundo. Por separado parecen inertes. Juntas, el juego escribe Exiting.., guarda el mundo, descarga la sesión, cierra Steam. Tres segundos, sin perder nada.
Queda entender por qué nadie lo encuentra, y la trampa es bonita. Está en el PID:
pgrep -f "SpaceEngineersDedicated.exe" | head -1 # devuelve tmux
pgrep -f busca en la línea de comandos entera. El proceso tmux que aloja el servidor lleva el comando wine en sus argumentos, así que también coincide. Y como es él quien lanza el juego, su PID es siempre el más bajo. Ese head -1 apunta a tmux, nunca al juego.
Así que envías una señal perfectamente válida al objetivo equivocado. La terminal muere, el pty se cierra bajo el juego, el juego cae sin guardar. El resultado observado es exacto; la conclusión que se saca de él, no. El arreglo es filtrar por el nombre del proceso, que nada puede suplantar, en lugar de por su línea de comandos:
for p in $(pgrep -f "SpaceEngineersDedicated\.exe"); do
case "$(ps -o comm= -p "$p")" in
*SpaceEngineersDedicated.exe) echo "$p"; break ;;
esac
done
Tres cosas que saber antes de construir sobre esto. El juego atiende la petición cuando le apetece: una décima de segundo en un servidor lleva unos minutos en pie, hasta tres minutos en uno que acaba de terminar de cargar. El proceso nunca sale solo, se queda en "press any key to close this window" y ninguna tecla enviada por script le llega, así que lo matas, pero solo cuando el guardado aparece en el registro. Y para leer ese guardado, el orden importa: el guardado de apagado y el automático escriben exactamente la misma línea final, así que buscas Exiting.. y luego el guardado, nunca el guardado solo.
Una salvaguarda no lo es si algo la esquiva por defecto
Por encima queda una salvaguarda para cuando el juego no obedece: el script de parada se niega entonces a cortar si el último guardado es demasiado antiguo, salvo que pases un --force explícito.
Me salvó una vez, y cómo me salvó resulta instructivo. Tenía un panel de ajustes que paraba el servidor, escribía los cambios y volvía a arrancar. Para que fuera cómodo, hacía que llamara al script de parada con --force. Funcionaba perfectamente, descartaba en silencio un trozo de mundo sin guardar, y yo había construido justo el atajo que dejaba inútil mi propio control.
El arreglo no fue quitar la opción, sino obligar al humano a pedirla: forzar pasó a ser una casilla explícita en el panel, desmarcada por defecto, justo al lado de la antigüedad del último guardado. Conservas la salida de emergencia, simplemente ya no puedes tomarla por accidente.
Lo que realmente te aporta
Un servidor dedicado no existe para que dos personas puedan jugar juntas. Los jugadores de consola llevan años alojándose entre ellos.
Lo que aporta es lo que una sesión alojada en consola estructuralmente no puede hacer. Microsoft y Sony prohíben ejecutar scripts personalizados en consola, así que el bloque programable, el sistema de scripting de Space Engineers, no está disponible ni en un jugador ni en multijugador alojado en consola. En un servidor dedicado funciona, y los jugadores de consola lo obtienen al unirse. La restricción es sobre quién aloja, no sobre quién juega.
Además obtienes un mundo que sigue simulando cuando no hay nadie conectado, una lista de mods que controlas tú, y una treintena de ajustes modificables sin pasar por la tienda de nadie.
El código
He dejado todo esto limpio en un repositorio público: script de instalación, arranque y parada con la salvaguarda de guardado, un panel de ajustes local para los ajustes del mundo que si no obligan a editar XML a mano, y un README donde cada callejón sin salida está documentado en vez de omitido en silencio.
github.com/Stark-52/se-server-macos
Con licencia MIT. Si estabas buscando esto y solo encontrabas "consíguete un PC con Windows", espero que te ahorre la tarde que a mí me costó.
