private void PlaceElementsInZone(Collections.Hashtable elements) { Collections.DictionaryEntry currentEntry; ArrayList currentList; SubNodeElement currentStartElement; SubNodeElement currentElement; IEnumerator entryEnumerator = elements.GetEntryEnumerator(); while (entryEnumerator.MoveNext()) { currentEntry = (Collections.DictionaryEntry)entryEnumerator.Current; currentStartElement = (SubNodeElement)currentEntry.key; currentList = (ArrayList)currentEntry.value; lock (currentStartElement) { for (int i = 0; i < currentList.Count; i++) { currentElement = (SubNodeElement)currentList[i]; currentStartElement.PushBack(currentElement); } } } }
private void PlaceElementsInOtherZones(Collections.Hashtable elements) { Collections.DictionaryEntry currentEntry; int currentType; ArrayList currentList; Zone currentZone; SubNodeElement currentElement; IEnumerator entryEnumerator = elements.GetEntryEnumerator(); while (entryEnumerator.MoveNext()) { currentEntry = (Collections.DictionaryEntry)entryEnumerator.Current; currentType = (int)currentEntry.key; currentList = (ArrayList)currentEntry.value; for (int i = 0; i < currentList.Count; i++) { currentElement = (SubNodeElement)currentList[i]; currentZone = ZoneRegion.GetZone(currentElement.data.X, currentElement.data.Y); currentZone?.ObjectEnterZone((eGameObjectType)currentType, currentElement); } } }
private void UnsafeAddToListWithDistanceCheck( SubNodeElement startElement, int x, int y, int z, uint sqRadius, int typeIndex, int subZoneIndex, ArrayList partialList, Collections.Hashtable inZoneElements, Collections.Hashtable outOfZoneElements, bool ignoreZ) { // => check all distances for all objects in the subzone SubNodeElement currentElement = startElement.next; SubNodeElement elementToRemove; GameObject currentObject; do { currentObject = currentElement.data; if (ShouldElementMove(currentElement, typeIndex, subZoneIndex, inZoneElements, outOfZoneElements)) { elementToRemove = currentElement; currentElement = currentElement.next; elementToRemove.Remove(); if (log.IsDebugEnabled) { log.Debug($"Zone{ID}: {(currentObject != null ? $"object {currentObject.ObjectID}" : "ghost object")} removed for subzone"); } } else { if (CheckSquareDistance(x, y, z, currentObject.X, currentObject.Y, currentObject.Z, sqRadius, ignoreZ) && !partialList.Contains(currentObject)) { // the current object exists, is Active and still in the current subzone // moreover it is in the right range and not yet in the result set // => add it partialList.Add(currentObject); } currentElement = currentElement.next; } } while (currentElement != startElement); }
private bool ShouldElementMove(SubNodeElement currentElement, int typeIndex, int subZoneIndex, Collections.Hashtable inZoneElements, Collections.Hashtable outOfZoneElements) { if (!m_initialized) { InitializeZone(); } bool removeElement = true; GameObject currentObject = currentElement.data; if (currentObject != null && (int)currentObject.ObjectState == (int)GameObject.eObjectState.Active && currentObject.CurrentRegion == ZoneRegion) { // the current object exists, is Active and still in the Region where this Zone is located int currentElementSubzoneIndex = GetSubZoneIndex(currentObject.X, currentObject.Y); if (currentElementSubzoneIndex == -1) { // the object has moved in another Zone in the same Region ArrayList movedElements = (ArrayList)outOfZoneElements[typeIndex]; if (movedElements == null) { movedElements = new ArrayList(); outOfZoneElements[typeIndex] = movedElements; } movedElements.Add(currentElement); Interlocked.Decrement(ref m_objectCount); } else { // the object is still inside this Zone if (removeElement = currentElementSubzoneIndex != subZoneIndex) { // it has changed of subzone SubNodeElement newSubZoneStartElement = m_subZoneElements[currentElementSubzoneIndex][typeIndex]; ArrayList movedElements = (ArrayList)inZoneElements[newSubZoneStartElement]; if (movedElements == null) { movedElements = new ArrayList(); inZoneElements[newSubZoneStartElement] = movedElements; } // make it available for relocation movedElements.Add(currentElement); } } } else { // ghost object // => remove it Interlocked.Decrement(ref m_objectCount); } return(removeElement); }
internal void Relocate(object state) { if (!m_initialized) { return; } if (m_objectCount > 0) { SubNodeElement startElement; SubNodeElement currentElement; SubNodeElement elementToRemove; Collections.Hashtable outOfZoneElements = new Collections.Hashtable(); Collections.Hashtable inZoneElements = new Collections.Hashtable(); for (int subZoneIndex = 0; subZoneIndex < m_subZoneElements.Length; subZoneIndex++) { for (int typeIndex = 0; typeIndex < m_subZoneElements[subZoneIndex].Length; typeIndex++) { if (Environment.TickCount > m_subZoneTimestamps[(subZoneIndex << 2) | typeIndex]) { // it is time to relocate some elements in this subzone // => perform needed relocations of elements startElement = m_subZoneElements[subZoneIndex][typeIndex]; lock (startElement) { if (startElement != startElement.next) { // there are some elements in the list currentElement = startElement.next; do { if (ShouldElementMove(currentElement, typeIndex, subZoneIndex, inZoneElements, outOfZoneElements)) { elementToRemove = currentElement; currentElement = currentElement.next; elementToRemove.Remove(); if (log.IsDebugEnabled) { log.Debug($"Zone{ID} object {elementToRemove.data.ObjectID} removed for subzone"); } } else { currentElement = currentElement.next; } } while (currentElement != startElement); UnsafeUpdateSubZoneTimestamp(subZoneIndex, typeIndex); } } } } } if (inZoneElements.Count > 0) { PlaceElementsInZone(inZoneElements); if (log.IsDebugEnabled) { log.Debug($"Zone{ID} {inZoneElements.Count} objects moved inside zone"); } } if (outOfZoneElements.Count > 0) { PlaceElementsInOtherZones(outOfZoneElements); if (log.IsDebugEnabled) { log.Debug($"Zone{ID} {outOfZoneElements.Count} objects moved outside zone"); } } } }
private void UnsafeAddToListWithoutDistanceCheck(SubNodeElement startElement, int typeIndex, int subZoneIndex, ArrayList partialList, Collections.Hashtable inZoneElements, Collections.Hashtable outOfZoneElements) { SubNodeElement currentElement = startElement.next; SubNodeElement elementToRemove; GameObject currentObject; do { currentObject = currentElement.data; if (ShouldElementMove(currentElement, typeIndex, subZoneIndex, inZoneElements, outOfZoneElements)) { elementToRemove = currentElement; currentElement = currentElement.next; elementToRemove.Remove(); if (log.IsDebugEnabled) { log.Debug($"Zone{ID}: {(currentObject != null ? $"object {currentObject.ObjectID}" : "ghost object")} removed for subzone"); } } else { // the current object exists, is Active and still in the current subzone // => add it if (!partialList.Contains(currentObject)) { partialList.Add(currentObject); } currentElement = currentElement.next; } } while (currentElement != startElement); }
/// <summary> /// Gets the lists of objects, located in the current Zone and of the given type, that are at most at a 'radius' distance from (x,y,z) /// The found objects are appended to the given 'partialList'. /// </summary> /// <param name="type">the type of objects to look for</param> /// <param name="x">the x coordinate of the observation position</param> /// <param name="y">the y coordinate of the observation position</param> /// <param name="z">the z coordinate of the observation position</param> /// <param name="radius">the radius to check against</param> /// <param name="partialList">an initial (eventually empty but initialized, i.e. never null !!) list of objects</param> /// <returns>partialList augmented with the new objects verigying both type and radius in the current Zone</returns> internal ArrayList GetObjectsInRadius(eGameObjectType type, int x, int y, int z, ushort radius, ArrayList partialList, bool ignoreZ) { if (!m_initialized) { InitializeZone(); } // initialise parameters uint sqRadius = radius * (uint)radius; int referenceSubzoneIndex = GetSubZoneIndex(x, y); int typeIndex = (int)type; int xInZone = x - XOffset; // x in zone coordinates int yInZone = y - YOffset; // y in zone coordinates int cellNbr = (radius >> SUBZONE_SHIFT) + 1; // radius in terms of subzone number int xInCell = xInZone >> SUBZONE_SHIFT; // xInZone in terms of subzone coord int yInCell = yInZone >> SUBZONE_SHIFT; // yInZone in terms of subzone coord int minColumn = xInCell - cellNbr; if (minColumn < 0) { minColumn = 0; } int maxColumn = xInCell + cellNbr; if (maxColumn > (SUBZONE_NBR_ON_ZONE_SIDE - 1)) { maxColumn = SUBZONE_NBR_ON_ZONE_SIDE - 1; } int minLine = yInCell - cellNbr; if (minLine < 0) { minLine = 0; } int maxLine = yInCell + cellNbr; if (maxLine > (SUBZONE_NBR_ON_ZONE_SIDE - 1)) { maxLine = SUBZONE_NBR_ON_ZONE_SIDE - 1; } Collections.Hashtable inZoneElements = new Collections.Hashtable(); Collections.Hashtable outOfZoneElements = new Collections.Hashtable(); for (int currentLine = minLine; currentLine <= maxLine; ++currentLine) { int currentSubZoneIndex; SubNodeElement startElement; for (int currentColumn = minColumn; currentColumn <= maxColumn; ++currentColumn) { currentSubZoneIndex = GetSubZoneOffset(currentLine, currentColumn); // get the right list of objects startElement = m_subZoneElements[currentSubZoneIndex][typeIndex]; if (startElement != startElement.next) { // allow dirty read here to enable a more efficient and fine grained locking later... // the current subzone contains some objects if (currentSubZoneIndex == referenceSubzoneIndex) { lock (startElement) { // we are in the subzone of the observation point // => check all distances for all objects in the subzone UnsafeAddToListWithDistanceCheck(startElement, x, y, z, sqRadius, typeIndex, currentSubZoneIndex, partialList, inZoneElements, outOfZoneElements, ignoreZ); UnsafeUpdateSubZoneTimestamp(currentSubZoneIndex, typeIndex); } } else { int xLeft = currentColumn << SUBZONE_SHIFT; int xRight = xLeft + SUBZONE_SIZE; int yTop = currentLine << SUBZONE_SHIFT; int yBottom = yTop + SUBZONE_SIZE; if (CheckMinDistance(xInZone, yInZone, xLeft, xRight, yTop, yBottom, sqRadius)) { // the minimum distance is smaller than radius if (CheckMaxDistance(xInZone, yInZone, xLeft, xRight, yTop, yBottom, sqRadius)) { // the current subzone is fully enclosed within the radius // => add all the objects of the current subzone lock (startElement) { UnsafeAddToListWithoutDistanceCheck(startElement, typeIndex, currentSubZoneIndex, partialList, inZoneElements, outOfZoneElements); UnsafeUpdateSubZoneTimestamp(currentSubZoneIndex, typeIndex); } } else { // the current subzone is partially enclosed within the radius // => only add the objects within the right area lock (startElement) { UnsafeAddToListWithDistanceCheck(startElement, x, y, z, sqRadius, typeIndex, currentSubZoneIndex, partialList, inZoneElements, outOfZoneElements, ignoreZ); UnsafeUpdateSubZoneTimestamp(currentSubZoneIndex, typeIndex); } } } } } } } // // perform needed relocations // if (inZoneElements.Count > 0) { PlaceElementsInZone(inZoneElements); if (log.IsDebugEnabled) { log.Debug($"Zone{ID} {inZoneElements.Count} objects moved inside zone"); } } if (outOfZoneElements.Count > 0) { PlaceElementsInOtherZones(outOfZoneElements); if (log.IsDebugEnabled) { log.Debug($"Zone{ID} {outOfZoneElements.Count} objects moved outside zone"); } } return(partialList); }