Main Loop on clients?

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Main Loop on clients?

      Hi folks,

      I'm wrestling with two different main loops in my code base.
      One is server-side running the simulation but no rendering,
      and the other is client-side, which needs to do some simulation
      but primarily rendering.

      On the server, the main loop can pretty much spin through
      simulation updates as fast as the CPU can, or can be throttled
      to some fixed rate by delaying until it is time for the
      next simulation frame to be updated. So, I don't know, maybe
      I can make it do 50Hz or so. But this should be basically
      distribute all the queued events to all the correct simulation
      object for their processing.

      Eventually, update events (say, object position events) get all
      the way across the wide 'net over to the clients, come off the
      network layer, and show up as shiny new Events ready to be
      processed.

      So, my questions are really ones of how to set up the main loop
      on the client to allow for a host of factors:

      • It should be running basically the same Simulation as on the server
      • It should incorporate object position Events from the server and treat the data as authoritative,
      • It should allow for simulation frame rates to be different than the rendering frame rate,
      • It should allow object animation sequences to happen,


      To avoid objects from "snapping" between their "current" position
      and their "server-true" position, some form of "ease-in" interpolation
      should be used.

      So how many "current position" concepts for an object are at work now?

      • Last known server-authoritative position,
      • Actual rendered position,
      • Simulation projected position dead-reckoning position,


      Add in animation frame information too, and its interpolation
      between keyframes. Now we have Client simulation frame rate,
      renderer frame rate, and animation frame rate.

      IIRC, Eberly suggests that the Simulation is also ticked at a
      frame rate lower than the physics layer, and that the Physics
      handling is the _real_ driving force behind the main loop.

      OK. With all that behind us and basically "assumed", (right? :-),
      what the heck does the main loop _really_ look like? Is it just
      time-management with delegation out to the Simulation update and
      the physics update and render pass all at the "right time and
      frequency"?

      I think I could add more detail here for any of these bits, of course,
      and I could toss out a whole day's design session around many of
      the followup questions too, but I think I want to toss this out
      for discussion and see where it leads us first.
      jdl
    • I think you're figuring out that there are more "main loops" out there than meet the eye - and you're right.

      On a remote client - it does have to have the ability to make really good guesses about what's going on on a server, but it will never be able to make guesses correctly 100% of the time. For example, a server will never know if one of the players fired a weapon or not, even if a target was clearly in view.

      In a laggy internet situation, the only thing you can do is fake it - the classic example is what Quake did for slower moving weapons like RPGs. If internet lag was bad enough to allow the "victim" to step behind cover, even though the server calculated a valid hit - the local client would "fix" things by having the RPG make a right hand turn around the cover and the dead guy. It's a bit of a bummer, but there's really not much else you can do about it.

      That means that the local client has a main loop that includes the ability to simulate game logic - in order to make some reasonable predictions - but also some code to reconcile the local client's game universe with the "real" game universe when they diverge.

      The book runs through decoupling the render loop from the game logic, which is still the right solution. How you decide to spend the CPU cycles for both system is completely dependant on your game - especially if your game includes expensive logic side code like physics, which can get pretty unstable below 20Hz.

      The big clue is the decoupling itself, and allowing for some scalability for every system in case the hardware or the complexity of the simulation begins to stress out the system.

      The good console game design philosophy says you shouldn't ever let this stuff get out of control - set a good budget for everything and stick to it. I think that is the best solution of all - even though it seems better to have a complicated, solve-everything technology running in the background.

      In other words, keep it simple.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • I think one of the things I'm not quite sure about
      is how to arrange to have the server and client "run
      the same simulation" while fundamentally differing
      in its treatments of events.

      Another point of concern for me is sort of a "where
      do the levels separate" kind of question. I wrote
      a Simulation class, and my notion for it was to take
      the current World State, account for some Messages
      and Events, run some Newtonian physics, and, and, and...
      My original notion was that this was "pure world simulation".
      I should be able to run a complete, autonomous simulation
      on the server with no graphics or rendering. My goal
      was to have this be in one thread.

      But then I realized that this class also had to be the basis
      for the Client Simulation as well. It too had to process
      messages and Events, run the same physics update, but
      in addition, also allow for graphics rendering. Again, my
      goal was to have the simulation in one thread, and the
      graphics rendering code all in a different thread.

      So what I was calling the "Simulation Update", is, I think,
      what everyone else is effectively calling the "physics update".
      Basically, doing Newtonian rigid body updates and all.

      But then, the upper, surrounding parts of my Simulation were
      all located in what others were calling the "main loop". OK.
      I'm having trouble with the "one-to-one-to-one" ratio that
      this imposes on things. That is, each time through the
      main loop, you do one Event distribution and resolution,
      one Physics update, one new-position Event generation pass,
      and (on the client) one graphics render pass.

      I don't think that is realistic, is it? But what I now
      think makes more sense is to re-write the top-level main loop
      so that it is more like a big cam wheel with registered
      (frequency, call-backs) pairs. Then, it is clicking along
      at some relatively high rate, and when a given frequency
      tick happens, the appropriate call-outs are made. So you
      want Simulation/World Updates at 10Hz, but physics updates
      at 30Hz, so out of this top-level loop you get call-outs in
      a 1-to-3 ratio. Seems odd, though.

      Would you throw the graphics rendering call-out into this
      loop as well? And still hope to get, say, 50 or 60Hz? Or,
      with the rendering being done in some other thread, do you
      just make it have its own frame-rate driver/throttle mechanism?
      If you do this, there is some trickiness needed to coordinate
      that with OpenGL's notion of being the "main loop" and having
      call-back for the frame rendering.

      Design thinking out loud. Sorry. :)
      jdl

      The post was edited 1 time, last by jdl ().


    • The book runs through decoupling the render loop from the game logic, which is still the right solution.


      So, let me help clarify things in at least my own mind here. There
      are likely three different major data structures that need to be decoupled,
      yet are very related.

      The first is the fundamental "World Simulation Objects". These are all the
      objects that are present in the game world, some of which might need
      Simulation time for (Newtonian) position updates, some of which might
      need AI time, some of which are backed by real people represented by
      avatar actors, some of which are static builds. This data structure likely
      also has zone-split information or explicit algorithm parallelization cut points
      represented in it near "the top" as well. All these objects likely have World
      position data associated with them.

      We _could_ run the physics simulation directly on that data structure.
      However, it is likely that we _should_ separate and decouple that data
      structure into a second data structure that has specific physics data or
      use by, say, Novodex or some other Physics API, HW or SW as needed.
      But the point here is that a subset of the World Objects need to have
      physics updates done on them; those objects get represented in a data
      structure that is "decoupled" from the real World Objects. The "forward"
      process would be to initialize the physics API with World Position, velocity,
      acceleration, mass, materials, etc. The "reverse" process is represented
      by the routine BaseGamePhysics::VSyncVisibleScene() whereby new
      transform data, etc, is copied back out of the Physics API into the World
      Objects.

      The third data structure being decoupled here is the Scene Graph that
      is actually used for the various rendering passes. Again, we _could_
      run that directly off the World Objects, but the recommendation is to
      actually derive different SceneGraph, based on the World Simulations
      Objects, that contains all the info needed to do the rendering. This data
      structure again has a whole stack of World Position transforms in it,
      but also has model meshes, animation info and maybe some extra
      lighting or camera trickery as well.

      For efficiency, the data structures are maintained and updated semi-
      simultaneously, right? That is, the physics object list is not destroyed and
      rebuilt each frame. In fact, a dirty bit could be used to limit even which
      nodes are updated or need to be added. And, as pointed out in the book,
      there is a "back pointer" to the actor-id in the "user data" of the Physics
      API's node space. Are we using the Event publisher/subscriber pattern
      here to keep them in sync?

      But that separation is a little fuzzier for the SceneGraph. It seems a bit
      more tightly coupled to the World Objects. In fact in some books these
      really are the same data structures.

      As a newbie in this field, _that_ coupling seems to me to be at the heart
      of a lot of problems (I encounter) when reading the literature. In particular,
      in trying to make a network game that clearly has to separate server side
      object simulation from client side rendering, I really don't want to even
      have any rendering bits in the World Simulation objects at all.

      (There has to be some information there about how it _will_ be rendered, of
      course. For example, looking at the animation-based axis-aligned bounding
      box, or deriving a bounding-sphere around all the animated vertices, etc.
      Or more generally, whatever the final rendering will be, the server side
      collision detection has to know what the extent of the model will be.)

      Man, that rambled. But I think my question boils down to this: In the quote
      above, you are talking about separating the World Objects into the Scene
      Graph and just rendering the Scene Graph at, say, 60Hz with a call to
      theSceneGraph->m_RootNode->Render(), right?
      jdl
    • You should probably have graphics rendering done in a way that it "peeks" into the gamestate for a given moment, and draws what it sees. In that sense, your simulation doesn't need to do much more than give the graphics a way to look into it. That's how I see it, anyway (and boy could I be wrong!)
      -Larrik Jaerico

      www.LarrikJ.com
    • Gosh! I was searching around the forum for discussions on how to deal with the separation between the logical (pure data) manifestation of game objects and their visual counterparts. So this thread had me all exited when I saw that it ended (a long time ago, lol).

      My understanding is that the logic updates the state of the game. It does this by operating on the data representations of game objects.

      The View (since it handles anything that smells like visual/auditory/keyboard translation) handles rendering the objects in their correct places based on the state.

      So for a player that wants to move his Character, say, left... the view will convert his keystroke into the appropriate game command. This game command gets fired and dispatched by the event manager which makes its way to the game logic layer.

      The Logic makes proper checks and determines if the request is valid, and if it is, updates the state (the character's position info).

      My question then is this:

      A. Does the View then "dip" (or peek) into the world state to grab the updated position data for the Character?... or

      B. does the Logic directly tells the View that the Character's position was updated via an event (with new pos data riding with the event)?... or

      C. Does the Logic directly manipulate the Character's mesh so that the View can render it in the updated pos?

      I know there's a couple of ways to do this but wanted to hear people's thoughts on this.

      Thanks in advanced!
      RGBme
    • Depends on your architecture. I have a component based architecture, so everything in the world is an entity which contains a list of properties. Entities are controlled and updated by the game logic. Entities that need to be rendered have a property for their visual representation, which inherits from a Renderable class. I maintain a list (actually a Quadtree) of Renderable smart pointers of everything that's active and can be rendered.

      When something tells the entity to move, it sends a message to all of its properties saying "I want to move". All of those properties have a chance to respond. This is when I do collision detection, for example. Once I'm done and have officially moved the entity, one final call is made to it's renderable property that says "I just moved". The renderable property then updates the model's matrix.

      Ratrace & Barbie worked differently. There was a logical representation of the view component for the entity and a low-level view representation. At render time, a synchronize function was called to make sure they were in synch. This function actually became the source of a lot of bugs when we started optimizing our scene graph updates.... but that's another story. :)

      So those are two real-world examples of both A and B at work. In principle, the View shouldn't have read or write permission to anything outside of it's own little world. All it does is update when it's told and render when it's told. If it needs to complain about something, it sends a message and keeps right on going. That's why Mike's architecture makes the Logic explicitly own the View. The more you decouple the two, the easier it will be to rip out the renderer.

      As for question C, I the logic shouldn't even know what a mesh is. That's all view stuff. It should call a function saying "here's the new position, go for it" and the View component should know how to update the matrix, mesh, or whatever else.

      That's my opinion anyway. Remember, there as many ways to do something as there are programmers.

      -Rez
    • Rez -- Thank you for the awesome insight on the two approaches. I have been seeing that people are handling this area in a myriad of ways. When I look at the way I have things, I realize I have been going the component way without knowing it.

      I'll stick to the component model. So it's clear to me now, thanks to your advice, that I will keep the Logic strictly dealing with game state. The View will know about and handle the renderable objects.

      To test things out, I have a Player character structured with an Entity object. This object has a EntityData property, which is an object that has all the data for a given character (name, health, xpos, ypos, zpos, etc.). The Entity object also has a EntityVisual object which inherits from Renderable. So in this way, the Entity is a container of properties. The Logic manages the data part and the view handles the visual part.

      The thing I have to figure work on is the part where something just tells the entity to move -- and the entity does what is necessary. Right now it's the logic that directly changes the EntityData and the View directly changes the EntityVisual. So in this sense, my Entity component structure is nothing but data... it contains no real logic.

      Any other comments are more than appreciated.

      Best wishes!
      RGBme
    • Sounds like a winner! One suggestion I have is that it sounds like you only have two massive components that do a considerable amount of work. You basically have entities with a Logic component and a View component. If you want to embrace component architecture ideals, you might want to consider breaking these up. You can have multiple view components and multiple logic components each doing different things. For example, you may not want all entities to have health, or even a position.

      As an example, we had several properties in Rat Race that could fall into the View realm. One such property was called SeClump (the name is a holdover from Renderware) which manages the model/texture info. Another was called ShadowCaster, which dealt with the dynamic shadowing. Not all entities had models, and not all entities could cast dynamic shadows. This allowed us to mix & match. I remember in Barbie we had one entity called narrator which had nothing but a SoundPlayer property.

      If you're interested in the component architecture, there's a great article in Game Programming Gems 5 by Warrick Buchanan starting on page 177. It gives a nice introduction. :)

      -Rez
    • In a way, you are right. The entity has to big components; it's data representation and the visual one (a logic and view thing, respectively). These components don't, however, do any real work. :) It's only data, no logic.

      I do remember reading that article in GPG 5. If I recall correctly, the component architecture they described made use of the Strategy design pattern. Taking your advice, I am now planning on refactoring my entity structure to extract the right abstractions and give them specific responsibilities. In this way, I will move away from having just two huge Entity properties. I will have to brush up on that article before I do any changes, this way I have a better plan. hehe

      BTW, after reading GCC and playing with my own examples, I decided to attach myself to the open source project, JAD Engine (C# & Managed DX). I've been helping develop minor features for that engine as a way to get down and dirty. The GCC book gave me some awesome lessons that gave me the right amount of context to make myself useful. :)
      RGBme