Announcement

Collapse
No announcement yet.

Trying to understand how networking is done

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Trying to understand how networking is done

    Hi all

    Im trying to figure out what goes on with networking and this is what i have come up with so far.

    The basic network I/O seems to be in place and working somewhat well so i opted not to investigate that part too much.
    in /net/general there is a lot of files named net_something and that something is most of the time a name of a class in /gs/gameobj.
    the purpose of those files seems to be to pack and unpack the corresponding gameobject for net transfer.
    Also in /net/general is the class Network. Network acts as the main entrypoint to network activities from the rest of the code where it is instantiated as g_network

    Then there is the question: what makes it tick?
    I havent really been able to understand that part yet but it has to do with events and is tightly integrated into the normal event operations of the game (wich i have a hard time comprehending as well).

    Then how do you go about getting information propagated to the rest of the network?
    Heres a few examples:

    Code:
     
    
    void WonderTracker::AddBuilt(sint32 which)
    {
    	m_builtWonders |= (uint64)1 << (uint64)which;
    	if(g_network.IsHost()) {
    		g_network.Enqueue(new NetInfo(NET_INFO_CODE_BUILT_WONDERS,
    									  (uint32)(m_builtWonders & 0xffffffff),
    									  (uint32)(m_builtWonders >> (uint64)32)));
    	}
    }
    This peice of code seems to propagate the fact that a wonder has been build from the host.
    Couriusly i havent found any place where the client tells the host that it has completed a wonder.

    Code:
    void BuildQueue::Clear(BOOL fromServer)
    
    {
    	if(!fromServer && g_network.IsClient() && g_network.IsLocalPlayer(m_owner)) {
    		g_network.SendAction(new NetAction(NET_ACTION_CLEAR_QUEUE,
    										   (uint32)m_city));
    	} else if(g_network.IsHost()) {
    		g_network.Block(m_owner);
    		g_network.Enqueue(new NetInfo(NET_INFO_CODE_CLEAR_QUEUE,
    									  (uint32)m_city));
    		g_network.Unblock(m_owner);
    	}
    										   
        while (m_list->GetHead()) {
    		if (m_list->GetHead()->m_category == k_GAME_OBJ_TYPE_WONDER)
    		{
                m_wonderStopped = m_list->GetHead()->m_type;
    			
    			g_theWonderTracker->ClearBuildingWonder(m_wonderStopped, m_owner);
    		}
    
    		delete m_list->RemoveHead();
        } 
    
    	Assert(m_list->GetCount() == 0);
    }
    This peice of code contains handling of both the client action and the host propagating it to the rest of the players. This might be the place to correct bug #26 "* Added check for clear queue actions from clients received after they lost the city to another player".

    I suspect that the chat functions might be a good place to look if you want to know more about real time data transfer but i hasnt been in my interest.

    I hope you will give me some usefull feedback to this

    -klaus

  • #2
    Code:
     
    
    void BuildQueue::Clear(BOOL fromServer)
    
    {
    #if !defined(ACTIVISION_ORIGINAL) // possible bug 26 solution
    	if (m_owner == m_city->GetOwner()) {
    #endif
    		if(!fromServer && g_network.IsClient() && g_network.IsLocalPlayer(m_owner)) {
    			g_network.SendAction(new NetAction(NET_ACTION_CLEAR_QUEUE,
    											(uint32)m_city));
    		} else if(g_network.IsHost()) {
    			g_network.Block(m_owner);
    			g_network.Enqueue(new NetInfo(NET_INFO_CODE_CLEAR_QUEUE,
    										(uint32)m_city));
    			g_network.Unblock(m_owner);
    		}
    											   
    		while (m_list->GetHead()) {
    			if (m_list->GetHead()->m_category == k_GAME_OBJ_TYPE_WONDER)
    			{
    				m_wonderStopped = m_list->GetHead()->m_type;
    				
    				g_theWonderTracker->ClearBuildingWonder(m_wonderStopped, m_owner);
    			}
    
    			delete m_list->RemoveHead();
    		} 
    
    		Assert(m_list->GetCount() == 0);
    #if !defined(ACTIVISION_ORIGINAL) // possible bug 26 solution
    	}
    #endif
    }
    this might do the trick

    can someone compile this with the latest source and post it here so that it might be playtested?
    Attached Files

    Comment


    • #3
      Could you make a check to make sure non-host players get their pw every turn? I have know idea how these things work so feel free to ignore me
      "

      Comment


      • #4
        Originally posted by EPW
        Could you make a check to make sure non-host players get their pw every turn? I have know idea how these things work so feel free to ignore me
        Well the reason i wrote this is that i have almost no idea myself

        Writing it and reading it again made me think though.
        There is a class called net_action and in it is a very long switch that contains this bit of code:

        Code:
         
        		case NET_ACTION_CLEAR_QUEUE:
        		{
        			DPRINTF(k_DBG_NET, ("Client %d clearing queue for city %lx\n",
        								index, m_data[0]));
        			Unit city(m_data[0]);
        [B]#if !defined(ACTIVISION_ORIGINAL) // possible bug 26 solution[/B]
        			if(g_theUnitPool->IsValid(city) [B]&& city->GetOwner() == index[/B]) {
        				city.GetData()->GetCityData()->GetBuildQueue()->Clear();
        			}
        [B]#else[/B]
        			if(g_theUnitPool->IsValid(city)) {
        				city.GetData()->GetCityData()->GetBuildQueue()->Clear();
        			}
        [B]#endif[/B]
        			break;
        The trained eye will spot the changes i made, this might also be a place to solve the #26 bug.
        Attached Files

        Comment


        • #5
          This might be the solution for bug #30

          "* Network object bookkeeping gets cleared when exiting a game (exiting and rejoining occasionally left something lying around, resulting in a resync not long after joining)"

          Code:
          void Network::RemovePlayer(uint16 id) 
          {
          	DPRINTF(k_DBG_NET, ("Removing player %d\n", id));
          
          	if(id == m_pid) {
          		SessionLost();
          [B]#if !defined(ACTIVISION_ORIGINAL) // possible bug 30 solution
          		// removing object bookkeeping
          		if (m_gameObjects) delete m_gameObjects;
          #endif[/B] 
          	}
          
          ...
          -klaus
          Attached Files

          Comment


          • #6
            well good luck kaan! i think if MP can be sorted a bit, then many people will be super happy
            'The very basis of the liberal idea – the belief of individual freedom is what causes the chaos' - William Kristol, son of the founder of neo-conservitivism, talking about neo-con ideology and its agenda for you.info here. prove me wrong.

            Bush's Republican=Neo-con for all intent and purpose. be afraid.

            Comment


            • #7
              Fantastic to see someone's finally tackling MP

              I can't offer it myelf, but hopefully you'll get the support needed to bring it at least on par with 1.11...
              Administrator of WePlayCiv -- Civ5 Info Centre | Forum | Gallery

              Comment


              • #8
                Re: Trying to understand how networking is done

                Originally posted by kaan
                Code:
                 
                void WonderTracker::AddBuilt(sint32 which)
                {
                	m_builtWonders |= (uint64)1 << (uint64)which;
                	if(g_network.IsHost()) {
                		g_network.Enqueue(new NetInfo(NET_INFO_CODE_BUILT_WONDERS,
                		  (uint32)(m_builtWonders & 0xffffffff),
                		  (uint32)(m_builtWonders >> (uint64)32)));
                	}
                }
                This peice of code seems to propagate the fact that a wonder has been build from the host.
                Couriusly i havent found any place where the client tells the host that it has completed a wonder.
                That's not so curious. The host doesn't need to be told that the client completed a wonder, because it will determine this fact for itself when it processes the turn of the player in question. What is strange is that the client needs to be told. I'm not sure whether clients do any kind of processing at all, or whether the host works everything out and then just tells the client what has happened...

                Code:
                void BuildQueue::Clear(BOOL fromServer)
                
                {
                	if(!fromServer && g_network.IsClient() && g_network.IsLocalPlayer(m_owner)) {
                		g_network.SendAction(new NetAction(NET_ACTION_CLEAR_QUEUE,
                		  (uint32)m_city));
                	} else if(g_network.IsHost()) {
                		g_network.Block(m_owner);
                		g_network.Enqueue(new NetInfo(NET_INFO_CODE_CLEAR_QUEUE,
                		  (uint32)m_city));
                		g_network.Unblock(m_owner);
                	}
                										   
                    while (m_list->GetHead()) {
                		if (m_list->GetHead()->m_category == k_GAME_OBJ_TYPE_WONDER)
                		{
                            m_wonderStopped = m_list->GetHead()->m_type;
                			
                			g_theWonderTracker->ClearBuildingWonder(m_wonderStopped, m_owner);
                		}
                
                		delete m_list->RemoveHead();
                    } 
                
                	Assert(m_list->GetCount() == 0);
                }
                This peice of code contains handling of both the client action and the host propagating it to the rest of the players.


                In this case, it makes sense that the client must tell the host, because this is a user-triggered event and so there is no way the host would be able to work out for itself that it occured.

                Comment


                • #9
                  Re: Re: Trying to understand how networking is done

                  Originally posted by J Bytheway


                  That's not so curious. The host doesn't need to be told that the client completed a wonder, because it will determine this fact for itself when it processes the turn of the player in question. What is strange is that the client needs to be told. I'm not sure whether clients do any kind of processing at all, or whether the host works everything out and then just tells the client what has happened...

                  Code:
                  void BuildQueue::Clear(BOOL fromServer)
                  
                  {
                  	if(!fromServer && g_network.IsClient() && g_network.IsLocalPlayer(m_owner)) {
                  		g_network.SendAction(new NetAction(NET_ACTION_CLEAR_QUEUE,
                  		  (uint32)m_city));
                  	} else if(g_network.IsHost()) {
                  		g_network.Block(m_owner);
                  		g_network.Enqueue(new NetInfo(NET_INFO_CODE_CLEAR_QUEUE,
                  		  (uint32)m_city));
                  		g_network.Unblock(m_owner);
                  	}
                  										   
                      while (m_list->GetHead()) {
                  		if (m_list->GetHead()->m_category == k_GAME_OBJ_TYPE_WONDER)
                  		{
                              m_wonderStopped = m_list->GetHead()->m_type;
                  			
                  			g_theWonderTracker->ClearBuildingWonder(m_wonderStopped, m_owner);
                  		}
                  
                  		delete m_list->RemoveHead();
                      } 
                  
                  	Assert(m_list->GetCount() == 0);
                  }
                  This peice of code contains handling of both the client action and the host propagating it to the rest of the players.


                  In this case, it makes sense that the client must tell the host, because this is a user-triggered event and so there is no way the host would be able to work out for itself that it occured.
                  Well i hadnt thought about it that way but it would be logical that the host is responsible for the gamelogic and that the client need only to be concerned about userinteractions.

                  and to the fans: thanks for the support

                  Comment


                  • #10
                    possible bug 21 solution "* Added extra checks to disable science victory in network games"

                    Code:
                    #if defined(ACTIVISION_ORIGINAL)
                    	if (GetGaiaController()->TurnsToComplete() == 0)
                    	{
                    		GameOver(GAME_OVER_WON_SCIENCE, -1);
                    	}
                    }
                    #else // possible bug 21 solution
                    	if ([B]!g_network.IsActive()[/B] && GetGaiaController()->TurnsToComplete() == 0)
                    	{
                    		GameOver(GAME_OVER_WON_SCIENCE, -1);
                    	}
                    #endif
                    Code:
                     
                    
                    		case GAME_OVER_WON_SCIENCE:
                    #if defined(ACTIVISION_ORIGINAL)
                                GenerateDescriptionString(TRUE);
                    			m_hasWonTheGame = TRUE;
                    			for(i = 1; i < k_MAX_PLAYERS; i++) {
                    				if(g_player[i] && !g_player[i]->m_isDead && i != m_owner) {
                    					
                    					
                    					
                    					
                    					
                    					if(!g_network.IsClient()) {
                    						
                    						g_player[i]->GameOver(GAME_OVER_LOST_SCIENCE, -1);
                    					}
                    				}
                    			}
                    #else // possible bug 21 solution
                    			[B]if (!g_network.IsActive()) {[/B] 
                    				GenerateDescriptionString(TRUE);
                    				m_hasWonTheGame = TRUE;
                    				for(i = 1; i < k_MAX_PLAYERS; i++) {
                    					if(g_player[i] && !g_player[i]->m_isDead && i != m_owner) {
                    						
                    						
                    						
                    						
                    						
                    						if(!g_network.IsClient()) {
                    							
                    							g_player[i]->GameOver(GAME_OVER_LOST_SCIENCE, -1);
                    						}
                    					}
                    				}
                    			[B]}[/B] 
                    #endif
                    Both codesnippets are from the Player class.
                    Attached Files

                    Comment


                    • #11
                      OK heres an update


                      Client/Host Communication
                      I have found that most communication between client and host is done through 2 classes:
                      net_action and net_info

                      net_action is used to transport requests/trigger events from the clients to the host.
                      net_info is used to propagate information from the host to the clients and/or to execute events on the clients.

                      To complicate things a bit it seems that it isnt all traffic that follows this routine.


                      Code Structure
                      Much of the networking code is tangled into the "normal" code and the structure suggest that most of the code is run simultaneusly on both the host and client.
                      To keep that environment sane the host will often check the clients data and if nessesary request a resync.
                      This structure facilitates that any client can become host if the host suddently drops out of the game.

                      Comment


                      • #12
                        I have been looking at the code for where battle takes place and I found it, but what I havent found is even the slightest bit of networking code relating to battle. Did any of you stumple upon anything that might be usefull?

                        Comment

                        Working...
                        X