mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Grids: Modernize TypeContainer with variadic template
(cherry picked from commit 401502ea3c)
This commit is contained in:
@@ -68,7 +68,7 @@ void AreaTrigger::AddToWorld()
|
||||
if (m_zoneScript)
|
||||
m_zoneScript->OnAreaTriggerCreate(this);
|
||||
|
||||
GetMap()->GetObjectsStore().Insert<AreaTrigger>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<AreaTrigger>(this);
|
||||
if (_spawnId)
|
||||
GetMap()->GetAreaTriggerBySpawnIdStore().insert(std::make_pair(_spawnId, this));
|
||||
|
||||
@@ -98,7 +98,7 @@ void AreaTrigger::RemoveFromWorld()
|
||||
|
||||
if (IsStaticSpawn())
|
||||
Trinity::Containers::MultimapErasePair(GetMap()->GetAreaTriggerBySpawnIdStore(), _spawnId, this);
|
||||
GetMap()->GetObjectsStore().Remove<AreaTrigger>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<AreaTrigger>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ void Conversation::AddToWorld()
|
||||
///- Register the Conversation for guid lookup and for caster
|
||||
if (!IsInWorld())
|
||||
{
|
||||
GetMap()->GetObjectsStore().Insert<Conversation>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<Conversation>(this);
|
||||
WorldObject::AddToWorld();
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ void Conversation::RemoveFromWorld()
|
||||
_ai->OnRemove();
|
||||
|
||||
WorldObject::RemoveFromWorld();
|
||||
GetMap()->GetObjectsStore().Remove<Conversation>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<Conversation>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ void Corpse::AddToWorld()
|
||||
{
|
||||
///- Register the corpse for guid lookup
|
||||
if (!IsInWorld())
|
||||
GetMap()->GetObjectsStore().Insert<Corpse>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<Corpse>(this);
|
||||
|
||||
Object::AddToWorld();
|
||||
}
|
||||
@@ -57,7 +57,7 @@ void Corpse::RemoveFromWorld()
|
||||
{
|
||||
///- Remove the corpse from the accessor
|
||||
if (IsInWorld())
|
||||
GetMap()->GetObjectsStore().Remove<Corpse>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<Corpse>(this);
|
||||
|
||||
WorldObject::RemoveFromWorld();
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ void Creature::AddToWorld()
|
||||
///- Register the creature for guid lookup
|
||||
if (!IsInWorld())
|
||||
{
|
||||
GetMap()->GetObjectsStore().Insert<Creature>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<Creature>(this);
|
||||
if (m_spawnId)
|
||||
GetMap()->GetCreatureBySpawnIdStore().insert(std::make_pair(m_spawnId, this));
|
||||
|
||||
@@ -373,7 +373,7 @@ void Creature::RemoveFromWorld()
|
||||
|
||||
if (m_spawnId)
|
||||
Trinity::Containers::MultimapErasePair(GetMap()->GetCreatureBySpawnIdStore(), m_spawnId, this);
|
||||
GetMap()->GetObjectsStore().Remove<Creature>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<Creature>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ void DynamicObject::AddToWorld()
|
||||
///- Register the dynamicObject for guid lookup and for caster
|
||||
if (!IsInWorld())
|
||||
{
|
||||
GetMap()->GetObjectsStore().Insert<DynamicObject>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<DynamicObject>(this);
|
||||
WorldObject::AddToWorld();
|
||||
BindToCaster();
|
||||
}
|
||||
@@ -77,7 +77,7 @@ void DynamicObject::RemoveFromWorld()
|
||||
|
||||
UnbindFromCaster();
|
||||
WorldObject::RemoveFromWorld();
|
||||
GetMap()->GetObjectsStore().Remove<DynamicObject>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<DynamicObject>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -930,7 +930,7 @@ void GameObject::AddToWorld()
|
||||
if (m_zoneScript)
|
||||
m_zoneScript->OnGameObjectCreate(this);
|
||||
|
||||
GetMap()->GetObjectsStore().Insert<GameObject>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<GameObject>(this);
|
||||
if (m_spawnId)
|
||||
GetMap()->GetGameObjectBySpawnIdStore().insert(std::make_pair(m_spawnId, this));
|
||||
|
||||
@@ -970,7 +970,7 @@ void GameObject::RemoveFromWorld()
|
||||
|
||||
if (m_spawnId)
|
||||
Trinity::Containers::MultimapErasePair(GetMap()->GetGameObjectBySpawnIdStore(), m_spawnId, this);
|
||||
GetMap()->GetObjectsStore().Remove<GameObject>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<GameObject>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ void Pet::AddToWorld()
|
||||
if (!IsInWorld())
|
||||
{
|
||||
///- Register the pet for guid lookup
|
||||
GetMap()->GetObjectsStore().Insert<Pet>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<Pet>(this);
|
||||
Unit::AddToWorld();
|
||||
AIM_Initialize();
|
||||
if (ZoneScript* zoneScript = GetZoneScript() ? GetZoneScript() : GetInstanceScript())
|
||||
@@ -97,7 +97,7 @@ void Pet::RemoveFromWorld()
|
||||
{
|
||||
///- Don't call the function for Creature, normal mobs + totems go in a different storage
|
||||
Unit::RemoveFromWorld();
|
||||
GetMap()->GetObjectsStore().Remove<Pet>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<Pet>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ void SceneObject::AddToWorld()
|
||||
{
|
||||
if (!IsInWorld())
|
||||
{
|
||||
GetMap()->GetObjectsStore().Insert<SceneObject>(GetGUID(), this);
|
||||
GetMap()->GetObjectsStore().Insert<SceneObject>(this);
|
||||
WorldObject::AddToWorld();
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ void SceneObject::RemoveFromWorld()
|
||||
if (IsInWorld())
|
||||
{
|
||||
WorldObject::RemoveFromWorld();
|
||||
GetMap()->GetObjectsStore().Remove<SceneObject>(GetGUID());
|
||||
GetMap()->GetObjectsStore().Remove<SceneObject>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,203 +23,71 @@
|
||||
* types of object at the same time.
|
||||
*/
|
||||
|
||||
#include <unordered_map>
|
||||
#include "Define.h"
|
||||
#include "Dynamic/TypeList.h"
|
||||
#include "GridRefManager.h"
|
||||
#include <type_traits>
|
||||
|
||||
/*
|
||||
* @class ContainerMapList is a mulit-type container for map elements
|
||||
* By itself its meaningless but collaborate along with TypeContainers,
|
||||
* it become the most powerfully container in the whole system.
|
||||
*/
|
||||
template<class OBJECT>
|
||||
struct ContainerMapList
|
||||
template <template <typename> typename UnderlyingContainer, typename... Types>
|
||||
struct TypeListContainerStorage
|
||||
{
|
||||
GridRefManager<OBJECT> _element;
|
||||
// empty case
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ContainerMapList<TypeNull> /* nothing is in type null */
|
||||
template <template <typename> typename UnderlyingContainer, typename First, typename... Rest>
|
||||
struct TypeListContainerStorage<UnderlyingContainer, First, Rest...>
|
||||
{
|
||||
typename UnderlyingContainer<First>::Container Head;
|
||||
TypeListContainerStorage<UnderlyingContainer, Rest...> Tail;
|
||||
|
||||
template <typename ObjectType>
|
||||
typename UnderlyingContainer<ObjectType>::Container& FindContainer()
|
||||
{
|
||||
if constexpr (std::is_same_v<First, ObjectType>)
|
||||
return Head;
|
||||
else
|
||||
return Tail.template FindContainer<ObjectType>();
|
||||
}
|
||||
|
||||
template <typename ObjectType>
|
||||
typename UnderlyingContainer<ObjectType>::Container const& FindContainer() const
|
||||
{
|
||||
if constexpr (std::is_same_v<First, ObjectType>)
|
||||
return Head;
|
||||
else
|
||||
return Tail.template FindContainer<ObjectType>();
|
||||
}
|
||||
};
|
||||
|
||||
template<class H, class T>
|
||||
struct ContainerMapList<TypeList<H, T> >
|
||||
template <template <typename> typename UnderlyingContainer, typename... Types>
|
||||
struct TypeListContainer
|
||||
{
|
||||
ContainerMapList<H> _elements;
|
||||
ContainerMapList<T> _TailElements;
|
||||
TypeListContainerStorage<UnderlyingContainer, Types...> Data;
|
||||
|
||||
template <typename ObjectType>
|
||||
static constexpr bool TypeExists = std::disjunction_v<std::is_same<ObjectType, Types>...>;
|
||||
|
||||
template <typename ObjectType> requires TypeExists<ObjectType>
|
||||
bool Insert(ObjectType* object)
|
||||
{
|
||||
return UnderlyingContainer<ObjectType>::Insert(Data.template FindContainer<ObjectType>(), object);
|
||||
}
|
||||
|
||||
template <typename ObjectType> requires TypeExists<ObjectType>
|
||||
bool Remove(ObjectType* object)
|
||||
{
|
||||
return UnderlyingContainer<ObjectType>::Remove(Data.template FindContainer<ObjectType>(), object);
|
||||
}
|
||||
|
||||
template <typename ObjectType> requires TypeExists<ObjectType>
|
||||
bool Size() const
|
||||
{
|
||||
return UnderlyingContainer<ObjectType>::Size(Data.template FindContainer<ObjectType>());
|
||||
}
|
||||
|
||||
template <typename ObjectType> requires TypeExists<ObjectType> && requires { typename UnderlyingContainer<ObjectType>::KeyType; }
|
||||
ObjectType* Find(typename UnderlyingContainer<ObjectType>::KeyType const& key) const
|
||||
{
|
||||
return UnderlyingContainer<ObjectType>::Find(Data.template FindContainer<ObjectType>(), key);
|
||||
}
|
||||
};
|
||||
|
||||
template<class OBJECT, class KEY_TYPE>
|
||||
struct ContainerUnorderedMap
|
||||
{
|
||||
std::unordered_map<KEY_TYPE, OBJECT*> _element;
|
||||
};
|
||||
|
||||
template<class KEY_TYPE>
|
||||
struct ContainerUnorderedMap<TypeNull, KEY_TYPE>
|
||||
{
|
||||
};
|
||||
|
||||
template<class H, class T, class KEY_TYPE>
|
||||
struct ContainerUnorderedMap<TypeList<H, T>, KEY_TYPE>
|
||||
{
|
||||
ContainerUnorderedMap<H, KEY_TYPE> _elements;
|
||||
ContainerUnorderedMap<T, KEY_TYPE> _TailElements;
|
||||
};
|
||||
|
||||
#include "TypeContainerFunctions.h"
|
||||
|
||||
/*
|
||||
* @class TypeMapContainer contains a fixed number of types and is
|
||||
* determined at compile time. This is probably the most complicated
|
||||
* class and do its simplest thing, that is, holds objects
|
||||
* of different types.
|
||||
*/
|
||||
|
||||
template<class OBJECT_TYPES>
|
||||
class TypeMapContainer
|
||||
{
|
||||
public:
|
||||
TypeMapContainer();
|
||||
TypeMapContainer(TypeMapContainer const&) = default;
|
||||
TypeMapContainer(TypeMapContainer&&) noexcept = default;
|
||||
TypeMapContainer& operator=(TypeMapContainer const&) = default;
|
||||
TypeMapContainer& operator=(TypeMapContainer&&) noexcept = default;
|
||||
~TypeMapContainer();
|
||||
|
||||
template<class SPECIFIC_TYPE>
|
||||
size_t Count() const;
|
||||
|
||||
/// inserts a specific object into the container
|
||||
template<class SPECIFIC_TYPE>
|
||||
bool insert(SPECIFIC_TYPE *obj);
|
||||
|
||||
/// Removes the object from the container, and returns the removed object
|
||||
//template<class SPECIFIC_TYPE>
|
||||
//bool remove(SPECIFIC_TYPE* obj)
|
||||
//{
|
||||
// SPECIFIC_TYPE* t = Trinity::Remove(i_elements, obj);
|
||||
// return (t != nullptr);
|
||||
//}
|
||||
|
||||
ContainerMapList<OBJECT_TYPES>& GetElements(void);
|
||||
const ContainerMapList<OBJECT_TYPES>& GetElements(void) const;
|
||||
|
||||
private:
|
||||
ContainerMapList<OBJECT_TYPES> i_elements;
|
||||
};
|
||||
|
||||
template <class OBJECT_TYPES>
|
||||
TypeMapContainer<OBJECT_TYPES>::TypeMapContainer() = default;
|
||||
|
||||
template <class OBJECT_TYPES>
|
||||
TypeMapContainer<OBJECT_TYPES>::~TypeMapContainer() = default;
|
||||
|
||||
template <class OBJECT_TYPES>
|
||||
template <class SPECIFIC_TYPE>
|
||||
size_t TypeMapContainer<OBJECT_TYPES>::Count() const
|
||||
{
|
||||
return Trinity::Count(i_elements, (SPECIFIC_TYPE*)nullptr);
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES>
|
||||
template <class SPECIFIC_TYPE>
|
||||
bool TypeMapContainer<OBJECT_TYPES>::insert(SPECIFIC_TYPE* obj)
|
||||
{
|
||||
SPECIFIC_TYPE* t = Trinity::Insert(i_elements, obj);
|
||||
return (t != nullptr);
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES>
|
||||
ContainerMapList<OBJECT_TYPES>& TypeMapContainer<OBJECT_TYPES>::GetElements()
|
||||
{
|
||||
return i_elements;
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES>
|
||||
const ContainerMapList<OBJECT_TYPES>& TypeMapContainer<OBJECT_TYPES>::GetElements() const
|
||||
{
|
||||
return i_elements;
|
||||
}
|
||||
|
||||
template<class OBJECT_TYPES, class KEY_TYPE>
|
||||
class TypeUnorderedMapContainer
|
||||
{
|
||||
public:
|
||||
TypeUnorderedMapContainer();
|
||||
TypeUnorderedMapContainer(TypeUnorderedMapContainer const&) = default;
|
||||
TypeUnorderedMapContainer(TypeUnorderedMapContainer&&) noexcept = default;
|
||||
TypeUnorderedMapContainer& operator=(TypeUnorderedMapContainer const&) = default;
|
||||
TypeUnorderedMapContainer& operator=(TypeUnorderedMapContainer&&) noexcept = default;
|
||||
~TypeUnorderedMapContainer();
|
||||
|
||||
template<class SPECIFIC_TYPE>
|
||||
bool Insert(KEY_TYPE const& handle, SPECIFIC_TYPE* obj);
|
||||
|
||||
template<class SPECIFIC_TYPE>
|
||||
bool Remove(KEY_TYPE const& handle);
|
||||
|
||||
template<class SPECIFIC_TYPE>
|
||||
SPECIFIC_TYPE* Find(KEY_TYPE const& handle);
|
||||
|
||||
template<class SPECIFIC_TYPE>
|
||||
std::size_t Size() const;
|
||||
|
||||
ContainerUnorderedMap<OBJECT_TYPES, KEY_TYPE>& GetElements();
|
||||
ContainerUnorderedMap<OBJECT_TYPES, KEY_TYPE> const& GetElements() const;
|
||||
|
||||
private:
|
||||
ContainerUnorderedMap<OBJECT_TYPES, KEY_TYPE> _elements;
|
||||
};
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::TypeUnorderedMapContainer() = default;
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::~TypeUnorderedMapContainer() = default;
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
template <class SPECIFIC_TYPE>
|
||||
bool TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::Insert(KEY_TYPE const& handle, SPECIFIC_TYPE* obj)
|
||||
{
|
||||
return Trinity::Insert(_elements, handle, obj);
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
template <class SPECIFIC_TYPE>
|
||||
bool TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::Remove(KEY_TYPE const& handle)
|
||||
{
|
||||
return Trinity::Remove(_elements, handle, (SPECIFIC_TYPE*)nullptr);
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
template <class SPECIFIC_TYPE>
|
||||
SPECIFIC_TYPE* TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::Find(KEY_TYPE const& handle)
|
||||
{
|
||||
return Trinity::Find(_elements, handle, (SPECIFIC_TYPE*)nullptr);
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
template <class SPECIFIC_TYPE>
|
||||
std::size_t TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::Size() const
|
||||
{
|
||||
std::size_t size = 0;
|
||||
Trinity::Size(_elements, &size, (SPECIFIC_TYPE*)nullptr);
|
||||
return size;
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
ContainerUnorderedMap<OBJECT_TYPES, KEY_TYPE>& TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::GetElements()
|
||||
{
|
||||
return _elements;
|
||||
}
|
||||
|
||||
template <class OBJECT_TYPES, class KEY_TYPE>
|
||||
ContainerUnorderedMap<OBJECT_TYPES, KEY_TYPE> const& TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>::GetElements() const
|
||||
{
|
||||
return _elements;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TYPECONTAINER_FUNCTIONS_H
|
||||
#define TYPECONTAINER_FUNCTIONS_H
|
||||
|
||||
/*
|
||||
* Here you'll find a list of helper functions to make
|
||||
* the TypeContainer usefull. Without it, its hard
|
||||
* to access or mutate the container.
|
||||
*/
|
||||
|
||||
#include "Define.h"
|
||||
#include "Dynamic/TypeList.h"
|
||||
|
||||
namespace Trinity
|
||||
{
|
||||
// Helpers
|
||||
// Insert helpers
|
||||
template<class SPECIFIC_TYPE, class KEY_TYPE, class H, class T>
|
||||
inline bool Insert(ContainerUnorderedMap<TypeList<H, T>, KEY_TYPE>& elements, KEY_TYPE const& handle, SPECIFIC_TYPE* obj)
|
||||
{
|
||||
if constexpr (std::is_same_v<H, SPECIFIC_TYPE>)
|
||||
{
|
||||
auto i = elements._elements._element.find(handle);
|
||||
if (i == elements._elements._element.end())
|
||||
{
|
||||
elements._elements._element[handle] = obj;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(i->second == obj, "Object with certain key already in but objects are different!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeNull>)
|
||||
return false;
|
||||
else
|
||||
return Insert(elements._TailElements, handle, obj);
|
||||
}
|
||||
|
||||
// Find helpers
|
||||
template<class SPECIFIC_TYPE, class KEY_TYPE, class H, class T>
|
||||
inline SPECIFIC_TYPE* Find(ContainerUnorderedMap<TypeList<H, T>, KEY_TYPE> const& elements, KEY_TYPE const& handle, SPECIFIC_TYPE* obj)
|
||||
{
|
||||
if constexpr (std::is_same_v<H, SPECIFIC_TYPE>)
|
||||
{
|
||||
auto i = elements._elements._element.find(handle);
|
||||
if (i == elements._elements._element.end())
|
||||
return nullptr;
|
||||
else
|
||||
return i->second;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeNull>)
|
||||
return nullptr;
|
||||
else
|
||||
return Find(elements._TailElements, handle, obj);
|
||||
}
|
||||
|
||||
// Erase helpers
|
||||
template<class SPECIFIC_TYPE, class KEY_TYPE, class H, class T>
|
||||
inline bool Remove(ContainerUnorderedMap<TypeList<H, T>, KEY_TYPE>& elements, KEY_TYPE const& handle, SPECIFIC_TYPE* obj)
|
||||
{
|
||||
if constexpr (std::is_same_v<H, SPECIFIC_TYPE>)
|
||||
{
|
||||
elements._elements._element.erase(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeNull>)
|
||||
return false;
|
||||
else
|
||||
return Remove(elements._TailElements, handle, obj);
|
||||
}
|
||||
|
||||
// Count helpers
|
||||
template<class SPECIFIC_TYPE, class KEY_TYPE, class H, class T>
|
||||
inline bool Size(ContainerUnorderedMap<TypeList<H, T>, KEY_TYPE> const& elements, std::size_t* size, SPECIFIC_TYPE* obj)
|
||||
{
|
||||
if constexpr (std::is_same_v<H, SPECIFIC_TYPE>)
|
||||
{
|
||||
*size = elements._elements._element.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeNull>)
|
||||
return false;
|
||||
else
|
||||
return Size(elements._TailElements, size, obj);
|
||||
}
|
||||
|
||||
/* ContainerMapList Helpers */
|
||||
// count functions
|
||||
template<class SPECIFIC_TYPE, class H, class T>
|
||||
inline size_t Count(ContainerMapList<TypeList<H, T>> const& elements, SPECIFIC_TYPE* fake)
|
||||
{
|
||||
if constexpr (std::is_same_v<H, SPECIFIC_TYPE>)
|
||||
{
|
||||
return elements._elements._element.getSize();
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeNull>)
|
||||
return 0;
|
||||
else
|
||||
return Count(elements._TailElements, fake);
|
||||
}
|
||||
|
||||
// non-const insert functions
|
||||
template<class SPECIFIC_TYPE, class H, class T>
|
||||
inline SPECIFIC_TYPE* Insert(ContainerMapList<TypeList<H, T>>& elements, SPECIFIC_TYPE* obj)
|
||||
{
|
||||
if constexpr (std::is_same_v<H, SPECIFIC_TYPE>)
|
||||
{
|
||||
obj->AddToGrid(elements._elements._element);
|
||||
return obj;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeNull>)
|
||||
return nullptr;
|
||||
else
|
||||
return Insert(elements._TailElements, obj);
|
||||
}
|
||||
|
||||
//// non-const remove method
|
||||
//template<class SPECIFIC_TYPE, class H, class T>
|
||||
//SPECIFIC_TYPE* Remove(ContainerMapList<TypeList<H, T>>& elements, SPECIFIC_TYPE* obj)
|
||||
//{
|
||||
// if constexpr (std::is_same_v<H, SPECIFIC_TYPE>)
|
||||
// {
|
||||
// obj->GetGridRef().unlink();
|
||||
// return obj;
|
||||
// }
|
||||
|
||||
// if constexpr (std::is_same_v<T, TypeNull>)
|
||||
// return nullptr;
|
||||
// else
|
||||
// return Remove(elements._TailElements, obj);
|
||||
//}
|
||||
}
|
||||
#endif
|
||||
@@ -26,76 +26,42 @@
|
||||
|
||||
#include "Dynamic/TypeContainer.h"
|
||||
|
||||
// forward declaration
|
||||
template<class T, class Y> class TypeContainerVisitor;
|
||||
|
||||
// visitor helper
|
||||
template<class VISITOR, class TYPE_CONTAINER> void VisitorHelper(VISITOR &v, TYPE_CONTAINER &c)
|
||||
template <class Visitor, template <typename> typename UnderlyingContainer, typename... Types>
|
||||
inline void VisitorHelper(Visitor& /*v*/, [[maybe_unused]] TypeListContainerStorage<UnderlyingContainer, Types...>& /*c*/)
|
||||
{
|
||||
v.Visit(c);
|
||||
}
|
||||
|
||||
// terminate condition container map list
|
||||
template<class VISITOR> void VisitorHelper(VISITOR &/*v*/, ContainerMapList<TypeNull> &/*c*/) { }
|
||||
|
||||
template<class VISITOR, class T> void VisitorHelper(VISITOR &v, ContainerMapList<T> &c)
|
||||
template <class Visitor, template <typename> typename UnderlyingContainer, typename First, typename... Types>
|
||||
inline void VisitorHelper(Visitor& v, TypeListContainerStorage<UnderlyingContainer, First, Types...>& c)
|
||||
{
|
||||
v.Visit(c._element);
|
||||
v.Visit(c.Head);
|
||||
VisitorHelper(v, c.Tail);
|
||||
}
|
||||
|
||||
// recursion container map list
|
||||
template<class VISITOR, class H, class T> void VisitorHelper(VISITOR &v, ContainerMapList<TypeList<H, T> > &c)
|
||||
template <class Visitor, template <typename> typename UnderlyingContainer, typename... Types>
|
||||
inline void VisitorHelper(Visitor& v, TypeListContainer<UnderlyingContainer, Types...>& c)
|
||||
{
|
||||
VisitorHelper(v, c._elements);
|
||||
VisitorHelper(v, c._TailElements);
|
||||
VisitorHelper(v, c.Data);
|
||||
}
|
||||
|
||||
// for TypeMapContainer
|
||||
template<class VISITOR, class OBJECT_TYPES> void VisitorHelper(VISITOR &v, TypeMapContainer<OBJECT_TYPES> &c)
|
||||
{
|
||||
VisitorHelper(v, c.GetElements());
|
||||
}
|
||||
|
||||
// TypeUnorderedMapContainer
|
||||
template<class VISITOR, class KEY_TYPE>
|
||||
void VisitorHelper(VISITOR& /*v*/, ContainerUnorderedMap<TypeNull, KEY_TYPE>& /*c*/) { }
|
||||
|
||||
template<class VISITOR, class KEY_TYPE, class T>
|
||||
void VisitorHelper(VISITOR& v, ContainerUnorderedMap<T, KEY_TYPE>& c)
|
||||
{
|
||||
v.Visit(c._element);
|
||||
}
|
||||
|
||||
template<class VISITOR, class KEY_TYPE, class H, class T>
|
||||
void VisitorHelper(VISITOR& v, ContainerUnorderedMap<TypeList<H, T>, KEY_TYPE>& c)
|
||||
{
|
||||
VisitorHelper(v, c._elements);
|
||||
VisitorHelper(v, c._TailElements);
|
||||
}
|
||||
|
||||
template<class VISITOR, class OBJECT_TYPES, class KEY_TYPE>
|
||||
void VisitorHelper(VISITOR& v, TypeUnorderedMapContainer<OBJECT_TYPES, KEY_TYPE>& c)
|
||||
{
|
||||
VisitorHelper(v, c.GetElements());
|
||||
}
|
||||
|
||||
template<class VISITOR, class TYPE_CONTAINER>
|
||||
template<class Visitor, class TypeContainer>
|
||||
class TypeContainerVisitor
|
||||
{
|
||||
public:
|
||||
TypeContainerVisitor(VISITOR &v) : i_visitor(v) { }
|
||||
public:
|
||||
TypeContainerVisitor(Visitor& v) : i_visitor(v) { }
|
||||
|
||||
void Visit(TYPE_CONTAINER& c)
|
||||
{
|
||||
VisitorHelper(i_visitor, c);
|
||||
}
|
||||
void Visit(TypeContainer& c)
|
||||
{
|
||||
VisitorHelper(i_visitor, c);
|
||||
}
|
||||
|
||||
void Visit(TYPE_CONTAINER const& c) const
|
||||
{
|
||||
VisitorHelper(i_visitor, c);
|
||||
}
|
||||
void Visit(TypeContainer const& c) const
|
||||
{
|
||||
VisitorHelper(i_visitor, c);
|
||||
}
|
||||
|
||||
private:
|
||||
VISITOR &i_visitor;
|
||||
private:
|
||||
Visitor& i_visitor;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
#include "Define.h"
|
||||
#include "TypeContainer.h"
|
||||
#include "Errors.h"
|
||||
#include "TypeContainerVisitor.h"
|
||||
|
||||
// forward declaration
|
||||
@@ -57,7 +57,7 @@ class Grid
|
||||
*/
|
||||
template<class SPECIFIC_OBJECT> void AddWorldObject(SPECIFIC_OBJECT *obj)
|
||||
{
|
||||
i_objects.template insert<SPECIFIC_OBJECT>(obj);
|
||||
i_objects.template Insert<SPECIFIC_OBJECT>(obj);
|
||||
ASSERT(obj->IsInGrid());
|
||||
}
|
||||
|
||||
@@ -103,14 +103,14 @@ class Grid
|
||||
template<class T>
|
||||
uint32 GetWorldObjectCountInGrid() const
|
||||
{
|
||||
return uint32(i_objects.template Count<T>());
|
||||
return uint32(i_objects.template Size<T>());
|
||||
}
|
||||
|
||||
/** Inserts a container type object into the grid.
|
||||
*/
|
||||
template<class SPECIFIC_OBJECT> void AddGridObject(SPECIFIC_OBJECT *obj)
|
||||
{
|
||||
i_container.template insert<SPECIFIC_OBJECT>(obj);
|
||||
i_container.template Insert<SPECIFIC_OBJECT>(obj);
|
||||
ASSERT(obj->IsInGrid());
|
||||
}
|
||||
|
||||
@@ -139,4 +139,5 @@ class Grid
|
||||
//typedef std::set<void*> ActiveGridObjects;
|
||||
//ActiveGridObjects m_activeGridObjects;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -62,11 +62,6 @@ class Conversation;
|
||||
#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT
|
||||
#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations
|
||||
|
||||
// Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case)
|
||||
typedef TYPELIST_4(Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/) AllWorldObjectTypes;
|
||||
typedef TYPELIST_7(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger, SceneObject, Conversation) AllGridObjectTypes;
|
||||
typedef TYPELIST_8(Creature, GameObject, DynamicObject, Pet, Corpse, AreaTrigger, SceneObject, Conversation) AllMapStoredObjectTypes;
|
||||
|
||||
typedef GridRefManager<Corpse> CorpseMapType;
|
||||
typedef GridRefManager<Creature> CreatureMapType;
|
||||
typedef GridRefManager<DynamicObject> DynamicObjectMapType;
|
||||
@@ -89,11 +84,12 @@ enum GridMapTypeMask
|
||||
GRID_MAP_TYPE_MASK_ALL = 0xFF
|
||||
};
|
||||
|
||||
extern template class TypeMapContainer<AllGridObjectTypes>;
|
||||
extern template class TypeMapContainer<AllWorldObjectTypes>;
|
||||
// Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case)
|
||||
extern template struct TypeListContainer<GridRefManagerContainer, GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger, SceneObject, Conversation>;
|
||||
extern template struct TypeListContainer<GridRefManagerContainer, Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/>;
|
||||
|
||||
typedef TypeMapContainer<AllGridObjectTypes> GridTypeMapContainer;
|
||||
typedef TypeMapContainer<AllWorldObjectTypes> WorldTypeMapContainer;
|
||||
typedef TypeListContainer<GridRefManagerContainer, GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger, SceneObject, Conversation> GridTypeMapContainer;
|
||||
typedef TypeListContainer<GridRefManagerContainer, Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/> WorldTypeMapContainer;
|
||||
|
||||
extern template class Grid<Player, WorldTypeMapContainer, GridTypeMapContainer>;
|
||||
extern template class NGrid<MAX_NUMBER_OF_CELLS, Player, WorldTypeMapContainer, GridTypeMapContainer>;
|
||||
|
||||
@@ -27,4 +27,30 @@ template<class OBJECT>
|
||||
class GridRefManager : public RefManager<GridReference<OBJECT>>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename ObjectType>
|
||||
struct GridRefManagerContainer
|
||||
{
|
||||
using Container = GridRefManager<ObjectType>;
|
||||
using ValueType = ObjectType*;
|
||||
|
||||
static bool Insert(Container& container, ValueType object)
|
||||
{
|
||||
object->AddToGrid(container);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Remove(Container& /*container*/, ValueType object)
|
||||
{
|
||||
object->RemoveFromGrid();
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::size_t Size(Container const& container)
|
||||
{
|
||||
return container.getSize();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,5 +32,5 @@ GridInfo::GridInfo(time_t expiry, bool unload /*= true */) : i_timer(expiry), vi
|
||||
template class Grid<Player, WorldTypeMapContainer, GridTypeMapContainer>;
|
||||
template class NGrid<MAX_NUMBER_OF_CELLS, Player, WorldTypeMapContainer, GridTypeMapContainer>;
|
||||
|
||||
template class TC_GAME_API TypeMapContainer<AllGridObjectTypes>;
|
||||
template class TC_GAME_API TypeMapContainer<AllWorldObjectTypes>;
|
||||
template struct TC_GAME_API TypeListContainer<GridRefManagerContainer, GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger, SceneObject, Conversation>;
|
||||
template struct TC_GAME_API TypeListContainer<GridRefManagerContainer, Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/>;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "Grid.h"
|
||||
#include "GridRefManager.h"
|
||||
#include "GridReference.h"
|
||||
#include "Timer.h"
|
||||
|
||||
|
||||
@@ -180,52 +180,21 @@ template<class T>
|
||||
void Map::AddToGrid(T* obj, Cell const& cell)
|
||||
{
|
||||
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
||||
if (obj->IsStoredInWorldObjectGridContainer())
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).template AddWorldObject<T>(obj);
|
||||
else
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).template AddGridObject<T>(obj);
|
||||
}
|
||||
if constexpr (WorldTypeMapContainer::TypeExists<T> && GridTypeMapContainer::TypeExists<T>)
|
||||
{
|
||||
NGridType::GridType& cellType = grid->GetGridType(cell.CellX(), cell.CellY());
|
||||
if (obj->IsStoredInWorldObjectGridContainer())
|
||||
cellType.AddWorldObject<T>(obj);
|
||||
else
|
||||
cellType.AddGridObject<T>(obj);
|
||||
}
|
||||
else if constexpr (WorldTypeMapContainer::TypeExists<T>)
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject<T>(obj);
|
||||
else if constexpr (GridTypeMapContainer::TypeExists<T>)
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject<T>(obj);
|
||||
|
||||
template<>
|
||||
void Map::AddToGrid(Creature* obj, Cell const& cell)
|
||||
{
|
||||
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
||||
if (obj->IsStoredInWorldObjectGridContainer())
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj);
|
||||
else
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
||||
|
||||
obj->SetCurrentCell(cell);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Map::AddToGrid(GameObject* obj, Cell const& cell)
|
||||
{
|
||||
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
||||
|
||||
obj->SetCurrentCell(cell);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Map::AddToGrid(DynamicObject* obj, Cell const& cell)
|
||||
{
|
||||
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
||||
if (obj->IsStoredInWorldObjectGridContainer())
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj);
|
||||
else
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
||||
|
||||
obj->SetCurrentCell(cell);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Map::AddToGrid(AreaTrigger* obj, Cell const& cell)
|
||||
{
|
||||
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
||||
|
||||
obj->SetCurrentCell(cell);
|
||||
if constexpr (std::is_base_of_v<MapObject, T>)
|
||||
obj->SetCurrentCell(cell);
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -240,10 +209,11 @@ void Map::AddToGrid(Corpse* obj, Cell const& cell)
|
||||
// to avoid failing an assertion in GridObject::AddToGrid
|
||||
if (grid->isGridObjectDataLoaded())
|
||||
{
|
||||
NGridType::GridType& cellType = grid->GetGridType(cell.CellX(), cell.CellY());
|
||||
if (obj->IsStoredInWorldObjectGridContainer())
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj);
|
||||
cellType.AddWorldObject(obj);
|
||||
else
|
||||
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
||||
cellType.AddGridObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4090,4 +4060,4 @@ std::string InstanceMap::GetDebugInfo() const
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template class TC_GAME_API TypeUnorderedMapContainer<AllMapStoredObjectTypes, ObjectGuid>;
|
||||
template struct TC_GAME_API TypeListContainer<MapStoredObjectsUnorderedMap, Creature, GameObject, DynamicObject, Pet, Corpse, AreaTrigger, SceneObject, Conversation>;
|
||||
|
||||
@@ -183,8 +183,40 @@ inline bool CompareRespawnInfo::operator()(RespawnInfo const* a, RespawnInfo con
|
||||
return a->type < b->type;
|
||||
}
|
||||
|
||||
extern template class TypeUnorderedMapContainer<AllMapStoredObjectTypes, ObjectGuid>;
|
||||
typedef TypeUnorderedMapContainer<AllMapStoredObjectTypes, ObjectGuid> MapStoredObjectTypesContainer;
|
||||
template <typename ObjectType>
|
||||
struct MapStoredObjectsUnorderedMap
|
||||
{
|
||||
using Container = std::unordered_map<ObjectGuid, ObjectType*>;
|
||||
using KeyType = ObjectGuid;
|
||||
using ValueType = ObjectType*;
|
||||
|
||||
static bool Insert(Container& container, ValueType object)
|
||||
{
|
||||
auto [itr, isNew] = container.try_emplace(object->GetGUID(), object);
|
||||
ASSERT(isNew || itr->second == object, "Object with certain key already in but objects are different!");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Remove(Container& container, ValueType object)
|
||||
{
|
||||
container.erase(object->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::size_t Size(Container const& container)
|
||||
{
|
||||
return container.size();
|
||||
}
|
||||
|
||||
static ValueType Find(Container const& container, KeyType const& key)
|
||||
{
|
||||
auto itr = container.find(key);
|
||||
return itr != container.end() ? itr->second : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
extern template struct TypeListContainer<MapStoredObjectsUnorderedMap, Creature, GameObject, DynamicObject, Pet, Corpse, AreaTrigger, SceneObject, Conversation>;
|
||||
typedef TypeListContainer<MapStoredObjectsUnorderedMap, Creature, GameObject, DynamicObject, Pet, Corpse, AreaTrigger, SceneObject, Conversation> MapStoredObjectTypesContainer;
|
||||
|
||||
class TC_GAME_API Map : public GridRefManager<NGridType>
|
||||
{
|
||||
|
||||
@@ -729,7 +729,7 @@ class instance_culling_of_stratholme : public InstanceMapScript
|
||||
// Reset respawn time on all permanent spawns, despawn all temporary spawns
|
||||
// @todo dynspawn, this won't work
|
||||
std::vector<Creature*> toDespawn;
|
||||
std::unordered_map<ObjectGuid, Creature*> const& objects = instance->GetObjectsStore().GetElements()._elements._element;
|
||||
std::unordered_map<ObjectGuid, Creature*> const& objects = instance->GetObjectsStore().Data.Head;
|
||||
for (std::unordered_map<ObjectGuid, Creature*>::const_iterator itr = objects.cbegin(); itr != objects.cend(); ++itr)
|
||||
{
|
||||
if (itr->second && (itr->second->isDead() || !itr->second->GetSpawnId() || itr->second->GetOriginalEntry() != itr->second->GetEntry()))
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_TYPELIST_H
|
||||
#define TRINITY_TYPELIST_H
|
||||
|
||||
/*
|
||||
@struct TypeList
|
||||
TypeList is the most simple but yet the most powerfull class of all. It holds
|
||||
at compile time the different type of objects in a linked list.
|
||||
*/
|
||||
|
||||
class TypeNull;
|
||||
|
||||
template<typename HEAD, typename TAIL>
|
||||
struct TypeList
|
||||
{
|
||||
typedef HEAD Head;
|
||||
typedef TAIL Tail;
|
||||
};
|
||||
|
||||
// enough for now.. can be expand at any point in time as needed
|
||||
#define TYPELIST_1(T1) TypeList<T1, TypeNull>
|
||||
#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2) >
|
||||
#define TYPELIST_3(T1, T2, T3) TypeList<T1, TYPELIST_2(T2, T3) >
|
||||
#define TYPELIST_4(T1, T2, T3, T4) TypeList<T1, TYPELIST_3(T2, T3, T4) >
|
||||
#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList<T1, TYPELIST_4(T2, T3, T4, T5) >
|
||||
#define TYPELIST_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TYPELIST_5(T2, T3, T4, T5, T6) >
|
||||
#define TYPELIST_7(T1, T2, T3, T4, T5, T6, T7) TypeList<T1, TYPELIST_6(T2, T3, T4, T5, T6, T7) >
|
||||
#define TYPELIST_8(T1, T2, T3, T4, T5, T6, T7, T8) TypeList<T1, TYPELIST_7(T2, T3, T4, T5, T6, T7, T8) >
|
||||
#endif
|
||||
Reference in New Issue
Block a user