public RegionInfo(SimpleRegionInfo ConvertFrom) { m_regionLocX = ConvertFrom.RegionLocX; m_regionLocY = ConvertFrom.RegionLocY; m_internalEndPoint = ConvertFrom.InternalEndPoint; m_externalHostName = ConvertFrom.ExternalHostName; m_remotingPort = ConvertFrom.RemotingPort; m_allow_alternate_ports = ConvertFrom.m_allow_alternate_ports; RemotingAddress = ConvertFrom.RemotingAddress; RegionID = UUID.Zero; }
public async Task<bool> SendCloseAgentAsync(SimpleRegionInfo regionInfo, UUID id) { return await m_regionClient.DoCloseAgentCallAsync(regionInfo, id); }
public System.Threading.Tasks.Task<bool> SendCloseAgentAsync(SimpleRegionInfo regionInfo, UUID id) { throw new NotImplementedException(); }
public ChildAgentUpdate2Response SendChildAgentUpdate2(SimpleRegionInfo regionInfo, AgentData data) { foreach (Scene s in m_sceneList) { if (s.RegionInfo.RegionHandle == regionInfo.RegionHandle) { return s.IncomingChildAgentDataUpdate2(data); } } return ChildAgentUpdate2Response.Error; }
private async Task<bool> WaitForScenePresenceEstablished(SimpleRegionInfo region) { //this is the HTTP timeout, however the actual wait timeout on the receiving side is 10 seconds //so that timeout should be triggered first const int HTTP_SP_WAIT_TIMEOUT = 15000; var req = (HttpWebRequest)HttpWebRequest.Create(region.InsecurePublicHTTPServerURI + String.Format("/agent2/{0}/{1}", _sp.UUID, region.RegionHandle)); req.Headers["authorization"] = Util.GenerateHttpAuthorization(_scene.GridSendKey); req.Timeout = HTTP_SP_WAIT_TIMEOUT; req.ReadWriteTimeout = HTTP_SP_WAIT_TIMEOUT; req.Method = "GET"; try { using (WebResponse response = await req.GetResponseAsync(HTTP_SP_WAIT_TIMEOUT)) { //we do nothing besides dispose on success. a 200 response means all is well } return true; } catch (Exception e) { _log.ErrorFormat("[REMOTEPRESENCE]: Waiting for viewer connection from {0} to {1} failed: {2}", _sp.Name, region.RegionHandle, e); return false; } }
/// <summary> /// Attempts to establish a presence on the given region /// </summary> /// <param name="region">The region we want to establish a child presence on</param> /// <param name="forceReestablish">Whether to force a reestablishment even if we already think we have a remote presence</param> /// <param name="isFarPresence">Is this presence intentionally far away? Eg. The beginning of a remote teleport</param> /// <returns></returns> private async Task<Tuple<EstablishPresenceResult, string>> EstablishPresenceOnRegion(SimpleRegionInfo region, bool forceReestablish, bool isFarPresence) { Task<Tuple<EstablishPresenceResult, string>> establishTask = null; bool presenceExisted = false; //check if we already have or are waiting on an establish TryGetRemotePresenceLocked(region.RegionHandle, (AvatarRemotePresence presence) => { if (presence != null && !forceReestablish) { //we have a presence //if it is established just return if (presence.State == RemotePresenceState.Established) { presenceExisted = true; } else { //if not, we can await the existing callback establishTask = presence.EstablishTask; } } else { //we have no presence and we're not waiting for a callback //begin an async establish and await a callback presence = new AvatarRemotePresence { PresenceInfo = new RemotePresenceInfo { RegionInfo = region, CapsPath = CapsUtil.GetRandomCapsObjectPath() }, IsFarPresence = isFarPresence, State = RemotePresenceState.Establishing }; if (_remotePresences.ContainsKey(region.RegionHandle)) _remotePresences.Remove(region.RegionHandle); _remotePresences.Add(region.RegionHandle, presence); establishTask = DoEstablishPresenceOnRegion(region, presence); presence.EstablishTask = establishTask; } }); //nothing to do, we're already established if (presenceExisted) return Tuple.Create(EstablishPresenceResult.Success, String.Empty); return await establishTask; }
private async Task HandleNeighborUp(SimpleRegionInfo neighbor) { // on any neighbor change, we need to recalculate all neighbors because // visibility rules may have resulted in more than one new neighbor. await CalculateAndResyncNeighbors((uint)_sp.DrawDistance, 0); }
/// <summary> /// Drops the given presence taking the operation semaphore /// </summary> /// <param name="neighbor">The presence to drop</param> /// <param name="onlyIfFar">Only drop if the presence is a far presence</param> public async Task DropRemotePresenceLocked(SimpleRegionInfo neighbor, bool onlyIfFar) { try { await _operationSemaphore.WaitAsync(); await DropRemotePresence(neighbor, onlyIfFar); } finally { _operationSemaphore.Release(); } }
public void Reset(SimpleRegionInfo destinationRegion) { m_scene.SendKillObject(m_localId, destinationRegion); // Put the child agent back at the center AbsolutePosition = new Vector3(128, 128, 70); ResetAnimations(); }
internal void TriggerAvatarLeavingRegion(ScenePresence agent, SimpleRegionInfo newRegion) { AvatarLeavingRegion handlerAvatarLeavingRegion = OnAvatarLeavingRegion; if (handlerAvatarLeavingRegion != null) handlerAvatarLeavingRegion(agent, newRegion); }
private static List<SimpleRegionInfo> ExtractRegionInfoFromMapBlockQuery(uint x, uint y, Hashtable respData) { List<SimpleRegionInfo> neighbours = new List<SimpleRegionInfo>(); foreach (ArrayList neighboursList in respData.Values) { foreach (Hashtable neighbourData in neighboursList) { uint regX = Convert.ToUInt32(neighbourData["x"]); uint regY = Convert.ToUInt32(neighbourData["y"]); if ((x != regX) || (y != regY)) { string simIp = (string)neighbourData["sim_ip"]; uint port = Convert.ToUInt32(neighbourData["sim_port"]); SimpleRegionInfo sri = new SimpleRegionInfo(regX, regY, simIp, port); sri.RegionID = new UUID((string)neighbourData["uuid"]); sri.RemotingPort = Convert.ToUInt32(neighbourData["remoting_port"]); if (neighbourData.ContainsKey("http_port")) { sri.HttpPort = Convert.ToUInt32(neighbourData["http_port"]); } if (neighbourData.ContainsKey("outside_ip")) { sri.OutsideIP = (string)neighbourData["outside_ip"]; } neighbours.Add(sri); } } } return neighbours; }
private void EventManager_OnAvatarLeavingRegion(ScenePresence presence, SimpleRegionInfo newRegion) { //Add them to the cache LeavingRegionInfo info = new LeavingRegionInfo() { RegionServerURI = newRegion.InsecurePublicHTTPServerURI, SessionID = presence.ControllingClient.SessionId }; _avatarRegionCache.AddOrUpdate(presence.UUID, info, CACHE_EXPIRATION_TIME); }
public bool HasEstablishedConnection(SimpleRegionInfo region) { AvatarRemotePresence pres; lock (_remotePresences) { if (_remotePresences.TryGetValue(region.RegionHandle, out pres)) { return (pres.State == RemotePresenceState.Established); } } return false; }
/// <summary> /// Returns a list of neighbors we are aware of that are within the given client draw distance from any /// of our edges /// </summary> /// <param name="drawDistance">DD in meters</param> /// <returns>List of known neighbors</returns> public List<SimpleRegionInfo> GetKnownNeighborsWithinClientDD(uint drawDistance) { drawDistance = Math.Max(drawDistance, 64); drawDistance = Math.Min(drawDistance, MAX_DRAW_DISTANCE); uint xmin, xmax, ymin, ymax; var regionInfo = _scene.RegionInfo; Util.GetDrawDistanceBasedRegionRectangle(drawDistance, regionInfo.RegionLocX, regionInfo.RegionLocY, out xmin, out xmax, out ymin, out ymax); uint gridsize = xmax - xmin + 1; uint center = (xmax - xmin) / 2; List<SimpleRegionInfo> neighbors = new List<SimpleRegionInfo>(); // visibleNeighbors[] and inspected[] arrays use 0-based coordinates. SimpleRegionInfo[,] visibleNeighbors = new SimpleRegionInfo[gridsize, gridsize]; lock (_knownNeighbors) { foreach (KnownNeighborRegion neighbor in _knownNeighbors.Values) { if (Util.IsWithinDDRectangle(neighbor.RegionInfo.RegionLocX, neighbor.RegionInfo.RegionLocY, xmin, xmax, ymin, ymax)) { //region within bounds neighbors.Add(neighbor.RegionInfo); } } // Apply per-presence region visibility filter bool[,] inspected = new bool[gridsize, gridsize]; // context for recursive call // The starting/center point in the visibility grid is always included. visibleNeighbors[center, center] = new SimpleRegionInfo(regionInfo); inspected[center, center] = true; // Recursively path-find all visible neighbors. AddVisibleNeighbors(visibleNeighbors, inspected, regionInfo.RegionLocX, regionInfo.RegionLocY, xmin, xmax, ymin, ymax); } // Now replace the full list of neighbors with the regions in the filtered visible array. neighbors.Clear(); foreach (var region in visibleNeighbors) if ((region != null) && (region.RegionHandle != regionInfo.RegionHandle)) // got one and it's not this one neighbors.Add(region); return neighbors; }
// Called with _knownNeighbors already locked. // Returns true if anything added to visibleNeighbors private void AddVisibleNeighbors(SimpleRegionInfo[,] visibleNeighbors, bool[,] inspected, uint x, uint y, uint xmin, uint xmax, uint ymin, uint ymax) { // Visibility rules are view must pass through at least one horizontal or vertical neighbor. AddVisibleRegion(visibleNeighbors, inspected, x, y - 1, xmin, xmax, ymin, ymax); AddVisibleRegion(visibleNeighbors, inspected, x, y + 1, xmin, xmax, ymin, ymax); AddVisibleRegion(visibleNeighbors, inspected, x - 1, y, xmin, xmax, ymin, ymax); AddVisibleRegion(visibleNeighbors, inspected, x + 1, y, xmin, xmax, ymin, ymax); }
/// <summary> /// Called by the surrounding region manager when there is a region state change /// </summary> /// <param name="neighbor"></param> /// <param name="changeType"></param> void SurroundingRegions_OnNeighborStateChange(SimpleRegionInfo neighbor, NeighborStateChangeType changeType) { switch (changeType) { case NeighborStateChangeType.NeighborUp: this.HandleNeighborUp(neighbor); break; case NeighborStateChangeType.NeighborDown: this.HandleNeighborDown(neighbor); break; } }
/// <summary> /// Async component for informing client of which neighbours exist /// </summary> /// <remarks> /// This needs to run asynchronously, as a network timeout may block the thread for a long while /// </remarks> /// <param name="remoteClient"></param> /// <param name="a"></param> /// <param name="regionHandle"></param> /// <param name="endPoint"></param> private void InformClientOfNeighbourAsync(ScenePresence avatar, AgentCircuitData a, SimpleRegionInfo reg, IPEndPoint endPoint, bool newAgent) { // Let's wait just a little to give time to originating regions to catch up with closing child agents // after a cross here Thread.Sleep(500); uint x, y; Utils.LongToUInts(reg.RegionHandle, out x, out y); x = x / Constants.RegionSize; y = y / Constants.RegionSize; m_log.Info("[INTERGRID]: Starting to inform client about neighbour " + x + ", " + y + "(" + endPoint.ToString() + ")"); string capsPath = "http://" + reg.ExternalHostName + ":" + reg.HttpPort + "/CAPS/" + a.CapsPath + "0000/"; string reason = String.Empty; //bool regionAccepted = m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, a); bool regionAccepted = m_interregionCommsOut.SendCreateChildAgent(reg.RegionHandle, a, out reason); if (regionAccepted && newAgent) { IEventQueue eq = avatar.Scene.RequestModuleInterface<IEventQueue>(); if (eq != null) { #region IP Translation for NAT IClientIPEndpoint ipepClient; if (avatar.ClientView.TryGet(out ipepClient)) { endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address); } #endregion eq.EnableSimulator(reg.RegionHandle, endPoint, avatar.UUID); eq.EstablishAgentCommunication(avatar.UUID, endPoint, capsPath); m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1} in region {2}", capsPath, avatar.UUID, avatar.Scene.RegionInfo.RegionName); } else { avatar.ControllingClient.InformClientOfNeighbour(reg.RegionHandle, endPoint); // TODO: make Event Queue disablable! } m_log.Info("[INTERGRID]: Completed inform client about neighbour " + endPoint.ToString()); } }
/// <summary> /// Drops the given presence /// </summary> /// <param name="neighbor">The presence to drop</param> /// <param name="onlyIfFar">Only drop if the presence is a far presence</param> private async Task DropRemotePresence(SimpleRegionInfo neighbor, bool onlyIfFar) { AvatarRemotePresence foundPresence = null; TryGetRemotePresenceLocked(neighbor.RegionHandle, (AvatarRemotePresence presence) => { foundPresence = presence; if (presence != null) { if (onlyIfFar && !presence.IsFarPresence) { return; } //You can not send a disablesimulator for the remote region //the viewer processes all disablesimulator messages coming from this //region as a disablesimulator message FOR this region //therefore, we remove the neighbor from our known regions list //and ask the sim on the other end to tear down the connection _remotePresences.Remove(neighbor.RegionHandle); } }); if (foundPresence != null) { //send a close to the neighbor await _scene.InterregionComms.SendCloseAgentAsync(neighbor, _sp.UUID); } }
/// <summary> /// This informs a single neighboring region about agent "avatar". /// Calls an asynchronous method to do so.. so it doesn't lag the sim. /// </summary> public void InformNeighborChildAgent(ScenePresence avatar, SimpleRegionInfo region) { AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); agent.BaseFolder = UUID.Zero; agent.InventoryFolder = UUID.Zero; agent.startpos = new Vector3(128, 128, 70); agent.child = true; InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; d.BeginInvoke(avatar, agent, region, region.ExternalEndPoint, true, InformClientOfNeighbourCompleted, d); }
/// <summary> /// Attempts to establish a presence on the given region. Does this while waiting for other major tasks to complete /// </summary> /// <param name="region">The region we want to establish a child presence on</param> /// <param name="forceReestablish">Whether to force a reestablishment even if we already think we have a remote presence</param> /// <param name="isFarPresence">Is this presence intentionally far away? Eg. The beginning of a remote teleport</param> /// <returns></returns> public async Task<Tuple<EstablishPresenceResult, string>> EstablishPresenceOnRegionLocked(SimpleRegionInfo region, bool forceReestablish, bool isFarPresence) { try { await _operationSemaphore.WaitAsync(); return await this.EstablishPresenceOnRegion(region, forceReestablish, isFarPresence); } finally { _operationSemaphore.Release(); } }
public async Task<Tuple<EstablishPresenceResult, string>> EstablishChildConnectionToRegionAsync(ScenePresence avatar, AgentCircuitData a, SimpleRegionInfo reg) { string capsPath = CapsUtil.GetFullCapsSeedURL(reg.InsecurePublicHTTPServerURI, a.CapsPath); Tuple<bool, string> createResult = await m_interregionCommsOut.SendCreateRemoteChildAgentAsync(reg, a); if (createResult.Item1) { IPEndPoint endPoint = reg.ExternalEndPoint; IEventQueue eq = avatar.Scene.RequestModuleInterface<IEventQueue>(); if (!eq.EnableSimulator(reg.RegionHandle, endPoint, avatar.UUID)) { m_log.ErrorFormat("[INTERGRID]: Could not enqueue eq.EnableSimulator for {0}", avatar.Name); return Tuple.Create(EstablishPresenceResult.ClientSignallingFailed, "Could not enqueue EnableSimulator"); } if (! eq.EstablishAgentCommunication(avatar.UUID, endPoint, capsPath)) { m_log.ErrorFormat("[INTERGRID]: Could not enqueue eq.EstablishAgentCommunication for {0}", avatar.Name); return Tuple.Create(EstablishPresenceResult.ClientSignallingFailed, "Could not enqueue EstablishAgentCommunication"); } return Tuple.Create(EstablishPresenceResult.Success, String.Empty); } else { return Tuple.Create(EstablishPresenceResult.ErrorInformingRegion, createResult.Item2); } }
private async Task<Tuple<EstablishPresenceResult, string>> DoEstablishPresenceOnRegion(SimpleRegionInfo region, AvatarRemotePresence initPresence) { Tuple<EstablishPresenceResult, string> establishResult; try { establishResult = await this.LaunchNewEstablishChildTask(initPresence, region); } catch (Exception e) { establishResult = new Tuple<EstablishPresenceResult, string>(EstablishPresenceResult.ErrorInformingRegion, e.Message); } bool failure = false; TryGetRemotePresenceLocked(region.RegionHandle, (AvatarRemotePresence presence) => { //success, change the status of the task if (presence != null) { if (establishResult.Item1 == EstablishPresenceResult.Success) { presence.State = RemotePresenceState.ViewerWait; } else { //failure contacting other region _remotePresences.Remove(region.RegionHandle); failure = true; } } else { failure = true; //hmm, someone stole this presence from us _log.ErrorFormat("[REMOTEPRESENCE]: Unable to update child presence established to {0} for {1}. Child presence missing.", establishResult, _sp.Name); establishResult = Tuple.Create(EstablishPresenceResult.ConnectionAborted, "Connection was aborted"); } }); if (failure) { return establishResult; } //now we need to call out to the remote region to wait for the SP to be set up bool waitSuccess = await WaitForScenePresenceEstablished(region); Tuple<EstablishPresenceResult, string> result = null; TryGetRemotePresenceLocked(region.RegionHandle, (AvatarRemotePresence presence) => { //success, change the status of the task if (presence != null) { if (waitSuccess) { presence.State = RemotePresenceState.Established; result = Tuple.Create(EstablishPresenceResult.Success, String.Empty); } else { //failure waiting for SP _remotePresences.Remove(region.RegionHandle); result = Tuple.Create(EstablishPresenceResult.ClientWaitTimeout, "Destination region never received a connection from the viewer"); } } else { //hmm, someone stole this presence from us _log.ErrorFormat("[REMOTEPRESENCE]: Unable to update child presence established to {0} for {1}. Child presence missing.", establishResult, _sp.Name); result = Tuple.Create(EstablishPresenceResult.ConnectionAborted, "Connection was aborted"); } }); return result; }
/// <summary> /// Moves the agent outside the region bounds /// Tells neighbor region that we're crossing to it /// If the neighbor accepts, remove the agent's viewable avatar from this scene /// set them to a child agent. /// </summary> protected void CrossToNewRegion(ulong neighborHandle, SimpleRegionInfo neighborInfo, Vector3 positionInNewRegion) { lock (m_posInfo) // SetInTransit and AbsolutePosition will grab this { if (PhysicsActor == null) { //when a user is crossing on a border due to being attached to a moving object //they will have no physics actor. This is our signal to let the object //do the crossing for us return; } } m_scene.CrossWalkingOrFlyingAgentToNewRegion(this, neighborHandle, neighborInfo, positionInNewRegion); }
private Task<Tuple<EstablishPresenceResult, string>> LaunchNewEstablishChildTask(AvatarRemotePresence presence, SimpleRegionInfo region) { AgentCircuitData agent = _sp.ControllingClient.RequestClientInfo(); agent.BaseFolder = UUID.Zero; agent.InventoryFolder = UUID.Zero; agent.startpos = Scene.DEFAULT_CHILD_AGENT_POS; agent.child = true; agent.CapsPath = presence.PresenceInfo.CapsPath; return _scene.SceneGridService.EstablishChildConnectionToRegionAsync(_sp, agent, region); }
protected int HaveNeighbor(Cardinals car, ref float[] fix, ref ulong neighborHandle, ref SimpleRegionInfo neighborInfo) { uint neighbourx = m_regionInfo.RegionLocX; uint neighboury = m_regionInfo.RegionLocY; float fixX = 0; float fixY = 0; int dir = (int)car; if (dir > 1 && dir < 5) //Heading East { neighbourx++; fixX -= (float)Constants.RegionSize; } else if (dir > 5) // Heading West { neighbourx--; fixX += (float)Constants.OUTSIDE_REGION_POSITIVE_EDGE; } if (dir < 3 || dir == 8) // Heading North { neighboury++; fixY -= (float)Constants.RegionSize; } else if (dir > 3 && dir < 7) // Heading Sout { neighboury--; fixY += (float)Constants.OUTSIDE_REGION_POSITIVE_EDGE; } neighborHandle = Util.RegionHandleFromLocation(neighbourx, neighboury); neighborInfo = m_scene.RequestNeighbouringRegionInfo(neighborHandle); if (neighborInfo == null) { fix[0] = (float)(int)(m_regionInfo.RegionLocX - neighbourx); fix[1] = (float)(int)(m_regionInfo.RegionLocY - neighboury); return dir * (-1); } else { fix[0] = fixX; fix[1] = fixY; return dir; } }
public System.Threading.Tasks.Task<Tuple<bool, string>> SendCreateRemoteChildAgentAsync(SimpleRegionInfo regionInfo, AgentCircuitData aCircuit) { throw new NotImplementedException(); }
/// <summary> /// Moves the agent outside the region bounds /// Tells neighbor region that we're crossing to it /// If the neighbor accepts, remove the agent's viewable avatar from this scene /// set them to a child agent. /// </summary> protected void CrossToNewRegion(ulong neighborHandle, SimpleRegionInfo neighborInfo, Vector3 positionInNewRegion) { ulong started = Util.GetLongTickCount(); lock (m_posInfo) // SetInTransit and AbsolutePosition will grab this { if (PhysicsActor == null) { //when a user is crossing on a border due to being attached to a moving object //they will have no physics actor. This is our signal to let the object //do the crossing for us return; } } m_scene.CrossWalkingOrFlyingAgentToNewRegion(this, neighborHandle, neighborInfo, positionInNewRegion); m_log.InfoFormat("[SCENE]: Crossing for avatar took {0} ms for {1}.", Util.GetLongTickCount()-started, this.Name); }
public ChildAgentUpdate2Response SendChildAgentUpdate2(SimpleRegionInfo regionInfo, AgentData data) { int childAgentUpdateStart = Environment.TickCount; try { // Try local first if (m_localBackend.SendChildAgentUpdate2(regionInfo, data) == ChildAgentUpdate2Response.Ok) return ChildAgentUpdate2Response.Ok; // else do the remote thing if (!m_localBackend.IsLocalRegion(regionInfo.RegionHandle)) { RegionClient.AgentUpdate2Ret ret = m_regionClient.DoChildAgentUpdateCall2(regionInfo, data); switch (ret) { case RegionClient.AgentUpdate2Ret.Ok: return ChildAgentUpdate2Response.Ok; case RegionClient.AgentUpdate2Ret.Error: return ChildAgentUpdate2Response.Error; case RegionClient.AgentUpdate2Ret.NotFound: return ChildAgentUpdate2Response.MethodNotAvailalble; case RegionClient.AgentUpdate2Ret.AccessDenied: return ChildAgentUpdate2Response.AccessDenied; } } return ChildAgentUpdate2Response.Error; } finally { m_log.DebugFormat("[REST COMM] SendChildAgentUpdateCall2: {0} ms", Environment.TickCount - childAgentUpdateStart); } }
public void Reset(SimpleRegionInfo destinationRegion) { m_scene.SendKillObject(m_localId, destinationRegion); ResetAnimations(); }
public async Task<Tuple<bool, string>> SendCreateRemoteChildAgentAsync(SimpleRegionInfo regionInfo, AgentCircuitData aCircuit) { if (regionInfo == null) throw new ArgumentNullException("regionInfo cannot be null"); return await m_regionClient.DoCreateChildAgentCallAsync(regionInfo, aCircuit); }
// Called with _knownNeighbors already locked. // Returns true if anything added to visibleNeighbors private void AddVisibleRegion(SimpleRegionInfo[,] visibleNeighbors, bool[,] inspected, uint x, uint y, uint xmin, uint xmax, uint ymin, uint ymax) { if ((x < xmin) || (x > xmax) || (y < ymin) || (y > ymax)) return; // off the grid, nothing to do // visibleNeighbors[] and inspected[] arrays use 0-based coordinates. uint xmap = x - xmin; uint ymap = y - ymin; if (inspected[xmap, ymap]) return; // already did this one inspected[xmap, ymap] = true; SimpleRegionInfo neighbor = FindKnownNeighbor(x, y); if (neighbor == null) return; // region not present visibleNeighbors[xmap, ymap] = neighbor; AddVisibleNeighbors(visibleNeighbors, inspected, x, y, xmin, xmax, ymin, ymax); }