/// <summary> /// Prepare a login to the given region. This involves both telling the region to expect a connection /// and appropriately customising the response to the user. /// </summary> /// <param name="regionInfo"></param> /// <param name="user"></param> /// <param name="response"></param> /// <returns>true if the region was successfully contacted, false otherwise</returns> private bool PrepareLoginToRegion(RegionProfileData regionInfo, UserProfileData user, LoginResponse response, string clientVersion) { string regionName = regionInfo.regionName; bool success = false; try { lock (_LastRegionFailure) { if (_LastRegionFailure.ContainsKey(regionName)) { // region failed previously RegionLoginFailure failure = _LastRegionFailure[regionName]; if (failure.IsExpired()) { // failure has expired, retry this region again _LastRegionFailure.Remove(regionName); // m_log.WarnFormat("[LOGIN]: Region '{0}' was previously down, retrying.", regionName); } else { if (failure.IsFailed()) { // m_log.WarnFormat("[LOGIN]: Region '{0}' was recently down, skipping.", regionName); return(false); // within 5 minutes, don't repeat attempt } // m_log.WarnFormat("[LOGIN]: Region '{0}' was recently down but under threshold, retrying.", regionName); } } } response.SimAddress = regionInfo.OutsideIpOrResolvedHostname; response.SimPort = regionInfo.serverPort; response.RegionX = regionInfo.regionLocX; response.RegionY = regionInfo.regionLocY; string capsPath = CapsUtil.GetRandomCapsObjectPath(); response.SeedCapability = CapsUtil.GetFullCapsSeedURL(regionInfo.httpServerURI, capsPath); // Notify the target of an incoming user m_log.InfoFormat( "[LOGIN]: Telling {0} @ {1},{2} ({3}) to prepare for client connection", regionInfo.regionName, response.RegionX, response.RegionY, response.SeedCapability); // Update agent with target sim user.CurrentAgent.Region = regionInfo.UUID; user.CurrentAgent.Handle = regionInfo.regionHandle; // Prepare notification Hashtable loginParams = new Hashtable(); loginParams["session_id"] = user.CurrentAgent.SessionID.ToString(); loginParams["secure_session_id"] = user.CurrentAgent.SecureSessionID.ToString(); loginParams["firstname"] = user.FirstName; loginParams["lastname"] = user.SurName; loginParams["agent_id"] = user.ID.ToString(); loginParams["circuit_code"] = (Int32)Convert.ToUInt32(response.CircuitCode); loginParams["startpos_x"] = user.CurrentAgent.Position.X.ToString(); loginParams["startpos_y"] = user.CurrentAgent.Position.Y.ToString(); loginParams["startpos_z"] = user.CurrentAgent.Position.Z.ToString(); loginParams["regionhandle"] = user.CurrentAgent.Handle.ToString(); loginParams["caps_path"] = capsPath; loginParams["client_version"] = clientVersion; // Get appearance AvatarAppearance appearance = m_userManager.GetUserAppearance(user.ID); if (appearance != null) { loginParams["appearance"] = appearance.ToHashTable(); m_log.DebugFormat("[LOGIN]: Found appearance version {0} for {1} {2}", appearance.Serial, user.FirstName, user.SurName); } else { m_log.DebugFormat("[LOGIN]: Appearance not for {0} {1}. Creating default.", user.FirstName, user.SurName); appearance = new AvatarAppearance(user.ID); } // Tell the client the COF version so it can use cached appearance if it matches. response.CofVersion = appearance.Serial.ToString(); loginParams["cof_version"] = response.CofVersion; ArrayList SendParams = new ArrayList(); SendParams.Add(loginParams); SendParams.Add(m_config.GridSendKey); // Send const string METHOD_NAME = "expect_user"; XmlRpcRequest GridReq = new XmlRpcRequest(METHOD_NAME, SendParams); XmlRpcResponse GridResp = GridReq.Send(Util.XmlRpcRequestURI(regionInfo.httpServerURI, METHOD_NAME), 6000); if (!GridResp.IsFault) { bool responseSuccess = true; if (GridResp.Value != null) { Hashtable resp = (Hashtable)GridResp.Value; if (resp.ContainsKey("success")) { if ((string)resp["success"] == "FALSE") { responseSuccess = false; } } if (!responseSuccess) { if (resp.ContainsKey("reason")) { response.ErrorMessage = resp["reason"].ToString(); } } } if (responseSuccess) { handlerUserLoggedInAtLocation = OnUserLoggedInAtLocation; if (handlerUserLoggedInAtLocation != null) { handlerUserLoggedInAtLocation(user.ID, user.CurrentAgent.SessionID, user.CurrentAgent.Region, user.CurrentAgent.Handle, user.CurrentAgent.Position.X, user.CurrentAgent.Position.Y, user.CurrentAgent.Position.Z, user.FirstName, user.SurName); } success = true; } else { m_log.ErrorFormat("[LOGIN]: Region responded that it is not available to receive clients"); } } else { m_log.ErrorFormat("[LOGIN]: XmlRpc request to region failed with message {0}, code {1} ", GridResp.FaultString, GridResp.FaultCode); } } catch (Exception e) { m_log.ErrorFormat("[LOGIN]: Region not available for login, {0}", e); } lock (_LastRegionFailure) { if (_LastRegionFailure.ContainsKey(regionName)) { RegionLoginFailure failure = _LastRegionFailure[regionName]; if (success) { // Success, so if we've been storing this as a failed region, remove that from the failed list. m_log.WarnFormat("[LOGIN]: Region '{0}' recently down, is available again.", regionName); _LastRegionFailure.Remove(regionName); } else { // Region not available, update cache with incremented count. failure.AddFailure(); // m_log.WarnFormat("[LOGIN]: Region '{0}' is still down ({1}).", regionName, failure.Count); } } else { if (!success) { // Region not available, cache that temporarily. m_log.WarnFormat("[LOGIN]: Region '{0}' is down, marking.", regionName); _LastRegionFailure[regionName] = new RegionLoginFailure(); } } } return(success); }