Retour au blog
Infra

J'ai fait tourner un serveur Space Engineers sur macOS. Le blocage que tout le monde cite n'est pas le bon.

22 août 2026 9 min de lecture

Ce que tout le monde répond, et pourquoi c'est à côté

Je voulais jouer à Space Engineers avec un ami qui est sur console. J'ai un Mac. Chaque réponse trouvée en ligne disait la même chose : prends un PC Windows, ou loue un serveur.

Les deux options me convenaient mal, alors j'ai vérifié si l'affirmation tenait. Elle ne tient pas. Le serveur tourne sur macOS, avec un monde moddé et un joueur console connecté dessus.

Voici la méthode, et surtout les trois pièges qui m'ont coûté le plus de temps.

Le serveur n'est pas le jeu

Cherchez « Space Engineers sur Mac » et vous trouverez une avalanche de résultats expliquant que le jeu ne tourne pas sur macOS. Ces résultats sont exacts, et ils répondent à une autre question.

Le serveur dédié est un binaire séparé, SpaceEngineersDedicated.exe, livré à côté du client. Il n'a aucun moteur de rendu. Il n'ouvre pas de fenêtre, ne charge pas de shader, ne touche jamais au GPU. Il simule de la physique, tient l'état du monde, et parle aux clients par le réseau.

Toute l'histoire est dans cette distinction. Le client est difficile à porter parce que c'est une application DirectX 11 qui fait du vrai travail graphique. Le serveur est une application .NET sans interface qui fait du calcul. Sous Wine, l'un est un projet de recherche, l'autre est une soirée.

J'ai moi-même perdu une heure sur cette confusion. J'avais lu « le jeu ne tourne pas » et j'avais arrêté de réfléchir.

Récupérer les fichiers sans compte Steam

Pas besoin de Windows pour obtenir les binaires, et pas besoin de compte Steam non plus. SteamCMD a une version macOS native, et le serveur dédié se télécharge en anonyme :

steamcmd +force_install_dir "$PWD/game" +login anonymous \
         +app_update 298740 validate +quit

L'app 298740 est le serveur dédié. Le +login anonymous n'est pas une combine : c'est ainsi que Valve publie les paquets de serveur dédié. Aucun compte, aucune licence du jeu sur la machine. Vos amis doivent posséder le jeu. Le serveur, non.

Le piège qui mange la soirée : .NET Framework

Le serveur vise .NET Framework 4.8. Le conseil standard, pour n'importe quelle application .NET sous Wine, est d'installer le vrai runtime Microsoft avec winetricks dotnet48.

Ne le faites pas sur Wine 11.

L'installeur de .NET Framework 4.8 est un exécutable 32 bits. Wine 11 a livré une couche WoW64 réécrite, la passerelle qui fait tourner du code Windows 32 bits dans un processus Wine 64 bits. L'installeur et cette nouvelle couche ne s'entendent pas. Ça ne plante pas. Ça n'affiche pas d'erreur. Ça reste là, à tenir un verrou, indéfiniment.

La solution est de ne pas l'installer du tout, et elle est aussi le comportement par défaut. Wine Mono, l'implémentation .NET de Wine, est fournie dans le cask wine-stable et s'installe seule à la création du préfixe :

export WINEPREFIX="$PWD/prefix"
wineboot --init
wineserver -w

C'est tout. Wine Mono est 64 bits, ça prend quelques secondes, et le serveur se moque de savoir à quelle implémentation .NET il parle.

Un détail transforme la soirée perdue en nuit perdue. Le verbe dotnet48 de winetricks supprime Wine Mono en tout premier, avant de commencer à installer quoi que ce soit. Si vous tentez cette route et qu'elle se bloque, vous ne revenez pas à votre point de départ : vous vous retrouvez avec un préfixe sans aucun runtime .NET, et un serveur qui échoue désormais pour une raison complètement différente de celle que vous poursuiviez. Supprimez le préfixe et recréez-le.

Le seul composant Microsoft réellement nécessaire est le redistribuable Visual C++ 2015-2019 x64, qui est un installeur 64 bits et se tient bien.

Le crossplay console, et son prix

Deux réglages dans la configuration du serveur :

<NetworkType>EOS</NetworkType>
<ConsoleCompatibility>true</ConsoleCompatibility>

