IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

NodeJS Discussion :

Main loop coté serveur pour un jeu avec NodeJS?


Sujet :

NodeJS

  1. #1
    Membre averti
    Homme Profil pro
    Game Créator
    Inscrit en
    Août 2006
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aube (Champagne Ardenne)

    Informations professionnelles :
    Activité : Game Créator

    Informations forums :
    Inscription : Août 2006
    Messages : 39
    Par défaut Main loop coté serveur pour un jeu avec NodeJS?
    Bonjour,

    j'aimerai bien discuter de comment gérer la boucle qui génère un jeu coté serveur avec NodeJS par exemple. Ça pourrait être bien de voir l'ensemble des solutions existante et leur avantages/désavantages.

    Pour gérer la simulation d'un monde coté serveur, on raisonne en "tick" ou "tour". La première chose qui vient donc à l'esprit c'est combien de "ticks" par seconde choisit-on?
    Comment faire ce choix? Quel impacte sur le jeu?

    Ensuite, comment l'appliquer en pratique?

    J'ai vu certaines implémentations qui utilisent un mais ça semble très douteux puisqu'on ne contrôle plus le nombre de ticks en faisant cela. Ça dépend de la capacité de la machine et du nombre de choses à calculé par tick. Ça me parait être une mauvaise solution, peu stable.

    Ensuite il y a la méthode du
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    setInterval(function(){
    tick();
    }, INTERVAL);
    que l'on peut justement minuté en milliseconde, mais le problème c'est qu'on ne peut pas descendre plus bas que la milliseconde...
    Mais finalement a-t-on besoin de pouvoir faire plus que 1000 ticks par seconde? Cela semble déjà être beaucoup. Du coup cette solution parait attrayante.
    J'ai d'ailleurs trouvé un module (dont je ne vois pas l'utilité réel en pratique) mais qui semble confirmer cette approche : https://www.npmjs.com/package/game-loop-dispatch

    Mais un autre problème qui me vient en tête : quelques soit la méthode choisit : que se passe t-il si le temps effectif pour passer un tick est supérieur au temps théorique?


    Voilà, pas mal de problématiques, si vous avez des idées, des retours d'expériences, ou des articles/tuto en français, je suis preneur.


    Merci.

  2. #2
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Par défaut
    Pourquoi un loop côté serveur ? C'est un jeu temps réel ? Généralement on parle de main loop dans un jeu pour maintenir des FPS constants, actualiser le rendu, enregistrer les saisies utilisateur... que des choses qui se passent côté client. Sinon si c'est pour un jeu temps réel avec par exemple une série d'actions qui doivent se produire toutes les minutes, là un setInterval est plus adapté oui. Mais attention, tu ne peux pas prédire le temps que prendront les échanges entre client et serveur.

  3. #3
    Membre chevronné

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Par défaut
    Comme tu travailles avec le framework NodeJs , n’oublie pas qu’il possède déjà une MainLoop pour gérer les évènements et les actions asynchrones.

    Donc toutes boucles infinis est à proscrire, dans NodeJs ,car tu vas bloquer l’exécution de ton script et donc ne jamais rendre la main , ne laissant, ainsi, aucune chance aux callbacks de tes actions asynchrones de s’exécuter.

    De manières générales et pour obtenir de bonnes performances, il faut toujours penser à rendre la main, donc d’écrire des scripts dont l’exécution se doit d’être court.
    Pour chaque itérations, tu dois donc estimer la volumétrie et le temps de calcule d’une itération.
    • Si la volumétrie est trop importante il te faut sous découper ton itérations, et celle-ci devient forcement asynchrone
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      function subIterate(array,limit,iterator,endCallback)
      {
      	var idx = -1;
      	function next()
      	{
      		idx+=1;
      		if(idx==array.length)
      			endCallback();
      		else
      		{
      			if(idx%limit==0)
      				setImmediate(iterator,array[idx],next)
      			else
      				iterator(array[idx],next);
      		}
      	}
      	next();
      }
       
      subIterate(collisions,2000,
      function(collision,nextCollision)
      {
      	physicEngine.creatContact(collision.entityA,collision.entityA)
      	nextCollision();
      },
      function()
      {
      	physicEngine.simulate()
      });
    • Si le temps de calcule est trop important, i te faut découper ton traitement
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      subIterate(collection,1,
      function(item,nextItem)
      {
      	traitementA(item,
      	function (err,resultA)
      	{
      		traitementA(item,resultA
      		function (err,resultB)
      		{
      			traitementA(resultA,resultB,nextItem)
      		})
      	})
      },
      function()
      {
      	//all done for collection
      });

    Je te conseille d’écrire toutes tes fonctions avec des signatures asynchrones, et donc d’ajouter le mot clef return a la liste des mots clef a proscrire ( while, for, throw , etc) , car le refactor d’un traitement synchrone en asynchrone est très douloureux !

    Concernant la fréquence d’intervalle des ticks, elle ne peut pas être garantie même avec un setInterval , tu dois donc prendre en compte le delta temps entre chaque tick

  4. #4
    Membre averti
    Homme Profil pro
    Game Créator
    Inscrit en
    Août 2006
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aube (Champagne Ardenne)

    Informations professionnelles :
    Activité : Game Créator

    Informations forums :
    Inscription : Août 2006
    Messages : 39
    Par défaut
    Oui il s'agit d'un jeu temps réel, sinon je ne parlerais pas de boucle coté serveur^^

    Le principe est que même sans client, le serveur tourne, le monde doit évolué, le client ne fait qu'envoyer des requêtes qui modifie des états/valeurs dans les objets qui compose le monde, mais le monde doit tourner tout seul sur le serveur. Je ne sais pas du coup quel méthode adopté.

    Effectivement l'argument de rendre la main pour que les events qui arrivent soit traités semble pertinent, donc on part plutôt sur un setInterval, mais de combien?
    Pour modéliser un univers, combien de tick/frame sont nécessaire ?

    Que faire si le temps de calcule est trop long? Chaque tick doit faire bouger tous les joueurs, tous les objets, calculé les collisions, ajouter des éléments ou en supprimer etc... beaucoup de chose à calculer, plusieurs fois par seconde. Le fait qu'il n'y ait pas de graphisme nous arrange bien, mais que faire si un tick est trop long? Exemple: je décide de faire 1 tick toute les 100ms, si un tick met 150ms à se calculer, ça veux dire que deux tick vont être gérer en parallèle à cause du setInterval... C'est assez problématique !

    Tu me dis qu'il faut couper le traitement pour le rendre asynchrone, je répond forcement non. Il faut qu'un tick se calcule entièrement et sans interruption ni prise en compte des events venu des clients, ça doit etre continue pour etre cohérent. C'est entre deux ticks que sont supposé être dépiler les events clients. Mais là aussi, même problème. Si les tick sont trop long, le "temps entre deux tick" n'existe plus...

  5. #5
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Par défaut
    Je ne comprends pas pourquoi tu parles de "frames" côté serveur. Les frames affichées côté client ne sont pas calculées côté serveur, ce serait une aberration en performance. Faire plusieurs itérations de calcul par seconde, ça ne me paraît pas envisageable avec du TCP. Même avec Websockets c'est ambitieux, dans de mauvaises conditions réseau le ping peut augmenter fortement. Dans tous les cas, il faut que le même code tourne côté client et que la réponse serveur vienne se synchroniser et éventuellement corriger les valeurs côté client. Et j'opterais plutôt pour un ping loop plutôt qu'une itération à délai fixe.

    C'est quel type de jeu exactement ?

  6. #6
    Membre averti
    Homme Profil pro
    Game Créator
    Inscrit en
    Août 2006
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aube (Champagne Ardenne)

    Informations professionnelles :
    Activité : Game Créator

    Informations forums :
    Inscription : Août 2006
    Messages : 39
    Par défaut
    Moi je ne comprends pas comment vous concevez vos jeux en temps réel avec ce raisonnement, pouvez-vous développer?

    Un jeu temps réel doit pouvoir tourner sans aucun client. Le client ne fait que modifier l'état d'objet ou la valeur de variable, mais le serveur doit avoir un système de loop pour simuler la réalité dont il a la charge.
    Et il n'y a pas nécessairement le même nombre de tick coté serveur que coté client qui concerne uniquement l'affichage. L'affichage peut avoir moins de frame et compenser par de l'interpolation.

  7. #7
    Membre chevronné

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Par défaut
    Oui Oui tu dois boucler mais setInterval n’est pas la solution, car si ton code dispose d’au minimum une action asynchrone ( et ce sera forcément le cas) tu risques de paralléliser plusieurs Tick en meme temps , si le temps d’exécution d’un Tick dépasse l’intervalle . tu dois donc utiliser setTimeout ou setImmediate, et "timer" tes fonctions de mise à jour, pour prendre en compte un Delta Temps variable entre chaque Tick

    par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var lastTick = Date.now()
    (function Tick()
    {
    	processInputs(
    	function()
    	{
    		var dt = Date.now()-lastTick;
    		lastTick = Date.now(); 
    		UpdateWorld(dt,
    		function()
    		{
    			processOutputs(
    			function()
    			{
    				setImmediate(Tick);
    			})
    		})
    	})
    })()
    Plus ton serveur effectuera de Tick/s, plus ta simulation sera fin, or tu dois prendre en compte que celui-ci soit variable il faut donc interpoler entre 2 frames

    Pour que le client ne ressente pas de lague il faut que le temps mise a jour + le temps rendus soit inferieur a 41ms ( 24 images/s). cela est donc quasi impossible même en LAN, le client doit donc prédire les mouvements des autres joueurs ce qui revient a simuler le monde localement en parallèle du serveur.

  8. #8
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Par défaut
    Je pensais à ce genre d'approche :
    https://developer.valvesoftware.com/...d_Optimization

  9. #9
    Membre averti
    Homme Profil pro
    Game Créator
    Inscrit en
    Août 2006
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aube (Champagne Ardenne)

    Informations professionnelles :
    Activité : Game Créator

    Informations forums :
    Inscription : Août 2006
    Messages : 39
    Par défaut
    @SylvainPV :
    Justement... Valve utilise une boucle serveur sur ses FPS, ça n'a rien avoir avec toutes les méthodes d'"interpolation" ou "lag compensation" qui sont des "techno" pour synchro le client avec justement la boucle serveur.


    @p3ga5e :
    Oui, ça c'est l'autre méthode, mais pourquoi tu pars du postulat que je ne peux pas de manière synchrone procéder à un tick en un temps X qui correspond (avec une marge d'erreur prit en compte) au temps définit entre deux ticks?

  10. #10
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Par défaut
    Il y a bien une boucle serveur qui vient lire les inputs reçus, mais de ce que j'en comprends, ce n'est pas une horloge à délai fixe:
    The server has a somewhat similar loop:
    1. Sample clock to find start time
    2. Read client user input messages from network
    3. Execute client user input messages
    4. Simulate server-controlled objects using simulation time from last full pass
    5. For each connected client, package up visible objects/world state and send to client
    6. Sample clock to find end time
    7. End time minus start time is the simulation time for the next frame


    Je n'ai jamais travaillé avec ce genre d'approches, côté serveur j'avais l'habitude de travailler avec des projections et des différentiels de date. Mais j'imagine que c'est propre aux jeux en ligne dits "temps réel" comme les FPS ou jeux de combat.

  11. #11
    Membre chevronné

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Par défaut
    De part mon expérience avec NodeJS , même en décomposant au maximum mes traitements par processus node , j’ai toujours regretté les rares fois où j’ai décidé d’écrire un process en synchrone pensant qu’il effectuerait qu’une seul tache .
    C’est pourquoi je te conseil d’utiliser des signatures asynchrone a toutes tes fonctions, même si dans un premier temps ces fonctions sont écrites en synchrone.

    J’imagine qu’il est possible de définir un temps X, avec une large marge d’erreurs, mais cela peut impacter la simulation physique, par exemple un corps rigide rebondissant sur un trampoline, avec pas de simulation trop grands, tu risques d’avoir un coéf de pénétration ainsi qu’une réponse au rebond incohérent, car une fréquence plus rapide de simulation aurait pris en compte la friction et la résistance des ressorts.

    Toutefois une de mes expériences dans le jeu vidéo, va quelque peu complexifier la chose ! En pratique, sur un jeu PS2 utilisant le moteur physique Open Dynamics Engine , nous avons constatés une bien meilleur simulation en fixant le delta time de la méthode dWorldStep. Mon exemple ci-dessus explique en partis pourquoi, nous avons pu fixer le delta time, car nous avons constatés qu’après un pic d’exécution suivait une zone morte, nous avons pu laisser la simulation se désynchroniser du temps réel sachant qu’à court terme elle rattrapait le temps perdus. Le lag obtenu été moins important que les incohérences de réponses du moteur physique … mais bon c’était sur console pour un jeu non multi-player,sur un system bien plus temps réel qu’un serveur NodeJS, l’autre solution est de clamper les réponses physique d’une simulation trop large, mais cela complexifie le paramétrage physiques du jeu

  12. #12
    Membre chevronné

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Par défaut
    Citation Envoyé par Valve
    5. For each connected client, package up visible objects/world state and send to client
    Tient donc, je me suis souvent questionné sur l’intérêt d’une tel optim, car cela implique une réponse adapter pour chaque frustum culling client , complexifiant une autre optim qu’est la parallélisassions des sorties du serveur, car le résultat pour chaque clients diffèrent !
    A mon avis cela n’a d’intérêt que si le client utilisent le résultat serveur comme liste d’objet à rendre, et n’effectue pas lui-même le frustum culling ( traitement qui a un coût on négligeable)

  13. #13
    Membre averti
    Homme Profil pro
    Game Créator
    Inscrit en
    Août 2006
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aube (Champagne Ardenne)

    Informations professionnelles :
    Activité : Game Créator

    Informations forums :
    Inscription : Août 2006
    Messages : 39
    Par défaut
    Citation Envoyé par SylvainPV Voir le message
    4. Simulate server-controlled objects using simulation time from last full pass
    Il s'agit là de la boucle serveur qui fait évoluer la simulation du monde émulé.

    Sinon pour le fait de rendre les traitements asynchrone au sein même du tick, si tu fais ça, ça veux dire que des inputs client vont être traité à n'importe quel moment (potentiellement) de l'évaluation du tick.
    Cela ne me semble pas être cohérent, qu'en dis-tu?

    Pour ce qui est des sorties, tu es obligé de le faire, si tu envois les infos sans distinction à tous les joueurs, il pourra y avoir des triches par des hackers qui écouteront les messages serveurs sur le réseau...
    Mais oui ça a un cout, c'est le cout que les tricheurs nous oblige à perdre... malheureusement.
    Dans le cas d'un jeu solo, on peut partir du principe qu'on s'en fou, mais dès que c'est multi... on est responsable de laisser la triche aussi accessible.

  14. #14
    Membre très actif
    Homme Profil pro
    rien
    Inscrit en
    Décembre 2015
    Messages
    282
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aveyron (Midi Pyrénées)

    Informations professionnelles :
    Activité : rien

    Informations forums :
    Inscription : Décembre 2015
    Messages : 282
    Par défaut
    Ton monde évolue à la milliseconde prés ? ça sert à rien.
    Et si tu utilises le CRON du serveur pour mettre à jour ton monde ?

    Sinon aussi la boucle elle-même de Node.js pour mettre à jour le monde ?

    Je sais qu'il vaut mieux utiliser un truc existant et accordé sur le systeme (cron, node.js,etc) que se faire sa propre boucle serveur qui va forcément à un moment gêner le node.js voire plus.

    J'aimerais bien savoir ce que tu as fait comme solution depuis merci

Discussions similaires

  1. [JSTL] Ecart local/serveur pour affichage de \" avec c:set et c:out
    Par cbil1 dans le forum Taglibs
    Réponses: 0
    Dernier message: 05/01/2012, 11h03
  2. Quels outils pour un jeu avec élément 3D ?
    Par Sergejack dans le forum Flash
    Réponses: 3
    Dernier message: 18/08/2009, 10h53
  3. Architecture client serveur pour un jeu
    Par cb-bk dans le forum Réseau et multijoueurs
    Réponses: 13
    Dernier message: 29/07/2009, 01h00
  4. Quels outils pour un jeu avec élément 3D ?
    Par Sergejack dans le forum Général Conception Web
    Réponses: 4
    Dernier message: 24/07/2009, 13h24
  5. Choix du Serveur pour un jeu flash
    Par Velvounet dans le forum Jeux web
    Réponses: 5
    Dernier message: 08/08/2008, 22h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo