User:Geoff the Medio/Coding

From FreeOrionWiki
< User:Geoff the Medio
Revision as of 23:09, 17 May 2005 by Geoff the Medio (Talk | contribs) (Bugs with changes?)

Jump to: navigation, search

Bugs with changes?

I seem to be getting crash bugs whenever the selected fleet would be destroyed by a drag-drop. Start a game, open up the fleets window, drag a ship to a new fleet, select that fleet, then drag its ship, or the fleet itself, back onto the original fleet. There's an unhandled exception about an invalid pointer.

Update: I removed the changes to MapWnd::RenderFleetMovementLines() and don't get the crashes anymore, so I don't think the problem was related to the other changes (which are more important).

System::Insert()

Replace System::Insert (appropriate version thereof) with this:

int System::Insert(int obj_id, int orbit)
{
    if (orbit < -1)
        throw std::invalid_argument("System::Insert() : Attempted to place an object in an orbit less than -1");
    if (!GetUniverse().Object(obj_id))
        throw std::invalid_argument("System::Insert() : Attempted to place an object in a System, when the object is not already in the Universe");
	
    // erase all preexisting instances of object in m_objects
    std::list< orbit_iterator > del_its;
    del_its.clear();
    for (orbit_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
        if (it->second == obj_id)
            del_its.push_back(it);
    for (std::list<orbit_iterator >::iterator it = del_its.begin(); it != del_its.end(); ++it)
        m_objects.erase(*it);
		
    if (m_orbits <= orbit)
        m_orbits = orbit + 1;
	
    m_objects.insert(std::pair<int, int>(orbit, obj_id));
    
    Fleet *fleet = GetUniverse().Object<Fleet>(obj_id);
    if(fleet)
        FleetAddedSignal()(*fleet);

    StateChangedSignal()();
	return orbit;
}

This comment should be added to System::Remove to clarify its function:

// assumes only one instance of object is in m_objects

SidePanel::ClickColoinze()

Goes in SidePanel.cpp

void SidePanel::PlanetPanel::ClickColonize()
{
    const Planet *planet = GetPlanet();
    int empire_id = HumanClientApp::GetApp()->EmpireID();
    std::map<int, int> pending_colonization_orders = HumanClientApp::GetApp()->PendingColonizationOrders();
    std::map<int, int>::const_iterator it = pending_colonization_orders.find(planet->ID());
    if(it == pending_colonization_orders.end()) // colonize
    {
        Ship *ship = FindColonyShip(planet->SystemID());
        if(!ship)
            throw std::runtime_error("SidePanel::PlanetPanel::ClickColonize ship not found!");
        
        Fleet *fleet = ship->GetFleet();
  	    
        if(!fleet->Accept(StationaryFleetVisitor(*fleet->Owners().begin())))
        {
            GG::ThreeButtonDlg dlg(320,200,UserString("SP_USE_DEPARTING_COLONY_SHIPS_QUESTION"),
                                   ClientUI::FONT,ClientUI::PTS,ClientUI::WND_COLOR,ClientUI::CTRL_BORDER_COLOR,ClientUI::CTRL_COLOR,ClientUI::TEXT_COLOR,2,
                                   "Yes","No");
            dlg.Run();
            if(dlg.Result()!=0)
                return;
        }
        
        HumanClientApp::Orders().IssueOrder( new FleetColonizeOrder(empire_id, ship->ID(), planet->ID()) );
        
        // BAD!! fleet might be already deleted, for instance, right now if the fleet is
        // selected at a FleetWnd and ship was the one and only, FleetWnd will delete the fleet
        // ok, that behaviour is also bad, but even so, you have to expect sideeffects when
        // issueing orders.
        
        // Where does FleetWnd delete the fleet?  I removed the code
        // from this function that did so, and replaced it with the
        // IssueOrder() below...

        if (!fleet->NumShips()) {
            HumanClientApp::Orders().IssueOrder( new DeleteFleetOrder( empire_id, fleet->ID()) );
        }
    }
    else // cancel colonization
    {
        const FleetColonizeOrder *col_order = dynamic_cast<const FleetColonizeOrder*>(HumanClientApp::Orders().ExamineOrder(it->second));
        int ship_id = col_order ? col_order->ShipID() : UniverseObject::INVALID_OBJECT_ID;
        Ship *ship = GetUniverse().Object<Ship>(ship_id);
        int fleet_id = ship->FleetID();
        
        // check if ship's fleet still exists
        Fleet *fleet = ship ? GetUniverse().Object<Fleet>(fleet_id) : NULL;
        
        if (!fleet) {
            // fleet doesn't exist, so recind order that deleted it, thus recreating it
            std::map<int, int> del_ords = HumanClientApp::GetApp()->PendingDeleteFleetOrders();
            std::map<int, int>::iterator del_ord_it = del_ords.find(fleet_id);
            if (del_ord_it != del_ords.end())
                HumanClientApp::Orders().RecindOrder(del_ord_it->second);
            
            // try again to get poitner...
            fleet = GetUniverse().Object<Fleet>(fleet_id);
        }
  
        if (!fleet)
            throw std::runtime_error("Sidepanel::ClickColonize: Fleet couldn't be recreated by recinding delete order!");
        
        // Recind colonization order, adding ship back into fleet
        HumanClientApp::Orders().RecindOrder(it->second);

        // make sure that fleet appears at a possibly opend FleetWnd      
        for (FleetWnd::FleetWndItr it = FleetWnd::FleetWndBegin(); it != FleetWnd::FleetWndEnd(); ++it) {
            FleetWnd *fleet_wnd = *it;
            if(fleet->SystemID() == fleet_wnd->SystemID()
               && !fleet_wnd->ContainsFleet(fleet->ID())) 
            {
                fleet_wnd->AddFleet(GetUniverse().Object<Fleet>(fleet->ID()));
                break;
            }
        }
    }		
}

FleetColonizeOrder class header

Goes in Order.h

class FleetColonizeOrder : public Order
{
public:
    /** \name Structors */ //@{
    FleetColonizeOrder();
    FleetColonizeOrder(const GG::XMLElement& elem);
    FleetColonizeOrder(int empire, int ship, int planet);
    //@}

    /** \name Accessors */ //@{
    int   PlanetID() const  {return m_planet;} ///< returns ID of the planet to be colonized
    int   ShipID  () const  {return m_ship  ;} ///< returns ID of the ship which is colonizing the planet

    virtual void           ServerExecute() const; //< called if the server allows the colonization effort 

    virtual GG::XMLElement XMLEncode() const; ///< constructs an XMLElement for the order
    //@}

private:
    /**
     *  Preconditions:
     *     - m_fleet must be the ID of a fleet owned by issuing empire
     *     - m_planet must be the ID of an un-owned planet.
     *     - the fleet and the planet must have the same x,y coordinates
     *     - the fleet must contain a colony ship
     *
     *  Postconditions:
     *      - a colony ship will be removed from the fleet and deallocated
     *        if the fleet becomes empty it will be deallocated.
     *      - the empire issuing the order will be added to the list of owners
     *            for the planet
     *      - the planet's population will be increased
     *      - the planet will be added to the empire's list of owned planets
     *     
     */
    //< either ExecuteServerApply or ExecuteServerRevoke is called!!!
    virtual void ExecuteImpl() const;
    virtual bool UndoImpl() const;

    int m_ship;
    int m_planet;

    // these are for undoing this order only
    mutable int m_colony_fleet_id;   // the fleet from which the colony ship was taken
};

DeleteFleetOrder class header

Goes in Order.h

/////////////////////////////////////////////////////
// DeleteFleetOrder
/////////////////////////////////////////////////////
/** the Order subclass that represents forming a new fleet. 
    Only one of system or position will be used to place the new fleet.*/
class DeleteFleetOrder : public Order
{
public:
    /** \name Structors */ //@{
    DeleteFleetOrder();
    DeleteFleetOrder(const GG::XMLElement& elem);
    DeleteFleetOrder(int empire, int fleet);
    //@}

    /** \name Accessors */ //@{
    int   FleetID() const   {return m_fleet;}  ///< returns ID of the fleet to be deleted

    virtual GG::XMLElement XMLEncode() const; ///< constructs an XMLElement for the order
    //@}

private:
    /**
     *  Preconditions:
     *     - m_fleet must be the ID of a fleet owned by issuing empire
     *     - m_planet must be the ID of an un-owned planet.
     *     - the fleet and the planet must have the same x,y coordinates
     *     - the fleet must contain a colony ship
     *
     *  Postconditions:
     *      - a colony ship will be removed from the fleet and deallocated
     *        if the fleet becomes empty it will be deallocated.
     *      - the empire issuing the order will be added to the list of owners
     *            for the planet
     *      - the planet's population will be increased
     *      - the planet will be added to the empire's list of owned planets
     *     
     */
    //< either ExecuteServerApply or ExecuteServerRevoke is called!!!
    virtual void ExecuteImpl() const;
    virtual bool UndoImpl() const;

    int m_fleet;
	
	// these are for undoing this order only
	mutable std::string m_fleet_name;
	mutable int m_system_id;
};

HumanClientApp Header changes

In HumanClientApp.h, after this declaration:

    /** Returns a map from Planet IDs to pending (issued earlier this turn and undo-able) colonization order IDs. */
    std::map<int, int> PendingColonizationOrders() const;

Add this:

   /** Returns a map from Fleet IDs to pending (issued earlier this turn, may not be undo-able) delete fleet order IDs. */
    std::map<int, int> PendingDeleteFleetOrders() const;
    //@}

HumanClientApp Function definition

In HumanClientApp.cpp, after the code for the function:

std::map<int, int> HumanClientApp::PendingColonizationOrders() const

Add this function:

std::map<int, int> HumanClientApp::PendingDeleteFleetOrders() const
{
    std::map<int, int> retval;
    for (OrderSet::const_iterator it = m_orders.begin(); it != m_orders.end(); ++it) {
        if (const DeleteFleetOrder* order = dynamic_cast<const DeleteFleetOrder*>(it->second)) {
            retval[order->FleetID()] = it->first;
        }
    }
    return retval;
}

FleetColonizeOrder function definitions

Goes in Order.cpp

FleetColonizeOrder::FleetColonizeOrder() : 
    Order(),
    m_ship(UniverseObject::INVALID_OBJECT_ID),
    m_planet(UniverseObject::INVALID_OBJECT_ID)
{
}

FleetColonizeOrder::FleetColonizeOrder(const GG::XMLElement& elem) : Order(elem.Child("Order"))
{
    if(elem.Tag() != ("FleetColonizeOrder"))
        throw std::invalid_argument("Attempted to construct FleetColonizeOrder from malformed XMLElement");
    
    m_ship   = lexical_cast<int>(elem.Child("m_ship").Text());
    m_planet = lexical_cast<int>(elem.Child("m_planet").Text());
    m_colony_fleet_id = lexical_cast<int>(elem.Child("m_colony_fleet_id").Text());
}

FleetColonizeOrder::FleetColonizeOrder(int empire, int ship, int planet) : 
    Order(empire),
    m_ship(ship),
    m_planet(planet)
{
}

void FleetColonizeOrder::ServerExecute() const
{
    Universe& universe = GetUniverse();
    universe.Delete(m_ship);
    Planet* planet = universe.Object<Planet>(m_planet);
    planet->ResetMaxMeters();
    planet->AdjustMaxMeters();
    planet->AdjustPop(INITIAL_COLONY_POP);
    planet->GetMeter(METER_FARMING)->SetCurrent(INITIAL_COLONY_POP);
    planet->GetMeter(METER_HEALTH)->SetCurrent(planet->GetMeter(METER_HEALTH)->Max());
    planet->AddOwner(EmpireID());
    if (System* system = planet->GetSystem())
        system->AddOwner(EmpireID());
}

XMLElement FleetColonizeOrder::XMLEncode() const
{
    XMLElement retval("FleetColonizeOrder");
    retval.AppendChild(Order::XMLEncode());
    retval.AppendChild(XMLElement("m_ship", lexical_cast<std::string>(m_ship)));
    retval.AppendChild(XMLElement("m_planet", lexical_cast<std::string>(m_planet)));
    retval.AppendChild(XMLElement("m_colony_fleet_id", lexical_cast<std::string>(m_colony_fleet_id)));
    return retval;
}

void FleetColonizeOrder::ExecuteImpl() const
{
    ValidateEmpireID();

    Universe& universe = GetUniverse();
    
    // look up the ship and fleet in question
    Ship* ship = universe.Object<Ship>(m_ship);
    Fleet* fleet = universe.Object<Fleet>(ship->FleetID());
   
    // ensure fleet exists
    if (!fleet)
        throw std::runtime_error("Illegal fleet id specified in fleet colonize order.");

    // verify that empire issuing order owns specified fleet
    if (!fleet->OwnedBy(EmpireID()))
        throw std::runtime_error("Empire attempted to issue colonize order to another's fleet.");

    // verify that planet exists and is un-occupied.
    Planet* planet = universe.Object<Planet>(m_planet);
    if (planet == NULL)
        throw std::runtime_error("Colonization order issued with invalid planet id.");

    if (!planet->Unowned())
        throw std::runtime_error("Colonization order issued for owned planet.");    

    // verify that planet is in same system as the fleet
    if (planet->SystemID() != fleet->SystemID() ||
        planet->SystemID() == UniverseObject::INVALID_OBJECT_ID) {
        throw std::runtime_error("Fleet specified in colonization order is not in "
                                 "specified system.");
    }

    planet->SetIsAboutToBeColonized(true);

    m_colony_fleet_id = fleet->ID(); // record the fleet in which the colony ship started

    // Remove colony ship from fleet;
    fleet->RemoveShip(m_ship);
}

bool FleetColonizeOrder::UndoImpl() const
{
    // Note that this function does double duty: it serves as a normal client-side undo, but must also
    // serve as a server-side undo, when more than one empire tries to colonize the same planet at the
    // same time.

    Universe& universe = GetUniverse();
    
    Fleet* fleet = universe.Object<Fleet>(m_colony_fleet_id);
    Planet* planet = universe.Object<Planet>(m_planet);

    planet->SetIsAboutToBeColonized(false);
    
    Ship* ship = universe.Object<Ship>(m_ship);

    // if the fleet from which the colony ship came no longer exists or has moved, make a new one	
    if (!fleet || fleet->SystemID() != ship->SystemID()) {
        System* system = planet->GetSystem();
        fleet = new Fleet("Colony Fleet", system->X(), system->Y(), EmpireID());
        universe.Insert(fleet);
        fleet->AddShip(ship->ID());
        system->Insert(fleet);
    } else {
        fleet->AddShip(ship->ID());
    }

    return true;
}

DeleteFleetOrder function definitions

Goes in Order.cpp

////////////////////////////////////////////////
// DeleteFleetOrder
////////////////////////////////////////////////
DeleteFleetOrder::DeleteFleetOrder() : 
    Order(),
    m_fleet(-1)
{
}

DeleteFleetOrder::DeleteFleetOrder(const GG::XMLElement& elem):
    Order(elem.Child("Order"))
{
    if(elem.Tag() != ("DeleteFleetOrder"))
        throw std::invalid_argument("Attempted to construct DeleteFleetOrder from malformed XMLElement");

    m_fleet = lexical_cast<int>(elem.Child("m_fleet").Text());
}

DeleteFleetOrder::DeleteFleetOrder(int empire, int fleet) : 
    Order(empire),
    m_fleet(fleet)
{
}

GG::XMLElement DeleteFleetOrder::XMLEncode() const
{
    XMLElement retval("DeleteFleetOrder");
    retval.AppendChild(Order::XMLEncode());
    retval.AppendChild(XMLElement("m_fleet", lexical_cast<std::string>(m_fleet)));
    return retval;
}

void DeleteFleetOrder::ExecuteImpl() const
{
    ValidateEmpireID();

    Fleet* fleet = GetUniverse().Object<Fleet>(FleetID());

    if(!fleet)
        throw std::runtime_error("Illegal fleet id specified in fleet colonize order.");

    if (!fleet->OwnedBy(EmpireID()))
        throw std::runtime_error("Empire attempted to issue deletion order to another's fleet.");

    if (fleet->NumShips())
        throw std::runtime_error("Attempted to delete an unempty fleet.");

    // store info so as to be able to recind order
    m_system_id = fleet->GetSystem()->ID(); // better fleet->SystemID(); 
    m_fleet_name = fleet->Name();

    GetUniverse().Delete(FleetID());
}

bool DeleteFleetOrder::UndoImpl() const
{
    Universe& universe = GetUniverse();
    System* system = universe.Object<System>(m_system_id);
    
    Fleet *fleet = new Fleet(m_fleet_name, system->X(), system->Y(), EmpireID());
           
    universe.InsertID(fleet, m_fleet);
    system->Insert(fleet);

    return true;
}

Destroy / Universe Header changes

In public section of Universe.h, after:

    /** Applies all Effects from Buildings, Specials, Techs, etc. */
    void ApplyEffects();

add:

    /** Adds passed object ID to the list of UniverseObjects that have had Destroy effects executed on them this turn and need to be Deleted after effects processing */
    void AddDestroyTarget(int id);

And, in protected section of Universe.h, add:

    /** Calls Universe::Delete() to actually delete the memory objects of UniverseObjects that had a Destroy effected executed on them */
    void DoDestroyEffectDeletion();

and, also in protected section of Universe.h, add:

    std::list<int> m_destroy_effect_targets;				///< ordered list of UniverseObject IDs that have been the target of a Destroy effect this turn and need to be Deleted after effects processing is finished

Destroy Effect Functions

Replace this function in Universe.cpp (minor changes to change how destroy effects are executed):

void Universe::ApplyEffects()
{
    // reset max meter state that is affected by the application of effects
    for (const_iterator it = begin(); it != end(); ++it) {
        it->second->ResetMaxMeters();
        it->second->AdjustMaxMeters();
    }

    // emtpy list of UniverseObjects that have have had Destroy effects executed on them this turn
    m_destroy_effect_targets.clear();

    // cache all activation and scoping condition results before applying Effects, since the application of
    // these Effects may affect the activation and scoping evaluations
    typedef std::multimap<boost::shared_ptr<const Effect::EffectsGroup>, std::pair<int, Effect::EffectsGroup::TargetSet> > EffectsAndTargetsMap;
    typedef std::pair<boost::shared_ptr<const Effect::EffectsGroup>, std::pair<int, Effect::EffectsGroup::TargetSet> > EffectsAndTargetsMapElem;
    EffectsAndTargetsMap effects_targets_map;
    for (EmpireManager::iterator it = Empires().begin(); it != Empires().end(); ++it) {
        it->second->ClearRefinements();
        for (Empire::TechItr tech_it = it->second->TechBegin(); tech_it != it->second->TechEnd(); ++tech_it) {
            const Tech* tech = GetTech(*tech_it);
            assert(tech);
            for (unsigned int i = 0; i < tech->Effects().size(); ++i) {
                boost::shared_ptr<const Effect::EffectsGroup> effect = tech->Effects()[i];
                EffectsAndTargetsMapElem map_elem(effect, std::make_pair(it->second->CapitolID(), Effect::EffectsGroup::TargetSet()));
                tech->Effects()[i]->GetTargetSet(it->second->CapitolID(), map_elem.second.second);
                effects_targets_map.insert(map_elem);
            }
        }
    }
    std::vector<Building*> buildings = FindObjects<Building>();
    for (unsigned int i = 0; i < buildings.size(); ++i) {
        const BuildingType* building_type = buildings[i]->GetBuildingType();
        assert(building_type);
        for (unsigned int j = 0; j < building_type->Effects().size(); ++j) {
            boost::shared_ptr<const Effect::EffectsGroup> effect = building_type->Effects()[j];
            EffectsAndTargetsMapElem map_elem(effect, std::make_pair(buildings[i]->ID(), Effect::EffectsGroup::TargetSet()));
            building_type->Effects()[j]->GetTargetSet(buildings[i]->ID(), map_elem.second.second);
            effects_targets_map.insert(map_elem);
        }
    }
    for (Universe::const_iterator it = begin(); it != end(); ++it) {
        for (std::set<std::string>::const_iterator special_it = it->second->Specials().begin();
             special_it != it->second->Specials().end();
             ++special_it) {
            const Special* special = GetSpecial(*special_it);
            assert(special);
            for (unsigned int i = 0; i < special->Effects().size(); ++i) {
                boost::shared_ptr<const Effect::EffectsGroup> effect = special->Effects()[i];
                EffectsAndTargetsMapElem map_elem(effect, std::make_pair(it->first, Effect::EffectsGroup::TargetSet()));
                special->Effects()[i]->GetTargetSet(it->first, map_elem.second.second);
                effects_targets_map.insert(map_elem);
            }
        }
    }

    // execute effects on cached results
    std::map<std::string, Effect::EffectsGroup::TargetSet> executed_nonstacking_effects;
    for (EffectsAndTargetsMap::const_iterator it = effects_targets_map.begin(); it != effects_targets_map.end(); ++it) {
        // if other EffectsGroups with the same stacking group have affected some of the targets in the scope of the current EffectsGroup, skip them
        std::map<std::string, std::set<UniverseObject*> >::iterator non_stacking_it = executed_nonstacking_effects.find(it->first->StackingGroup());
        Effect::EffectsGroup::TargetSet targets(it->second.second);
        if (non_stacking_it != executed_nonstacking_effects.end()) {
            for (Effect::EffectsGroup::TargetSet::const_iterator object_it = non_stacking_it->second.begin(); object_it != non_stacking_it->second.end(); ++object_it) {
                targets.erase(*object_it);
            }
        }

        // execute the Effects in the EffectsGroup
        it->first->Execute(it->second.first, targets);

        // if this EffectsGroup belongs to a stacking group, add the objects just affected by it to executed_nonstacking_effects
        if (it->first->StackingGroup() != "") {
            Effect::EffectsGroup::TargetSet& affected_targets = executed_nonstacking_effects[it->first->StackingGroup()];
            for (Effect::EffectsGroup::TargetSet::const_iterator object_it = targets.begin(); object_it != targets.end(); ++object_it) {
                affected_targets.insert(*object_it);
            }
        }
    }

    // delete objects that had Destroy effects executed on them
    DoDestroyEffectDeletion();
}

And add these two new functions (called by the above):

void Universe::AddDestroyTarget(int id)
{
    m_destroy_effect_targets.push_back(id);
}

void Universe::DoDestroyEffectDeletion()
{
    std::set<int> destroyedObjects;
    destroyedObjects.clear();
	
    // iterate through destroy effect targets, destroying them and doing side effects as appropriate
    for (std::list<int>::iterator target_it = m_destroy_effect_targets.begin(); target_it != m_destroy_effect_targets.end(); ++target_it) {
		
        int id = *target_it;
		
        std::set<int>::iterator dest_objs_it = destroyedObjects.find(id);
        // ensure object hasn't already been destroyed
        if (dest_objs_it == destroyedObjects.end()) {
			
            // add it to the list of destroyed objects
            destroyedObjects.insert(id);
			
            // find out if object still exists
            UniverseObject* obj = 0;
            iterator it = m_objects.find(id);
            if (it != m_objects.end()) {
                obj = it->second;
				
                // Special cases for various types of UniverseObject
				
                if (Ship* ship = universe_object_cast<Ship*>(obj)) {
                    // if a ship is being deleted, and it is the last ship in its fleet,
                    // then the fleet should also be deleted (after the ship is deleted)
                    if (Fleet* fleet = ship->GetFleet()) {
                        fleet->RemoveShip(ship->ID());
                        if (fleet->NumShips() <= 0)
                            m_destroy_effect_targets.push_back(fleet->ID());
                    }
                }

                else if (Fleet* fleet = universe_object_cast<Fleet*>(obj)) {
                    // if a fleet is being deleted, all its ships should be deleted as well.
                    // (before the fleet is deleted)					
					
                    // Effect execution should happen after player orders are executed, so that
                    // orders to delete fleets or transfer ships from fleets happen before
                    // these destroy effects
					
                    std::set<int> ships;
                    for(Fleet::const_iterator ship_it = fleet->begin(); ship_it != fleet->end(); ++ship_it)
                        ships.insert(*ship_it);

                    // delete ships NOW, don't just add them to the list of things to delete later.
                    // this way, when when the Delete(fleet's id) function is called below, the fleet's ships
                    // will have already been deleted, so there won't be an exception about attempting to
                    // delete a non-empty fleet.
                    for(std::set<int>::iterator ship_it = ships.begin(); ship_it != ships.end(); ++ship_it) {
                        int ship_id = *ship_it;
                        fleet->RemoveShip(ship_id);
                        Delete(ship_id);
                        destroyedObjects.insert(ship_id);
                    }
                }

                Delete(id);
            } // endif does UniverseObject still exist
        } // endif has the object already been deleted
    } // end for loop over list of object IDs to delete
}


Fleet Movepath Colours

To colour fleets' movepaths with their empire colour: In MapWnd.cpp, in MapWnd::RenderFleetMovementLines(), immediately before this line:

glBegin(GL_LINES);

Add these lines:

// get color of fleet's owner empire's color
GG::Clr emp_clr;
const std::set<int> owners = it->first->Owners();
if (owners.size() == 1) {
    emp_clr = HumanClientApp::Empires().Lookup(*owners.begin())->Color();
}
else {
    emp_clr = GG::Clr(255, 255, 255, 255);
}
	
glColor4d(emp_clr.r / 255.0f, emp_clr.g / 255.0f, emp_clr.b / 255.0f, emp_clr.a / 255.0f);

I suspect there's a better way to convert a GG:Clr to OpenGL's components in the range [0.0, 1.0], but I don't know it. In any case, this works.