EOS bascule le transport de Steam vers Epic Online Services, ce que parlent les clients console. ConsoleCompatibility restreint le monde aux fonctions que les consoles encaissent.

Le prix, ce sont les mods. Avec la compatibilité console, le Workshop Steam devient indisponible. Toute la liste de mods doit venir de mod.io, dont le catalogue est plus petit et les identifiants différents pour un même mod. Si vous aviez une collection Workshop patiemment constituée, vous allez la refaire.

Le mot de passe, à calculer chez soi

La configuration ne stocke pas le mot de passe, elle stocke une empreinte, et le format n'est documenté nulle part officiellement. Plusieurs sites proposent de la générer pour vous.

Ne les utilisez pas : vous tapez un mot de passe dans le formulaire d'un inconnu.

Le format est standard et tient en dix lignes en local. PBKDF2 avec HMAC-SHA1, 10000 itérations, clé de 20 octets, sel aléatoire de 16 octets. Attention, ce sont deux champs distincts dans la configuration, pas une seule valeur concaténée :

import base64, hashlib, os

mot_de_passe = b"changeme"
sel = os.urandom(16)
cle = hashlib.pbkdf2_hmac("sha1", mot_de_passe, sel, 10000, 20)

print("<ServerPasswordSalt>" + base64.b64encode(sel).decode() + "</ServerPasswordSalt>")
print("<ServerPasswordHash>" + base64.b64encode(cle).decode() + "</ServerPasswordHash>")

Et <ServerPassword> en clair n'existe pas et ne sert à rien.

Trois leçons sur la durée de vie des processus

C'est là qu'une installation qui marche devient une installation utilisable, et là que j'ai fait toutes les erreurs disponibles.

Le serveur meurt avec le terminal. Évident après coup. La solution est une session détachée via tmux, et le serveur vit alors indépendamment.

La mise en veille du Mac emporte le serveur. caffeinate règle ça, arrimé au PID du serveur pour relâcher tout seul quand celui-ci s'arrête. Et j'ai refait exactement la même erreur : j'avais écrit cette ligne à l'intérieur du script de démarrage, donc caffeinate était un enfant du script, donc il mourait à la seconde où le script se terminait. Ça n'avait jamais fonctionné une seule fois depuis que je l'avais écrit. Le correctif est le même principe que pour le serveur : détacher pour de bon, avec start_new_session=True.

Et quand ça a planté quand même, la veille n'était pas en cause. Le serveur est tombé deux fois dans la même soirée. Je venais d'écrire la protection contre la veille, alors j'ai passé une heure à prouver qu'elle marchait. Elle marchait. Le serveur continuait de mourir.

La vraie cause était un processus d'arrière-plan sans rapport, sur la même machine, qui touchait à l'installation. Ce qui l'a masquée : les horodatages. Le serveur écrit en UTC, mon horloge est locale, et deux heures de décalage suffisaient pour que la corrélation ne saute jamais aux yeux. Dès que j'ai ramené les deux au même fuseau, les événements se sont alignés à la seconde.

La leçon dépasse ce jeu. Quand vous venez de construire un correctif pour le problème X et que X continue, votre certitude sur la cause est la chose la moins fiable de la pièce. Ramenez tous vos horodatages au même fuseau avant de théoriser.

L'arrêt propre que tout le monde croit impossible

Cherchez comment arrêter proprement un serveur Space Engineers dédié et vous trouverez partout la même réponse : ça n'existe pas. Pas de commande « sauvegarde et quitte », aucun signal traité, on tue le processus et on perd tout ce qui a été construit depuis la dernière sauvegarde automatique.

C'est faux. L'arrêt propre existe, il sauvegarde, et il tient en deux lignes :

tmux send-keys -t se C-c    # l'octet 0x03, dans la console
kill -INT "$PID"            # le signal, au PID du JEU

Les deux ensemble, et c'est là que tout se joue : aucun des deux ne fait quoi que ce soit tout seul. C'est exactement ce que produit un Ctrl+C tapé à la main dans un terminal attaché, où la discipline de ligne passe l'octet à l'application et lève le signal. send-keys ne fait que le premier, kill -INT que le second. Séparément, ils n'ont l'air de rien. Ensemble, le jeu écrit Exiting.., sauvegarde le monde, décharge la session, ferme Steam. Trois secondes, rien de perdu.

