Serveurs et administration

Héberger une partie avec ses réglages, ou un serveur dédié Linux ouvert 24 h/24 qui apparaît dans la liste des serveurs Steam, et l'administrer : groupes, permissions, exclusions et bans.

Deux façons d'héberger

Partie hébergéeServeur dédié
LancementJouer → Multijoueur → HébergerProgramme sans fenêtre sur un serveur Linux
Joueur localL'hôte joue (et est superadmin)Aucun : le serveur ne fait que servir
Trouver la partieOnglet Parties (lobbies Steam), invitations, IPOnglet Serveurs (liste Steam, favoris, réseau local), IP
RéglagesRéglages du serveur… dans le formulaire d'hébergementserver.cfg ou variables d'environnement

Les deux utilisent les mêmes réglages, les mêmes groupes et les mêmes bans.

Réglages du serveur

Dans le jeu, ils sont enregistrés dans %APPDATA%\Godot\app_userdata\PhysiCore\server.cfg (bouton Dossier) ; un serveur dédié lit server.cfg à côté de son programme (créé au premier lancement, ou --config <fichier>).

SectionCléRôle
[server] IdentiténameNom dans la liste des serveurs
passwordMot de passe (vide = public). Les joueurs le saisissent avant de rejoindre.
motdMessage d'accueil envoyé à chaque joueur
max_playersJoueurs max (1 à 64)
portPort UDP du jeu (27015)
query_portPort UDP de requête Steam (27016), pour la liste des serveurs
regionRégion (eu, na…)
tagsTags, séparés par des virgules
[game] PartiegamemodeGamemode (dédié) : sandbox, ou ws_<id> pour un gamemode du Workshop
mapCarte (dédié) : builtin:sb_naturebox, builtin:sb_desert, builtin:sb_create ou addon:ws_<id>/maps/<fichier>.pcmap
map_rotationCartes à enchaîner
rotation_minutesMinutes entre deux cartes (0 = jamais)
prop_limitProps max par joueur (200 au plus)
npc_limitPNJ max sur la carte, tous joueurs et scripts confondus (50 par défaut, 500 au plus)
pvpDégâts entre joueurs : désactivé, les armes du jeu ne blessent plus les autres joueurs. Les gamemodes le lisent avec game.GetServerSetting("pvp")
noclipfalse interdit le noclip à tous, même si le gamemode l'autorise (sauf permission noclip)
rp_movementDéplacements réalistes pour le roleplay (false par défaut) : démarrages et arrêts progressifs, plus lent à reculer, de côté et en montée, pas de bunny hop (pas de vitesse gagnée en sautant, peu de contrôle en l'air, un temps entre deux sauts, pas de saut accroupi plus haut), ralenti après une chute. ConVar sv_rp_movement
player_pushEn marchant, les joueurs poussent les props non figés (avec la force d'une personne : les lourds bougent à peine) et bousculent les autres joueurs au lieu d'être bloqués (true par défaut). ConVar sv_player_push
force_fogBrouillard imposé (false par défaut) : les joueurs ne peuvent plus réduire ni couper le brouillard dans Configuration → Graphismes, tout le monde voit le même (utile en PvP, ou pour l'ambiance d'un serveur RP). ConVar sv_force_fog, modifiable en direct
name_tag_distanceDistance en mètres jusqu'à laquelle on voit le pseudo au-dessus d'un joueur (10 par défaut, 0 = jamais) ; il s'efface en fondu sur les 2 derniers mètres, et aussi quand un mur, le décor ou un prop cache le joueur (le véhicule où il est assis ne compte pas). Les PNJ n'ont pas de nom au-dessus de la tête. ConVar sv_nametag_distance, modifiable en direct
[content] Contenuworkshop_collectionIdentifiant d'une collection Workshop : tous ses objets sont chargés
workshop_itemsIdentifiants d'objets Workshop (gamemodes, addons, maps)
[security] Sécuritéchat_messagesMessages de chat max par joueur toutes les 10 s
props_per_secondApparitions (prop ou dupe) max par seconde et par joueur
afk_minutesExclusion après N minutes sans bouger (0 = jamais)
require_steamRefuse les joueurs sans compte Steam vérifié
anticheatAnti-triche des déplacements (true par défaut, voir Sécurité). ConVar sv_anticheat, modifiable en direct
anticheat_kickExclut un joueur après N infractions rapprochées de l'anti-triche (6 par défaut, 0 = jamais : il est seulement remis à sa place)
allow_httpAutorise les requêtes web du Lua (http.Fetch, http.Post, HTTP, sound.PlayURL), côté serveur et chez les joueurs (false par défaut). ConVar sv_allow_http, modifiable en direct
http_whitelistDomaines web autorisés, séparés par des virgules (sous-domaines compris ; vide = tous). Le réseau local est toujours refusé. ConVar sv_http_whitelist
allow_csluaTout joueur peut lancer du Lua sur son propre jeu avec /lua_run_cl (false par défaut : seulement avec la permission lua_run). ConVar sv_allowcslua
[fastdl] FastDLenabledEnvoie le dossier fastdl/ aux joueurs (serveur dédié, voir FastDL)
httpTéléchargement HTTP ; false = seulement par la connexion du jeu
http_portPort HTTP en TCP (0 = le même numéro que le port du jeu)
max_rateDébit max par joueur en Ko/s (0 = illimité)
max_downloadsTéléchargements HTTP en même temps, tous joueurs confondus (4 par défaut)
[admins]superadminsSteamID64 des superadmins

Le contenu Workshop listé est téléchargé avec ses dépendances et activé pour la partie ; les joueurs le reçoivent automatiquement en rejoignant.

VersionLe jeu et le serveur doivent avoir la même version : un joueur avec une autre version est refusé avec un message clair.

Serveur dédié Linux

  1. Dans Godot : Éditeur → Gérer les modèles d'exportation → Télécharger et installer (une seule fois).
  2. Dans PowerShell, à la racine du projet : .\tools\export_linux_server.ps1. Il crée build\linux-server\ et l'archive build\physicore-server-linux.tar.gz (programme, start.sh, server.cfg.example). Le dossier addons/godotsteam_server a un .gdignore : sur Windows, l'éditeur et le jeu ne chargent que GodotSteam (les deux ensemble déclarent deux fois SteamProjectSettings). Le script le retire le temps de l'export, pour que le serveur Linux ait SteamServer ; ne le supprime pas.
  3. Sur le serveur (Debian/Ubuntu) : extrais l'archive, puis chmod +x PhysiCoreServer.x86_64 start.sh et ./start.sh.
  4. Ouvre les ports UDP 27015 (jeu) et 27016 (requête Steam), et le port 27015 en TCP si tu utilises le FastDL en HTTP.

Pour la liste Steam, le serveur a besoin de la bibliothèque steamclient.so (fournie par SteamCMD) dans ~/.steam/sdk64/ :

mkdir -p ~/steamcmd ~/.steam/sdk64 && cd ~/steamcmd
curl -sSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz | tar -xz
./steamcmd.sh +login anonymous +app_update 1007 +quit
cp linux64/steamclient.so ~/.steam/sdk64/

Sans elle, le serveur fonctionne quand même, mais seulement par IP (et sans contenu Workshop automatique).

Pterodactyl

L'egg installe le serveur depuis Steam : l'application PhysiCore Dedicated Server (App ID 5448960), téléchargée par SteamCMD en connexion anonyme (aucun compte Steam à saisir).

  1. Dans le panel : Admin → Nests → Import Egg, choisis server/pterodactyl/egg-physicore.json (s'il existe déjà, importe-le par-dessus ou mets à jour l'egg, puis réinstalle les serveurs qui utilisaient l'ancien).
  2. Crée un serveur avec cet egg (image ghcr.io/parkervcp/steamcmd:debian), une allocation principale (port du jeu, UDP et TCP pour le FastDL) et une deuxième pour la requête Steam (Port de requête Steam).
  3. L'installation télécharge SteamCMD puis le serveur Linux, met steamclient.so dans .steam/sdk64/ (indispensable pour la liste des serveurs Steam et le Workshop) et rend PhysiCoreServer.x86_64 exécutable.
  4. Mise à jour automatique (AUTO_UPDATE, 1 par défaut) : SteamCMD met le serveur à jour à chaque démarrage. Branche Steam (SRCDS_BETAID) et son mot de passe (SRCDS_BETAPASS) installent une branche bêta.
  5. Les autres variables (nom, mot de passe, gamemode, carte, collection et objets Workshop, superadmins, FastDL…) remplacent les valeurs de server.cfg. Les objets et la collection Workshop sont téléchargés par le serveur à chaque démarrage.
  6. Pour le FastDL, envoie tes mods dans le dossier fastdl/ avec le gestionnaire de fichiers du panel (ou en SFTP), puis redémarre.

La console du panel envoie les commandes au serveur ; le bouton Arrêter envoie quit. Les fichiers à garder entre deux mises à jour (server.cfg, convars.cfg, prop_protection.json, lua_data/, fastdl/) ne sont pas dans le dépôt Steam : SteamCMD ne les touche pas.

Côté Steamworks (développeur)L'app 5448960 doit contenir le build Linux (dossier de tools\export_linux_server.ps1 : PhysiCoreServer.x86_64, PhysiCoreServer.pck, les lib*.so, start.sh…) dans un dépôt Linux publié sur la branche par défaut, et être téléchargeable en anonyme (outil serveur dédié). Si l'installation dit « PhysiCoreServer.x86_64 introuvable après SteamCMD », c'est l'un de ces deux points.
Variables d'environnementPartout (Pterodactyl, Docker, systemd), PHYSICORE_<SECTION>_<CLÉ> remplace un réglage : PHYSICORE_SERVER_NAME, PHYSICORE_GAME_MAP, PHYSICORE_CONTENT_WORKSHOP_ITEMS="123,456"…

FastDL : envoyer ses mods sans le Workshop

Un serveur dédié peut envoyer lui-même ses mods aux joueurs qui le rejoignent : addons (modèles, maps, véhicules, armes, playermodels, sons, Lua…) et gamemodes, sans rien publier sur le Workshop. Le Workshop ([content]) marche toujours en même temps.

  1. Mets chaque mod dans un sous-dossier de fastdl/, à côté du programme du serveur (créé au premier lancement). Un dossier avec un gamemode.json est un gamemode, les autres sont des addons (même structure qu'un addon local). Noms de dossier : lettres, chiffres, _, - et ..
  2. Redémarre le serveur : il prépare chaque mod (seulement ceux qui ont changé) et affiche [FastDL] N mod(s) prêts.
  3. Les joueurs les téléchargent en rejoignant (barre de progression sur l'écran de chargement), puis seulement quand ils changent.
PhysiCoreServer.x86_64
server.cfg
fastdl/
├── ma_ville/          addon → identifiants addon:fd_ma_ville/…
│   ├── addon.json
│   ├── maps/ville.pcmap
│   ├── models/…
│   └── lua/autorun/…
└── monrp/             gamemode (gamemode.json) → gamemode="monrp"
    ├── gamemode.json
    ├── init.lua, cl_init.lua, shared.lua
    └── sound/…

Dans server.cfg, un addon FastDL se nomme fd_<dossier> (map="addon:fd_ma_ville/maps/ville.pcmap"), un gamemode par son nom de dossier (gamemode="monrp" ; il remplace un gamemode du même nom). Les fichiers préparés sont dans fastdl_build/ (régénérés tout seuls) et la clé du serveur dans fastdl.key : garde-la privée et ne la change pas, sinon les joueurs retéléchargent tout.

Téléchargement

Protection des mods

LimiteLe jeu du joueur doit lire les mods pour les afficher : un tricheur déterminé, avec des outils de piratage, peut toujours les extraire pendant qu'il est connecté. La protection empêche de simplement les retrouver et les copier.

Sécurité et anti-triche

Le serveur ne croit pas ce que les joueurs lui envoient : il vérifie lui-même. Ce qui est fait par le jeu, sans rien régler :

L'hôteEn partie hébergée (sans serveur dédié), l'hôte fait tourner le serveur sur son ordinateur : ses propres déplacements ne sont pas contrôlés. Pour un serveur public, préfère un serveur dédié.

Protection des props

En multijoueur, chaque prop, véhicule et PNJ appartient au joueur qui l'a fait apparaître. La protection (inspirée de FPP sur Garry's Mod) décide qui peut y toucher. Elle marche dans tous les gamemodes et le serveur vérifie chaque action : un joueur ne peut pas la contourner en trafiquant son jeu. En solo, elle ne fait rien (tout est à toi).

Tout se règle dans le menu de spawn (Q), onglet Protection. Clique dans l'onglet et le menu reste ouvert ; Q ou Échap le referme.

RègleChoixPar défaut
Physgun, toolgun, utiliser (E), endommager, monter dedans, pour les props, les véhicules et les PNJ des joueurspropriétaire seul, propriétaire et amis, tout le mondepropriétaire et amis ; utiliser un prop et monter dans un véhicule : tout le monde
Props de la carte, par actionpersonne, admins, tout le mondephysgun et toolgun : admins ; le reste : tout le monde
Sans propriétaire (props créés par les scripts, props d'un joueur parti), par actionpersonne, admins, tout le mondecomme la carte
Admins passent outre, par actionpersonne, superadmins, adminsadmins
Les joueurs peuvent ouvrir leurs props à tout le mondeoui / nonoui
Supprimer les props d'un joueur parti après N secondes (0 = tout de suite, ou jamais)0 à 3600 s, y compris ceux des admins ou non120 s, admins compris
Dire au joueur pourquoi c'est refuséoui / nonoui

Ce que couvre chaque action : physgun pour attraper, et R pour libérer un prop figé ; toolgun pour tous les outils du jeu et les outils Lua, le remover sur un PNJ et la sélection du duplicateur (une contrainte vérifie ses deux props) ; utiliser pour E sur un prop (bouton, entité scriptée, arme au sol) ; endommager pour les tirs, les coups et les explosions d'un joueur sur un prop, un véhicule ou un PNJ ; monter dedans pour E sur un véhicule. Si un joueur revient avant la fin du délai de nettoyage (même SteamID), il récupère ses props. Quand une action est refusée, sa raison s'affiche un moment à droite du viseur.

Pour les scriptsLe hook ProtectionCanTouch tranche avant les règles, GM.PropProtection = false désactive la protection pour un gamemode, et la bibliothèque protection avec les fonctions CPPI (ent:CPPIGetOwner(), ent:CPPICanTool(ply)…) rend compatibles les addons écrits pour les protections de Garry's Mod. Les hooks du gamemode (PhysgunPickup, CanTool, PlayerUse…) passent après la protection : ils peuvent seulement refuser en plus.

Console du serveur

Dans le terminal du serveur dédié (ou la console Pterodactyl), avec tous les droits :

CommandeRôle
helpListe des commandes
statusNom, gamemode, carte, joueurs, connexion Steam
playersJoueurs avec leur #id, SteamID, IP, groupe et ping
say <texte>Message à tous les joueurs
quitArrête le serveur
commandes adminToutes celles ci-dessous, sans le / (ban, map, setgroup…)

Groupes et permissions

Chaque joueur appartient à un groupe (par son SteamID). Un groupe hérite des permissions d'un autre, et a une immunité : on ne peut viser qu'un joueur d'immunité inférieure.

GroupeHérite deImmunitéPermissions ajoutées
user—0—
moderatoruser10kick, mute, freeze, goto, bring, slay, cleanup, afk_immune
adminmoderator50ban, unban, map, gamemode, noclip, convars
superadminadmin100toutes (*)

Autres permissions : setgroup, groups (créer et modifier les groupes), config (recharger la configuration), lua (recharger les scripts), protection (régler la protection des props ; superadmins seulement par défaut, /grant admin protection pour les admins), lua_run (lancer du Lua avec /lua_run, /lua_openscript et /lua_run_cl ; superadmins seulement par défaut). Un admin_groups.json créé avant l'arrivée de convars garde ses anciennes permissions : ajoute-la avec /grant admin convars. Les groupes sont modifiables en jeu (/addgroup, /grant…) et sont enregistrés dans admin_groups.json ; les joueurs dans admin_users.json, les bans dans bans.json (dans le dossier de server.cfg).

En partie hébergée, l'hôte est superadmin. Sur un serveur dédié, les SteamID de [admins] superadmins le sont ; les autres groupes se donnent avec /setgroup. Les SteamID sont vérifiés par Steam (ticket d'authentification) : impossible de se faire passer pour un admin. Un joueur n'entre qu'une fois que Steam a confirmé son identité (15 s au plus, sinon il est refusé), et un même compte ne peut pas être connecté deux fois.

Serveur sans SteamSi le serveur ne peut pas vérifier les tickets (serveur dédié sans steamclient.so, partie hébergée sans Steam), le SteamID annoncé par un joueur n'est pas cru : le joueur joue avec le SteamID 0. Les groupes donnés par SteamID (dont superadmins) et les bans par SteamID ne s'appliquent alors pas ; les bans par IP, si. Le serveur l'indique au premier joueur : [ServerAuth] Steam indisponible….

Commandes admin

Tapées dans le chat (Y) avec un /. Un joueur se désigne par une partie de son nom, #id (voir players), son SteamID, ou ^ pour soi-même. /admin liste les commandes permises.

CommandePermissionRôle
/kick <joueur> [raison]kickExclut un joueur (la raison lui est affichée)
/ban <joueur> <durée> [raison]banBannit par SteamID et IP. Durée : 30m, 2h, 7d, 1w, 0 = définitif
/banid <steamid> <durée> [raison]banBannit un SteamID, même hors ligne
/banip <ip> <durée> [raison]banBannit une IP (connexions directes sans Steam)
/unban <steamid ou ip>, /bansunban / banLève un ban ; liste les bans actifs
/mute, /unmute <joueur>muteRetire / rend la parole dans le chat
/freeze, /unfreeze <joueur>freezeFige / libère un joueur
/goto, /bring <joueur>goto / bringVa vers un joueur / l'amène
/slay <joueur>slayTue un joueur
/cleanupplayer <joueur>cleanupSupprime ses props
/map <carte>, /gamemode <nom>map / gamemodeChange de carte ou de gamemode pour tout le monde
/setgroup <joueur ou steamid> <groupe>setgroupChange le groupe d'un joueur
/addgroup <nom> <hérite de> <immunité>, /delgroupgroupsCrée / supprime un groupe
/grant, /revoke <groupe> <permission>groupsDonne / retire une permission
/groups—Liste les groupes
/reloadconfig, /reloadluaconfig / luaRelit server.cfg et les fichiers admin ; recharge les scripts Lua
/cvar <nom> [valeur]convarsLit ou change un réglage (ConVar), ex. /cvar sbox_maxprops 300
/cvars [filtre]—Liste les ConVars et leur valeur
/lua_run <code>lua_runLance une ligne de Lua sur le serveur (notée dans la console)
/lua_openscript <fichier>lua_runLance le fichier lua/<fichier> d'un addon activé (ex. /lua_openscript monaddon/test.lua)
/lua_run_cl <code>lua_run, ou personne si sv_allowcslua 1Lance une ligne de Lua sur son propre jeu (realm client)

Réglages en jeu (ConVars)

Les gamemodes et addons déclarent leurs propres réglages (durée de manche, argent de départ…) : ce sont les ConVars. L'hôte et les joueurs qui ont la permission convars les changent en jeu par Pause → Réglages du serveur, ou avec /cvar. Des ConVars intégrées reflètent server.cfg : sbox_maxprops, sbox_maxnpcs, sbox_noclip, sbox_playershurtplayers, sv_props_per_second, sv_rp_movement, sv_player_push, sv_anticheat, sv_force_fog, sv_nametag_distance, sv_allow_http, sv_http_whitelist et sv_allowcslua. En Lua, game.ConsoleCommand("sbox_maxprops 300\n") les change comme la console du serveur.

Sur un serveur dédié, les valeurs archivées sont dans convars.cfg à côté de server.cfg, et PHYSICORE_CVAR_<NOM> impose une valeur de départ (variable à ajouter dans l'egg Pterodactyl si besoin). Les données des scripts (file.Write) sont dans lua_data/, au même endroit : pense à le sauvegarder.

En Lua

Les scripts peuvent s'appuyer sur ce système : ply:IsAdmin(), ply:IsSuperAdmin(), ply:GetUserGroup() (aussi côté client), et côté serveur ply:HasPermission(nom), ply:Kick(raison), ply:Ban(minutes, raison), ply:IPAddress(), game.GetServerSetting(clé) et game.IsDedicated().

concommand.Add("annonce", function(ply, args, texte)
	if not ply:IsAdmin() then
		ply:ChatPrint("Réservé aux admins.")
		return
	end
	PrintMessage("[Annonce] " .. texte)
end)