Reste à comprendre pourquoi personne ne le trouve, et le piège est joli. Il est dans le PID :

pgrep -f "SpaceEngineersDedicated.exe" | head -1   # renvoie tmux

pgrep -f cherche dans la ligne de commande entière. Le processus tmux qui héberge le serveur porte la commande wine dans ses arguments, donc il correspond lui aussi. Et comme c'est lui qui lance le jeu, son PID est toujours le plus petit. Ce head -1 désigne tmux, jamais le jeu.

On envoie alors un signal parfaitement valide à la mauvaise cible. Le terminal meurt, le pty se referme sous le jeu, le jeu tombe sans sauvegarder. Le résultat observé est exact, la conclusion qu'on en tire ne l'est pas. Le correctif est de filtrer sur le nom du processus, que personne ne peut usurper, plutôt que sur sa ligne de commande :

for p in $(pgrep -f "SpaceEngineersDedicated\.exe"); do
  case "$(ps -o comm= -p "$p")" in
    *SpaceEngineersDedicated.exe) echo "$p"; break ;;
  esac
done

Trois choses à savoir avant de bâtir dessus. Le jeu honore la demande quand il veut : un dixième de seconde sur un serveur en place depuis quelques minutes, jusqu'à trois minutes sur un serveur qui vient de finir de charger. Le processus ne sort jamais de lui-même, il attend sur « press any key to close this window » et aucune touche envoyée par script ne l'atteint, donc on le tue, mais seulement une fois la sauvegarde lue dans le journal. Et pour lire cette sauvegarde, l'ordre compte : la sauvegarde d'arrêt et la sauvegarde automatique écrivent exactement la même ligne de fin, alors on cherche Exiting.. puis la sauvegarde, jamais la sauvegarde seule.

Un garde-fou n'en est pas un s'il a un contournement par défaut

Par-dessus, il reste un garde-fou pour le cas où le jeu n'obéit pas : le script d'arrêt refuse alors de couper si la dernière sauvegarde est trop ancienne, sauf --force explicite.

Il m'a sauvé une fois, et la manière dont il m'a sauvé est instructive. J'avais un panneau de réglages qui arrêtait le serveur, écrivait les changements, puis relançait. Pour que ce soit pratique, je lui faisais appeler le script d'arrêt avec --force. Ça marchait parfaitement, ça jetait silencieusement une partie du monde non sauvegardé, et j'avais construit exactement le contournement qui rendait mon propre contrôle inutile.

Le correctif n'était pas de retirer l'option, mais d'obliger l'humain à la demander : le forçage est devenu une case à cocher explicite dans le panneau, décochée par défaut, posée juste à côté de l'âge de la dernière sauvegarde. On garde l'issue de secours, on ne peut simplement plus la prendre par accident.

Ce que ça apporte vraiment

Un serveur dédié n'existe pas pour permettre à deux personnes de jouer ensemble. Les joueurs console s'hébergent mutuellement depuis des années.

Ce qu'il apporte, c'est ce qu'une session hébergée sur console ne peut structurellement pas faire. Microsoft et Sony interdisent l'exécution de scripts personnalisés sur console, donc le bloc programmable, le système de scripting de Space Engineers, est indisponible en solo et en multijoueur hébergé sur console. Sur un serveur dédié, il fonctionne, et les joueurs console y ont accès en rejoignant. La restriction porte sur qui héberge, pas sur qui joue.

Et vous récupérez un monde qui continue de tourner quand personne n'est connecté, une liste de mods que vous contrôlez, et une trentaine de réglages modifiables sans passer par la boutique de qui que ce soit.

Le code

J'ai mis tout ça au propre dans un dépôt public : script d'installation, démarrage et arrêt avec le garde-fou de sauvegarde, un panneau de réglages local pour les réglages du monde qui demandent sinon d'éditer du XML à la main, et un README où chaque impasse est documentée plutôt que passée sous silence.

github.com/Stark-52/se-server-macos

Sous licence MIT. Si vous cherchiez ça et que vous ne trouviez que « prends un PC Windows », j'espère que ça vous épargnera la soirée que ça m'a coûtée.

Un projet du même genre ?

Je conçois et déploie des produits comme celui-ci. Parlons-en.

Discutons