Replaced all hashsets which are used concurrently to Concurrent hashsets, some slight db tweaks
This commit is contained in:
parent
aa96ed884e
commit
e60034728c
761
src/NadekoBot/DataStructures/ConcurrentHashSet.cs
Normal file
761
src/NadekoBot/DataStructures/ConcurrentHashSet.cs
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
// License MIT
|
||||||
|
// Source: https://github.com/i3arnon/ConcurrentHashSet
|
||||||
|
|
||||||
|
using ConcurrentCollections;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace System.Collections.Concurrent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a thread-safe hash-based unique collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the items in the collection.</typeparam>
|
||||||
|
/// <remarks>
|
||||||
|
/// All public members of <see cref="ConcurrentHashSet{T}"/> are thread-safe and may be used
|
||||||
|
/// concurrently from multiple threads.
|
||||||
|
/// </remarks>
|
||||||
|
[DebuggerDisplay("Count = {Count}")]
|
||||||
|
public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T>
|
||||||
|
{
|
||||||
|
private const int DefaultCapacity = 31;
|
||||||
|
private const int MaxLockNumber = 1024;
|
||||||
|
|
||||||
|
private readonly IEqualityComparer<T> _comparer;
|
||||||
|
private readonly bool _growLockArray;
|
||||||
|
|
||||||
|
private int _budget;
|
||||||
|
private volatile Tables _tables;
|
||||||
|
|
||||||
|
private static int DefaultConcurrencyLevel => PlatformHelper.ProcessorCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of items contained in the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The number of items contained in the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>.</value>
|
||||||
|
/// <remarks>Count has snapshot semantics and represents the number of items in the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// at the moment when Count was accessed.</remarks>
|
||||||
|
public int Count {
|
||||||
|
get {
|
||||||
|
var count = 0;
|
||||||
|
var acquiredLocks = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AcquireAllLocks(ref acquiredLocks);
|
||||||
|
|
||||||
|
for (var i = 0; i < _tables.CountPerLock.Length; i++)
|
||||||
|
{
|
||||||
|
count += _tables.CountPerLock[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLocks(0, acquiredLocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that indicates whether the <see cref="ConcurrentHashSet{T}"/> is empty.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>true if the <see cref="ConcurrentHashSet{T}"/> is empty; otherwise,
|
||||||
|
/// false.</value>
|
||||||
|
public bool IsEmpty {
|
||||||
|
get {
|
||||||
|
var acquiredLocks = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AcquireAllLocks(ref acquiredLocks);
|
||||||
|
|
||||||
|
for (var i = 0; i < _tables.CountPerLock.Length; i++)
|
||||||
|
{
|
||||||
|
if (_tables.CountPerLock[i] != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLocks(0, acquiredLocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// class that is empty, has the default concurrency level, has the default initial capacity, and
|
||||||
|
/// uses the default comparer for the item type.
|
||||||
|
/// </summary>
|
||||||
|
public ConcurrentHashSet()
|
||||||
|
: this(DefaultConcurrencyLevel, DefaultCapacity, true, EqualityComparer<T>.Default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// class that is empty, has the specified concurrency level and capacity, and uses the default
|
||||||
|
/// comparer for the item type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="concurrencyLevel">The estimated number of threads that will update the
|
||||||
|
/// <see cref="ConcurrentHashSet{T}"/> concurrently.</param>
|
||||||
|
/// <param name="capacity">The initial number of elements that the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// can contain.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="concurrencyLevel"/> is
|
||||||
|
/// less than 1.</exception>
|
||||||
|
/// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="capacity"/> is less than
|
||||||
|
/// 0.</exception>
|
||||||
|
public ConcurrentHashSet(int concurrencyLevel, int capacity)
|
||||||
|
: this(concurrencyLevel, capacity, false, EqualityComparer<T>.Default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// class that contains elements copied from the specified <see
|
||||||
|
/// cref="T:System.Collections.IEnumerable{T}"/>, has the default concurrency
|
||||||
|
/// level, has the default initial capacity, and uses the default comparer for the item type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="collection">The <see
|
||||||
|
/// cref="T:System.Collections.IEnumerable{T}"/> whose elements are copied to
|
||||||
|
/// the new
|
||||||
|
/// <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference.</exception>
|
||||||
|
public ConcurrentHashSet(IEnumerable<T> collection)
|
||||||
|
: this(collection, EqualityComparer<T>.Default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// class that is empty, has the specified concurrency level and capacity, and uses the specified
|
||||||
|
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>
|
||||||
|
/// implementation to use when comparing items.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference.</exception>
|
||||||
|
public ConcurrentHashSet(IEqualityComparer<T> comparer)
|
||||||
|
: this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// class that contains elements copied from the specified <see
|
||||||
|
/// cref="T:System.Collections.IEnumerable"/>, has the default concurrency level, has the default
|
||||||
|
/// initial capacity, and uses the specified
|
||||||
|
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="collection">The <see
|
||||||
|
/// cref="T:System.Collections.IEnumerable{T}"/> whose elements are copied to
|
||||||
|
/// the new
|
||||||
|
/// <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||||
|
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>
|
||||||
|
/// implementation to use when comparing items.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference
|
||||||
|
/// (Nothing in Visual Basic). -or-
|
||||||
|
/// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic).
|
||||||
|
/// </exception>
|
||||||
|
public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer)
|
||||||
|
: this(comparer)
|
||||||
|
{
|
||||||
|
if (collection == null) throw new ArgumentNullException(nameof(collection));
|
||||||
|
|
||||||
|
InitializeFromCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// class that contains elements copied from the specified <see cref="T:System.Collections.IEnumerable"/>,
|
||||||
|
/// has the specified concurrency level, has the specified initial capacity, and uses the specified
|
||||||
|
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="concurrencyLevel">The estimated number of threads that will update the
|
||||||
|
/// <see cref="ConcurrentHashSet{T}"/> concurrently.</param>
|
||||||
|
/// <param name="collection">The <see cref="T:System.Collections.IEnumerable{T}"/> whose elements are copied to the new
|
||||||
|
/// <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||||
|
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/> implementation to use
|
||||||
|
/// when comparing items.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// <paramref name="collection"/> is a null reference.
|
||||||
|
/// -or-
|
||||||
|
/// <paramref name="comparer"/> is a null reference.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||||
|
/// <paramref name="concurrencyLevel"/> is less than 1.
|
||||||
|
/// </exception>
|
||||||
|
public ConcurrentHashSet(int concurrencyLevel, IEnumerable<T> collection, IEqualityComparer<T> comparer)
|
||||||
|
: this(concurrencyLevel, DefaultCapacity, false, comparer)
|
||||||
|
{
|
||||||
|
if (collection == null) throw new ArgumentNullException(nameof(collection));
|
||||||
|
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
|
||||||
|
|
||||||
|
InitializeFromCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// class that is empty, has the specified concurrency level, has the specified initial capacity, and
|
||||||
|
/// uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="concurrencyLevel">The estimated number of threads that will update the
|
||||||
|
/// <see cref="ConcurrentHashSet{T}"/> concurrently.</param>
|
||||||
|
/// <param name="capacity">The initial number of elements that the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// can contain.</param>
|
||||||
|
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>
|
||||||
|
/// implementation to use when comparing items.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||||
|
/// <paramref name="concurrencyLevel"/> is less than 1. -or-
|
||||||
|
/// <paramref name="capacity"/> is less than 0.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference.</exception>
|
||||||
|
public ConcurrentHashSet(int concurrencyLevel, int capacity, IEqualityComparer<T> comparer)
|
||||||
|
: this(concurrencyLevel, capacity, false, comparer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConcurrentHashSet(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<T> comparer)
|
||||||
|
{
|
||||||
|
if (concurrencyLevel < 1) throw new ArgumentOutOfRangeException(nameof(concurrencyLevel));
|
||||||
|
if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||||
|
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
|
||||||
|
|
||||||
|
// The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard
|
||||||
|
// any buckets.
|
||||||
|
if (capacity < concurrencyLevel)
|
||||||
|
{
|
||||||
|
capacity = concurrencyLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
var locks = new object[concurrencyLevel];
|
||||||
|
for (var i = 0; i < locks.Length; i++)
|
||||||
|
{
|
||||||
|
locks[i] = new object();
|
||||||
|
}
|
||||||
|
|
||||||
|
var countPerLock = new int[locks.Length];
|
||||||
|
var buckets = new Node[capacity];
|
||||||
|
_tables = new Tables(buckets, locks, countPerLock);
|
||||||
|
|
||||||
|
_growLockArray = growLockArray;
|
||||||
|
_budget = buckets.Length / locks.Length;
|
||||||
|
_comparer = comparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified item to the <see cref="ConcurrentHashSet{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to add.</param>
|
||||||
|
/// <returns>true if the items was added to the <see cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// successfully; false if it already exists.</returns>
|
||||||
|
/// <exception cref="T:System.OverflowException">The <see cref="ConcurrentHashSet{T}"/>
|
||||||
|
/// contains too many items.</exception>
|
||||||
|
public bool Add(T item) =>
|
||||||
|
AddInternal(item, _comparer.GetHashCode(item), true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all items from the <see cref="ConcurrentHashSet{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
var locksAcquired = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AcquireAllLocks(ref locksAcquired);
|
||||||
|
|
||||||
|
var newTables = new Tables(new Node[DefaultCapacity], _tables.Locks, new int[_tables.CountPerLock.Length]);
|
||||||
|
_tables = newTables;
|
||||||
|
_budget = Math.Max(1, newTables.Buckets.Length / newTables.Locks.Length);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLocks(0, locksAcquired);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the <see cref="ConcurrentHashSet{T}"/> contains the specified
|
||||||
|
/// item.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to locate in the <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||||
|
/// <returns>true if the <see cref="ConcurrentHashSet{T}"/> contains the item; otherwise, false.</returns>
|
||||||
|
public bool Contains(T item)
|
||||||
|
{
|
||||||
|
var hashcode = _comparer.GetHashCode(item);
|
||||||
|
|
||||||
|
// We must capture the _buckets field in a local variable. It is set to a new table on each table resize.
|
||||||
|
var tables = _tables;
|
||||||
|
|
||||||
|
var bucketNo = GetBucket(hashcode, tables.Buckets.Length);
|
||||||
|
|
||||||
|
// We can get away w/out a lock here.
|
||||||
|
// The Volatile.Read ensures that the load of the fields of 'n' doesn't move before the load from buckets[i].
|
||||||
|
var current = Volatile.Read(ref tables.Buckets[bucketNo]);
|
||||||
|
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
if (hashcode == current.Hashcode && _comparer.Equals(current.Item, item))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current = current.Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to remove the item from the <see cref="ConcurrentHashSet{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to remove.</param>
|
||||||
|
/// <returns>true if an item was removed successfully; otherwise, false.</returns>
|
||||||
|
public bool TryRemove(T item)
|
||||||
|
{
|
||||||
|
var hashcode = _comparer.GetHashCode(item);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var tables = _tables;
|
||||||
|
|
||||||
|
int bucketNo, lockNo;
|
||||||
|
GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables.Buckets.Length, tables.Locks.Length);
|
||||||
|
|
||||||
|
lock (tables.Locks[lockNo])
|
||||||
|
{
|
||||||
|
// If the table just got resized, we may not be holding the right lock, and must retry.
|
||||||
|
// This should be a rare occurrence.
|
||||||
|
if (tables != _tables)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node previous = null;
|
||||||
|
for (var current = tables.Buckets[bucketNo]; current != null; current = current.Next)
|
||||||
|
{
|
||||||
|
Debug.Assert((previous == null && current == tables.Buckets[bucketNo]) || previous.Next == current);
|
||||||
|
|
||||||
|
if (hashcode == current.Hashcode && _comparer.Equals(current.Item, item))
|
||||||
|
{
|
||||||
|
if (previous == null)
|
||||||
|
{
|
||||||
|
Volatile.Write(ref tables.Buckets[bucketNo], current.Next);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
previous.Next = current.Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tables.CountPerLock[lockNo]--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
/// <summary>Returns an enumerator that iterates through the <see
|
||||||
|
/// cref="ConcurrentHashSet{T}"/>.</summary>
|
||||||
|
/// <returns>An enumerator for the <see cref="ConcurrentHashSet{T}"/>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The enumerator returned from the collection is safe to use concurrently with
|
||||||
|
/// reads and writes to the collection, however it does not represent a moment-in-time snapshot
|
||||||
|
/// of the collection. The contents exposed through the enumerator may contain modifications
|
||||||
|
/// made to the collection after <see cref="GetEnumerator"/> was called.
|
||||||
|
/// </remarks>
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
var buckets = _tables.Buckets;
|
||||||
|
|
||||||
|
for (var i = 0; i < buckets.Length; i++)
|
||||||
|
{
|
||||||
|
// The Volatile.Read ensures that the load of the fields of 'current' doesn't move before the load from buckets[i].
|
||||||
|
var current = Volatile.Read(ref buckets[i]);
|
||||||
|
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
yield return current.Item;
|
||||||
|
current = current.Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<T>.Add(T item) => Add(item);
|
||||||
|
|
||||||
|
bool ICollection<T>.IsReadOnly => false;
|
||||||
|
|
||||||
|
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
if (array == null) throw new ArgumentNullException(nameof(array));
|
||||||
|
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
|
||||||
|
|
||||||
|
var locksAcquired = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AcquireAllLocks(ref locksAcquired);
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < _tables.Locks.Length && count >= 0; i++)
|
||||||
|
{
|
||||||
|
count += _tables.CountPerLock[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array.Length - count < arrayIndex || count < 0) //"count" itself or "count + arrayIndex" can overflow
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The index is equal to or greater than the length of the array, or the number of elements in the set is greater than the available space from index to the end of the destination array.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyToItems(array, arrayIndex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLocks(0, locksAcquired);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<T>.Remove(T item) => TryRemove(item);
|
||||||
|
|
||||||
|
private void InitializeFromCollection(IEnumerable<T> collection)
|
||||||
|
{
|
||||||
|
foreach (var item in collection)
|
||||||
|
{
|
||||||
|
AddInternal(item, _comparer.GetHashCode(item), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_budget == 0)
|
||||||
|
{
|
||||||
|
_budget = _tables.Buckets.Length / _tables.Locks.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AddInternal(T item, int hashcode, bool acquireLock)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int bucketNo, lockNo;
|
||||||
|
|
||||||
|
var tables = _tables;
|
||||||
|
GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables.Buckets.Length, tables.Locks.Length);
|
||||||
|
|
||||||
|
var resizeDesired = false;
|
||||||
|
var lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (acquireLock)
|
||||||
|
Monitor.Enter(tables.Locks[lockNo], ref lockTaken);
|
||||||
|
|
||||||
|
// If the table just got resized, we may not be holding the right lock, and must retry.
|
||||||
|
// This should be a rare occurrence.
|
||||||
|
if (tables != _tables)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find this item in the bucket
|
||||||
|
Node previous = null;
|
||||||
|
for (var current = tables.Buckets[bucketNo]; current != null; current = current.Next)
|
||||||
|
{
|
||||||
|
Debug.Assert((previous == null && current == tables.Buckets[bucketNo]) || previous.Next == current);
|
||||||
|
if (hashcode == current.Hashcode && _comparer.Equals(current.Item, item))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The item was not found in the bucket. Insert the new item.
|
||||||
|
Volatile.Write(ref tables.Buckets[bucketNo], new Node(item, hashcode, tables.Buckets[bucketNo]));
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
tables.CountPerLock[lockNo]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the number of elements guarded by this lock has exceeded the budget, resize the bucket table.
|
||||||
|
// It is also possible that GrowTable will increase the budget but won't resize the bucket table.
|
||||||
|
// That happens if the bucket table is found to be poorly utilized due to a bad hash function.
|
||||||
|
//
|
||||||
|
if (tables.CountPerLock[lockNo] > _budget)
|
||||||
|
{
|
||||||
|
resizeDesired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken)
|
||||||
|
Monitor.Exit(tables.Locks[lockNo]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// The fact that we got here means that we just performed an insertion. If necessary, we will grow the table.
|
||||||
|
//
|
||||||
|
// Concurrency notes:
|
||||||
|
// - Notice that we are not holding any locks at when calling GrowTable. This is necessary to prevent deadlocks.
|
||||||
|
// - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0
|
||||||
|
// and then verify that the table we passed to it as the argument is still the current table.
|
||||||
|
//
|
||||||
|
if (resizeDesired)
|
||||||
|
{
|
||||||
|
GrowTable(tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetBucket(int hashcode, int bucketCount)
|
||||||
|
{
|
||||||
|
var bucketNo = (hashcode & 0x7fffffff) % bucketCount;
|
||||||
|
Debug.Assert(bucketNo >= 0 && bucketNo < bucketCount);
|
||||||
|
return bucketNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
|
||||||
|
{
|
||||||
|
bucketNo = (hashcode & 0x7fffffff) % bucketCount;
|
||||||
|
lockNo = bucketNo % lockCount;
|
||||||
|
|
||||||
|
Debug.Assert(bucketNo >= 0 && bucketNo < bucketCount);
|
||||||
|
Debug.Assert(lockNo >= 0 && lockNo < lockCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GrowTable(Tables tables)
|
||||||
|
{
|
||||||
|
const int maxArrayLength = 0X7FEFFFFF;
|
||||||
|
var locksAcquired = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// The thread that first obtains _locks[0] will be the one doing the resize operation
|
||||||
|
AcquireLocks(0, 1, ref locksAcquired);
|
||||||
|
|
||||||
|
// Make sure nobody resized the table while we were waiting for lock 0:
|
||||||
|
if (tables != _tables)
|
||||||
|
{
|
||||||
|
// We assume that since the table reference is different, it was already resized (or the budget
|
||||||
|
// was adjusted). If we ever decide to do table shrinking, or replace the table for other reasons,
|
||||||
|
// we will have to revisit this logic.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the (approx.) total size. Use an Int64 accumulation variable to avoid an overflow.
|
||||||
|
long approxCount = 0;
|
||||||
|
for (var i = 0; i < tables.CountPerLock.Length; i++)
|
||||||
|
{
|
||||||
|
approxCount += tables.CountPerLock[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the bucket array is too empty, double the budget instead of resizing the table
|
||||||
|
//
|
||||||
|
if (approxCount < tables.Buckets.Length / 4)
|
||||||
|
{
|
||||||
|
_budget = 2 * _budget;
|
||||||
|
if (_budget < 0)
|
||||||
|
{
|
||||||
|
_budget = int.MaxValue;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by
|
||||||
|
// 2,3,5 or 7. We can consider a different table-sizing policy in the future.
|
||||||
|
var newLength = 0;
|
||||||
|
var maximizeTableSize = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
// Double the size of the buckets table and add one, so that we have an odd integer.
|
||||||
|
newLength = tables.Buckets.Length * 2 + 1;
|
||||||
|
|
||||||
|
// Now, we only need to check odd integers, and find the first that is not divisible
|
||||||
|
// by 3, 5 or 7.
|
||||||
|
while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0)
|
||||||
|
{
|
||||||
|
newLength += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(newLength % 2 != 0);
|
||||||
|
|
||||||
|
if (newLength > maxArrayLength)
|
||||||
|
{
|
||||||
|
maximizeTableSize = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OverflowException)
|
||||||
|
{
|
||||||
|
maximizeTableSize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maximizeTableSize)
|
||||||
|
{
|
||||||
|
newLength = maxArrayLength;
|
||||||
|
|
||||||
|
// We want to make sure that GrowTable will not be called again, since table is at the maximum size.
|
||||||
|
// To achieve that, we set the budget to int.MaxValue.
|
||||||
|
//
|
||||||
|
// (There is one special case that would allow GrowTable() to be called in the future:
|
||||||
|
// calling Clear() on the ConcurrentHashSet will shrink the table and lower the budget.)
|
||||||
|
_budget = int.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now acquire all other locks for the table
|
||||||
|
AcquireLocks(1, tables.Locks.Length, ref locksAcquired);
|
||||||
|
|
||||||
|
var newLocks = tables.Locks;
|
||||||
|
|
||||||
|
// Add more locks
|
||||||
|
if (_growLockArray && tables.Locks.Length < MaxLockNumber)
|
||||||
|
{
|
||||||
|
newLocks = new object[tables.Locks.Length * 2];
|
||||||
|
Array.Copy(tables.Locks, 0, newLocks, 0, tables.Locks.Length);
|
||||||
|
for (var i = tables.Locks.Length; i < newLocks.Length; i++)
|
||||||
|
{
|
||||||
|
newLocks[i] = new object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newBuckets = new Node[newLength];
|
||||||
|
var newCountPerLock = new int[newLocks.Length];
|
||||||
|
|
||||||
|
// Copy all data into a new table, creating new nodes for all elements
|
||||||
|
for (var i = 0; i < tables.Buckets.Length; i++)
|
||||||
|
{
|
||||||
|
var current = tables.Buckets[i];
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
var next = current.Next;
|
||||||
|
int newBucketNo, newLockNo;
|
||||||
|
GetBucketAndLockNo(current.Hashcode, out newBucketNo, out newLockNo, newBuckets.Length, newLocks.Length);
|
||||||
|
|
||||||
|
newBuckets[newBucketNo] = new Node(current.Item, current.Hashcode, newBuckets[newBucketNo]);
|
||||||
|
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
newCountPerLock[newLockNo]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the budget
|
||||||
|
_budget = Math.Max(1, newBuckets.Length / newLocks.Length);
|
||||||
|
|
||||||
|
// Replace tables with the new versions
|
||||||
|
_tables = new Tables(newBuckets, newLocks, newCountPerLock);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Release all locks that we took earlier
|
||||||
|
ReleaseLocks(0, locksAcquired);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcquireAllLocks(ref int locksAcquired)
|
||||||
|
{
|
||||||
|
// First, acquire lock 0
|
||||||
|
AcquireLocks(0, 1, ref locksAcquired);
|
||||||
|
|
||||||
|
// Now that we have lock 0, the _locks array will not change (i.e., grow),
|
||||||
|
// and so we can safely read _locks.Length.
|
||||||
|
AcquireLocks(1, _tables.Locks.Length, ref locksAcquired);
|
||||||
|
Debug.Assert(locksAcquired == _tables.Locks.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcquireLocks(int fromInclusive, int toExclusive, ref int locksAcquired)
|
||||||
|
{
|
||||||
|
Debug.Assert(fromInclusive <= toExclusive);
|
||||||
|
var locks = _tables.Locks;
|
||||||
|
|
||||||
|
for (var i = fromInclusive; i < toExclusive; i++)
|
||||||
|
{
|
||||||
|
var lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Monitor.Enter(locks[i], ref lockTaken);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken)
|
||||||
|
{
|
||||||
|
locksAcquired++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleaseLocks(int fromInclusive, int toExclusive)
|
||||||
|
{
|
||||||
|
Debug.Assert(fromInclusive <= toExclusive);
|
||||||
|
|
||||||
|
for (var i = fromInclusive; i < toExclusive; i++)
|
||||||
|
{
|
||||||
|
Monitor.Exit(_tables.Locks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyToItems(T[] array, int index)
|
||||||
|
{
|
||||||
|
var buckets = _tables.Buckets;
|
||||||
|
for (var i = 0; i < buckets.Length; i++)
|
||||||
|
{
|
||||||
|
for (var current = buckets[i]; current != null; current = current.Next)
|
||||||
|
{
|
||||||
|
array[index] = current.Item;
|
||||||
|
index++; //this should never flow, CopyToItems is only called when there's no overflow risk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Tables
|
||||||
|
{
|
||||||
|
public readonly Node[] Buckets;
|
||||||
|
public readonly object[] Locks;
|
||||||
|
|
||||||
|
public volatile int[] CountPerLock;
|
||||||
|
|
||||||
|
public Tables(Node[] buckets, object[] locks, int[] countPerLock)
|
||||||
|
{
|
||||||
|
Buckets = buckets;
|
||||||
|
Locks = locks;
|
||||||
|
CountPerLock = countPerLock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Node
|
||||||
|
{
|
||||||
|
public readonly T Item;
|
||||||
|
public readonly int Hashcode;
|
||||||
|
|
||||||
|
public volatile Node Next;
|
||||||
|
|
||||||
|
public Node(T item, int hashcode, Node next)
|
||||||
|
{
|
||||||
|
Item = item;
|
||||||
|
Hashcode = hashcode;
|
||||||
|
Next = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/NadekoBot/DataStructures/PlatformHelper.cs
Normal file
25
src/NadekoBot/DataStructures/PlatformHelper.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConcurrentCollections
|
||||||
|
{
|
||||||
|
internal static class PlatformHelper
|
||||||
|
{
|
||||||
|
private const int ProcessorCountRefreshIntervalMs = 30000;
|
||||||
|
|
||||||
|
private static volatile int _processorCount;
|
||||||
|
private static volatile int _lastProcessorCountRefreshTicks;
|
||||||
|
|
||||||
|
internal static int ProcessorCount {
|
||||||
|
get {
|
||||||
|
var now = Environment.TickCount;
|
||||||
|
if (_processorCount == 0 || (now - _lastProcessorCountRefreshTicks) >= ProcessorCountRefreshIntervalMs)
|
||||||
|
{
|
||||||
|
_processorCount = Environment.ProcessorCount;
|
||||||
|
_lastProcessorCountRefreshTicks = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _processorCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
private string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) =>
|
private string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) =>
|
||||||
$"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content;
|
$"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content;
|
||||||
|
|
||||||
public static readonly ConcurrentDictionary<int, HashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, HashSet<ITextChannel>>();
|
public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
|
||||||
private Logger _log { get; }
|
private Logger _log { get; }
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -65,7 +65,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
var channel = (ITextChannel)msg.Channel;
|
var channel = (ITextChannel)msg.Channel;
|
||||||
var token = new NadekoRandom().Next();
|
var token = new NadekoRandom().Next();
|
||||||
var set = new HashSet<ITextChannel>();
|
var set = new ConcurrentHashSet<ITextChannel>();
|
||||||
if (Subscribers.TryAdd(token, set))
|
if (Subscribers.TryAdd(token, set))
|
||||||
{
|
{
|
||||||
set.Add(channel);
|
set.Add(channel);
|
||||||
@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
var channel = (ITextChannel)imsg.Channel;
|
var channel = (ITextChannel)imsg.Channel;
|
||||||
|
|
||||||
HashSet<ITextChannel> set;
|
ConcurrentHashSet<ITextChannel> set;
|
||||||
if (!Subscribers.TryGetValue(token, out set))
|
if (!Subscribers.TryGetValue(token, out set))
|
||||||
return;
|
return;
|
||||||
set.Add(channel);
|
set.Add(channel);
|
||||||
@ -96,7 +96,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
foreach (var subscriber in Subscribers)
|
foreach (var subscriber in Subscribers)
|
||||||
{
|
{
|
||||||
subscriber.Value.Remove(channel);
|
subscriber.Value.TryRemove(channel);
|
||||||
}
|
}
|
||||||
await channel.SendMessageAsync(":ok:").ConfigureAwait(false);
|
await channel.SendMessageAsync(":ok:").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using NadekoBot.Services.Database.Models;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NadekoBot.Modules.Administration.Commands.Migration;
|
using NadekoBot.Modules.Administration.Commands.Migration;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration
|
namespace NadekoBot.Modules.Administration
|
||||||
{
|
{
|
||||||
@ -99,12 +100,12 @@ namespace NadekoBot.Modules.Administration
|
|||||||
botConfig.RotatingStatusMessages = messages;
|
botConfig.RotatingStatusMessages = messages;
|
||||||
|
|
||||||
//races
|
//races
|
||||||
var races = new List<RaceAnimal>();
|
var races = new HashSet<RaceAnimal>();
|
||||||
oldData.RaceAnimals.ForEach(i => races.Add(new RaceAnimal() { Icon = i, Name = i }));
|
oldData.RaceAnimals.ForEach(i => races.Add(new RaceAnimal() { Icon = i, Name = i }));
|
||||||
botConfig.RaceAnimals = races;
|
botConfig.RaceAnimals = races;
|
||||||
|
|
||||||
//Prefix
|
//Prefix
|
||||||
var prefix = new List<ModulePrefix>
|
var prefix = new HashSet<ModulePrefix>
|
||||||
{
|
{
|
||||||
new ModulePrefix()
|
new ModulePrefix()
|
||||||
{
|
{
|
||||||
@ -158,7 +159,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
botConfig.Blacklist = new HashSet<BlacklistItem>(blacklist);
|
botConfig.Blacklist = new HashSet<BlacklistItem>(blacklist);
|
||||||
|
|
||||||
//Eightball
|
//Eightball
|
||||||
botConfig.EightBallResponses = oldData._8BallResponses.Select(response => new EightBallResponse() {Text = response}).ToList();
|
botConfig.EightBallResponses = new HashSet<EightBallResponse>(oldData._8BallResponses.Select(response => new EightBallResponse() {Text = response}));
|
||||||
|
|
||||||
//NOW save it
|
//NOW save it
|
||||||
botConfig.MigrationVersion = 1;
|
botConfig.MigrationVersion = 1;
|
||||||
|
@ -6,6 +6,7 @@ using NadekoBot.Services;
|
|||||||
using NadekoBot.Services.Database;
|
using NadekoBot.Services.Database;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -76,7 +77,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
var channel = (ITextChannel)umsg.Channel;
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
var toRemove = new HashSet<SelfAssignedRole>();
|
var toRemove = new ConcurrentHashSet<SelfAssignedRole>();
|
||||||
var removeMsg = new StringBuilder();
|
var removeMsg = new StringBuilder();
|
||||||
var msg = new StringBuilder();
|
var msg = new StringBuilder();
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
|
@ -17,15 +17,15 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
[NadekoModule("CustomReactions",".")]
|
[NadekoModule("CustomReactions",".")]
|
||||||
public class CustomReactions : DiscordModule
|
public class CustomReactions : DiscordModule
|
||||||
{
|
{
|
||||||
public static HashSet<CustomReaction> GlobalReactions { get; } = new HashSet<CustomReaction>();
|
public static ConcurrentHashSet<CustomReaction> GlobalReactions { get; } = new ConcurrentHashSet<CustomReaction>();
|
||||||
public static ConcurrentDictionary<ulong, HashSet<CustomReaction>> GuildReactions { get; } = new ConcurrentDictionary<ulong, HashSet<CustomReaction>>();
|
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>> GuildReactions { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>();
|
||||||
static CustomReactions()
|
static CustomReactions()
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var items = uow.CustomReactions.GetAll();
|
var items = uow.CustomReactions.GetAll();
|
||||||
GuildReactions = new ConcurrentDictionary<ulong, HashSet<CustomReaction>>(items.Where(g => g.GuildId != null).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new HashSet<CustomReaction>(g)));
|
GuildReactions = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(items.Where(g => g.GuildId != null).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new ConcurrentHashSet<CustomReaction>(g)));
|
||||||
GlobalReactions = new HashSet<CustomReaction>(items.Where(g => g.GuildId == null));
|
GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public CustomReactions(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
|
public CustomReactions(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
|
||||||
@ -43,7 +43,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
var t = Task.Run(async () =>
|
var t = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var content = umsg.Content.ToLowerInvariant();
|
var content = umsg.Content.ToLowerInvariant();
|
||||||
HashSet<CustomReaction> reactions;
|
ConcurrentHashSet<CustomReaction> reactions;
|
||||||
GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
|
GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
|
||||||
if (reactions != null && reactions.Any())
|
if (reactions != null && reactions.Any())
|
||||||
{
|
{
|
||||||
@ -101,7 +101,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var reactions = GuildReactions.GetOrAdd(channel.Guild.Id, new HashSet<CustomReaction>());
|
var reactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
|
||||||
reactions.Add(cr);
|
reactions.Add(cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,11 +115,11 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
|
|
||||||
if (page < 1 || page > 1000)
|
if (page < 1 || page > 1000)
|
||||||
return;
|
return;
|
||||||
HashSet<CustomReaction> customReactions;
|
ConcurrentHashSet<CustomReaction> customReactions;
|
||||||
if (channel == null)
|
if (channel == null)
|
||||||
customReactions = GlobalReactions;
|
customReactions = GlobalReactions;
|
||||||
else
|
else
|
||||||
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new HashSet<CustomReaction>());
|
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
|
||||||
|
|
||||||
if (customReactions == null || !customReactions.Any())
|
if (customReactions == null || !customReactions.Any())
|
||||||
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false);
|
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false);
|
||||||
@ -150,13 +150,16 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
if (toDelete.GuildId == null && channel == null)
|
if (toDelete.GuildId == null && channel == null)
|
||||||
{
|
{
|
||||||
uow.CustomReactions.Remove(toDelete);
|
uow.CustomReactions.Remove(toDelete);
|
||||||
GlobalReactions.RemoveWhere(cr => cr.Id == toDelete.Id);
|
var toRemove = GlobalReactions.FirstOrDefault(cr => cr.Id == toDelete.Id);
|
||||||
|
GlobalReactions.TryRemove(toRemove);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
else if (toDelete.GuildId != null && channel?.Guild.Id == toDelete.GuildId)
|
else if (toDelete.GuildId != null && channel?.Guild.Id == toDelete.GuildId)
|
||||||
{
|
{
|
||||||
uow.CustomReactions.Remove(toDelete);
|
uow.CustomReactions.Remove(toDelete);
|
||||||
GuildReactions.GetOrAdd(channel.Guild.Id, new HashSet<CustomReaction>()).RemoveWhere(cr => cr.Id == toDelete.Id);
|
var crs = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
|
||||||
|
var toRemove = crs.FirstOrDefault(cr => cr.Id == toDelete.Id);
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
if(success)
|
if(success)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -11,7 +12,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
public class TriviaQuestionPool
|
public class TriviaQuestionPool
|
||||||
{
|
{
|
||||||
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool();
|
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool();
|
||||||
public HashSet<TriviaQuestion> pool = new HashSet<TriviaQuestion>();
|
public ConcurrentHashSet<TriviaQuestion> pool = new ConcurrentHashSet<TriviaQuestion>();
|
||||||
|
|
||||||
private Random rng { get; } = new NadekoRandom();
|
private Random rng { get; } = new NadekoRandom();
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
pool.Add(tq);
|
pool.Add(tq);
|
||||||
}
|
}
|
||||||
var r = new NadekoRandom();
|
var r = new NadekoRandom();
|
||||||
pool = new HashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
|
pool = new ConcurrentHashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using NadekoBot.Services;
|
|||||||
using NadekoBot.Services.Database;
|
using NadekoBot.Services.Database;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -25,13 +26,13 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
[Group]
|
[Group]
|
||||||
public class BlacklistCommands
|
public class BlacklistCommands
|
||||||
{
|
{
|
||||||
public static HashSet<BlacklistItem> BlacklistedItems { get; set; } = new HashSet<BlacklistItem>();
|
public static ConcurrentHashSet<BlacklistItem> BlacklistedItems { get; set; } = new ConcurrentHashSet<BlacklistItem>();
|
||||||
|
|
||||||
static BlacklistCommands()
|
static BlacklistCommands()
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
BlacklistedItems = uow.BotConfig.GetOrCreate().Blacklist;
|
BlacklistedItems = new ConcurrentHashSet<BlacklistItem>(uow.BotConfig.GetOrCreate().Blacklist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +76,9 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
uow.BotConfig.GetOrCreate().Blacklist.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
|
uow.BotConfig.GetOrCreate().Blacklist.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
|
||||||
BlacklistedItems.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
|
var toRemove = BlacklistedItems.FirstOrDefault(bi => bi.ItemId == id && bi.Type == type);
|
||||||
|
if (toRemove != null)
|
||||||
|
BlacklistedItems.TryRemove(toRemove);
|
||||||
}
|
}
|
||||||
await uow.CompleteAsync().ConfigureAwait(false);
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -26,15 +26,15 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
[Group]
|
[Group]
|
||||||
public class CmdCdsCommands
|
public class CmdCdsCommands
|
||||||
{
|
{
|
||||||
public static ConcurrentDictionary<ulong, HashSet<CommandCooldown>> commandCooldowns { get; }
|
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> commandCooldowns { get; }
|
||||||
private static ConcurrentDictionary<ulong, HashSet<ActiveCooldown>> activeCooldowns = new ConcurrentDictionary<ulong, HashSet<ActiveCooldown>>();
|
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> activeCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();
|
||||||
|
|
||||||
static CmdCdsCommands()
|
static CmdCdsCommands()
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var configs = uow.GuildConfigs.GetAll();
|
var configs = uow.GuildConfigs.GetAll();
|
||||||
commandCooldowns = new ConcurrentDictionary<ulong, HashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => v.CommandCooldowns));
|
commandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => v.CommandCooldowns));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -51,7 +51,7 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var config = uow.GuildConfigs.For(channel.Guild.Id);
|
var config = uow.GuildConfigs.For(channel.Guild.Id);
|
||||||
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new HashSet<CommandCooldown>());
|
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||||
|
|
||||||
config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
|
config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
|
||||||
localSet.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
|
localSet.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
|
||||||
@ -69,7 +69,7 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
}
|
}
|
||||||
if (secs == 0)
|
if (secs == 0)
|
||||||
{
|
{
|
||||||
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new HashSet<ActiveCooldown>());
|
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>());
|
||||||
activeCds.RemoveWhere(ac => ac.Command == command.Text.ToLowerInvariant());
|
activeCds.RemoveWhere(ac => ac.Command == command.Text.ToLowerInvariant());
|
||||||
await channel.SendMessageAsync($"Command **{command}** has no coooldown now and all existing cooldowns have been cleared.").ConfigureAwait(false);
|
await channel.SendMessageAsync($"Command **{command}** has no coooldown now and all existing cooldowns have been cleared.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
public async Task AllCmdCooldowns(IUserMessage imsg)
|
public async Task AllCmdCooldowns(IUserMessage imsg)
|
||||||
{
|
{
|
||||||
var channel = (ITextChannel)imsg.Channel;
|
var channel = (ITextChannel)imsg.Channel;
|
||||||
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new HashSet<CommandCooldown>());
|
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||||
|
|
||||||
if (!localSet.Any())
|
if (!localSet.Any())
|
||||||
await channel.SendMessageAsync("`No command cooldowns set.`").ConfigureAwait(false);
|
await channel.SendMessageAsync("`No command cooldowns set.`").ConfigureAwait(false);
|
||||||
@ -94,11 +94,11 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
{
|
{
|
||||||
if (guild == null)
|
if (guild == null)
|
||||||
return false;
|
return false;
|
||||||
var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new HashSet<CommandCooldown>());
|
var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||||
CommandCooldown cdRule;
|
CommandCooldown cdRule;
|
||||||
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Text.ToLowerInvariant())) != null)
|
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Text.ToLowerInvariant())) != null)
|
||||||
{
|
{
|
||||||
var activeCdsForGuild = activeCooldowns.GetOrAdd(guild.Id, new HashSet<ActiveCooldown>());
|
var activeCdsForGuild = activeCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<ActiveCooldown>());
|
||||||
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == cmd.Text.ToLowerInvariant()) != null)
|
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == cmd.Text.ToLowerInvariant()) != null)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -17,26 +17,26 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
[Group]
|
[Group]
|
||||||
public class FilterCommands
|
public class FilterCommands
|
||||||
{
|
{
|
||||||
public static HashSet<ulong> InviteFilteringChannels { get; set; }
|
public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; set; }
|
||||||
public static HashSet<ulong> InviteFilteringServers { get; set; }
|
public static ConcurrentHashSet<ulong> InviteFilteringServers { get; set; }
|
||||||
|
|
||||||
//serverid, filteredwords
|
//serverid, filteredwords
|
||||||
private static ConcurrentDictionary<ulong, HashSet<string>> ServerFilteredWords { get; set; }
|
private static ConcurrentDictionary<ulong, ConcurrentHashSet<string>> ServerFilteredWords { get; set; }
|
||||||
|
|
||||||
public static HashSet<ulong> WordFilteringChannels { get; set; }
|
public static ConcurrentHashSet<ulong> WordFilteringChannels { get; set; }
|
||||||
public static HashSet<ulong> WordFilteringServers { get; set; }
|
public static ConcurrentHashSet<ulong> WordFilteringServers { get; set; }
|
||||||
|
|
||||||
public static HashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId)
|
public static ConcurrentHashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId)
|
||||||
{
|
{
|
||||||
HashSet<string> words = new HashSet<string>();
|
ConcurrentHashSet<string> words = new ConcurrentHashSet<string>();
|
||||||
if(WordFilteringChannels.Contains(channelId))
|
if(WordFilteringChannels.Contains(channelId))
|
||||||
ServerFilteredWords.TryGetValue(guildId, out words);
|
ServerFilteredWords.TryGetValue(guildId, out words);
|
||||||
return words;
|
return words;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HashSet<string> FilteredWordsForServer(ulong guildId)
|
public static ConcurrentHashSet<string> FilteredWordsForServer(ulong guildId)
|
||||||
{
|
{
|
||||||
var words = new HashSet<string>();
|
var words = new ConcurrentHashSet<string>();
|
||||||
if(WordFilteringServers.Contains(guildId))
|
if(WordFilteringServers.Contains(guildId))
|
||||||
ServerFilteredWords.TryGetValue(guildId, out words);
|
ServerFilteredWords.TryGetValue(guildId, out words);
|
||||||
return words;
|
return words;
|
||||||
@ -48,17 +48,17 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
{
|
{
|
||||||
var guildConfigs = uow.GuildConfigs.GetAll();
|
var guildConfigs = uow.GuildConfigs.GetAll();
|
||||||
|
|
||||||
InviteFilteringServers = new HashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
|
InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
|
||||||
InviteFilteringChannels = new HashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
|
InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
|
||||||
|
|
||||||
var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new HashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
|
var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
|
||||||
|
|
||||||
ServerFilteredWords = new ConcurrentDictionary<ulong, HashSet<string>>(dict);
|
ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
|
||||||
|
|
||||||
var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
|
var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
|
||||||
WordFilteringServers = new HashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
|
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
|
||||||
|
|
||||||
WordFilteringChannels = new HashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
|
WordFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,7 +205,7 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
await uow.CompleteAsync().ConfigureAwait(false);
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var filteredWords = ServerFilteredWords.GetOrAdd(channel.Guild.Id, new HashSet<string>());
|
var filteredWords = ServerFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<string>());
|
||||||
|
|
||||||
if (removed == 0)
|
if (removed == 0)
|
||||||
{
|
{
|
||||||
@ -227,7 +227,7 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
{
|
{
|
||||||
var channel = (ITextChannel)imsg.Channel;
|
var channel = (ITextChannel)imsg.Channel;
|
||||||
|
|
||||||
HashSet<string> filteredWords;
|
ConcurrentHashSet<string> filteredWords;
|
||||||
ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords);
|
ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords);
|
||||||
|
|
||||||
await channel.SendMessageAsync($"`List of banned words:`\n" + string.Join(",\n", filteredWords))
|
await channel.SendMessageAsync($"`List of banned words:`\n" + string.Join(",\n", filteredWords))
|
||||||
|
@ -140,10 +140,7 @@ namespace NadekoBot.Modules.Searches.IMDB
|
|||||||
List<string> list = new List<string>();
|
List<string> list = new List<string>();
|
||||||
string recUrl = "http://www.imdb.com/widget/recommendations/_ajax/get_more_recs?specs=p13nsims%3A" + mov.Id;
|
string recUrl = "http://www.imdb.com/widget/recommendations/_ajax/get_more_recs?specs=p13nsims%3A" + mov.Id;
|
||||||
string json = await GetUrlDataAsync(recUrl);
|
string json = await GetUrlDataAsync(recUrl);
|
||||||
list = MatchAll(@"title=\\""(.*?)\\""", json);
|
return MatchAll(@"title=\\""(.*?)\\""", json);
|
||||||
HashSet<String> set = new HashSet<string>();
|
|
||||||
foreach (String rec in list) set.Add(rec);
|
|
||||||
return new List<string>(set.ToList());
|
|
||||||
}
|
}
|
||||||
/*******************************[ Helper Methods ]********************************/
|
/*******************************[ Helper Methods ]********************************/
|
||||||
//Match single instance
|
//Match single instance
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
@ -19,7 +20,7 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
public float CurrencyGenerationChance { get; set; } = 0.02f;
|
public float CurrencyGenerationChance { get; set; } = 0.02f;
|
||||||
public int CurrencyGenerationCooldown { get; set; } = 10;
|
public int CurrencyGenerationCooldown { get; set; } = 10;
|
||||||
|
|
||||||
public List<ModulePrefix> ModulePrefixes { get; set; } = new List<ModulePrefix>();
|
public HashSet<ModulePrefix> ModulePrefixes { get; set; } = new HashSet<ModulePrefix>();
|
||||||
|
|
||||||
public List<PlayingStatus> RotatingStatusMessages { get; set; } = new List<PlayingStatus>();
|
public List<PlayingStatus> RotatingStatusMessages { get; set; } = new List<PlayingStatus>();
|
||||||
|
|
||||||
@ -31,8 +32,8 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
public string CurrencyName { get; set; } = "Nadeko Flower";
|
public string CurrencyName { get; set; } = "Nadeko Flower";
|
||||||
public string CurrencyPluralName { get; set; } = "Nadeko Flowers";
|
public string CurrencyPluralName { get; set; } = "Nadeko Flowers";
|
||||||
|
|
||||||
public List<EightBallResponse> EightBallResponses { get; set; } = new List<EightBallResponse>();
|
public HashSet<EightBallResponse> EightBallResponses { get; set; } = new HashSet<EightBallResponse>();
|
||||||
public List<RaceAnimal> RaceAnimals { get; set; } = new List<RaceAnimal>();
|
public HashSet<RaceAnimal> RaceAnimals { get; set; } = new HashSet<RaceAnimal>();
|
||||||
|
|
||||||
public string DMHelpString { get; set; } = "Type `-h` for help.";
|
public string DMHelpString { get; set; } = "Type `-h` for help.";
|
||||||
public string HelpString { get; set; } = @"To add me to your server, use this link -> <https://discordapp.com/oauth2/authorize?client_id={0}&scope=bot&permissions=66186303>
|
public string HelpString { get; set; } = @"To add me to your server, use this link -> <https://discordapp.com/oauth2/authorize?client_id={0}&scope=bot&permissions=66186303>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -37,7 +38,7 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
public bool VoicePlusTextEnabled { get; set; }
|
public bool VoicePlusTextEnabled { get; set; }
|
||||||
|
|
||||||
//stream notifications
|
//stream notifications
|
||||||
public List<FollowedStream> FollowedStreams { get; set; } = new List<FollowedStream>();
|
public HashSet<FollowedStream> FollowedStreams { get; set; } = new HashSet<FollowedStream>();
|
||||||
|
|
||||||
//currencyGeneration
|
//currencyGeneration
|
||||||
public ulong? GenerateCurrencyChannelId { get; set; }
|
public ulong? GenerateCurrencyChannelId { get; set; }
|
||||||
@ -51,11 +52,11 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
|
|
||||||
//filtering
|
//filtering
|
||||||
public bool FilterInvites { get; set; }
|
public bool FilterInvites { get; set; }
|
||||||
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; }
|
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new HashSet<FilterChannelId>();
|
||||||
|
|
||||||
public bool FilterWords { get; set; }
|
public bool FilterWords { get; set; }
|
||||||
public HashSet<FilteredWord> FilteredWords { get; set; }
|
public HashSet<FilteredWord> FilteredWords { get; set; } = new HashSet<FilteredWord>();
|
||||||
public HashSet<FilterChannelId> FilterWordsChannelIds { get; set; }
|
public HashSet<FilterChannelId> FilterWordsChannelIds { get; set; } = new HashSet<FilterChannelId>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FilterChannelId :DbEntity
|
public class FilterChannelId :DbEntity
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -10,7 +11,7 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
{
|
{
|
||||||
public bool IsLogging { get; set; }
|
public bool IsLogging { get; set; }
|
||||||
public ulong ChannelId { get; set; }
|
public ulong ChannelId { get; set; }
|
||||||
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; }
|
public ConcurrentHashSet<IgnoredLogChannel> IgnoredChannels { get; set; }
|
||||||
|
|
||||||
public bool MessageUpdated { get; set; } = true;
|
public bool MessageUpdated { get; set; } = true;
|
||||||
public bool MessageDeleted { get; set; } = true;
|
public bool MessageDeleted { get; set; } = true;
|
||||||
|
@ -47,7 +47,7 @@ namespace NadekoBot.Services.Database
|
|||||||
{
|
{
|
||||||
var bc = new BotConfig();
|
var bc = new BotConfig();
|
||||||
|
|
||||||
bc.ModulePrefixes.AddRange(new HashSet<ModulePrefix>()
|
bc.ModulePrefixes.AddRange(new ConcurrentHashSet<ModulePrefix>()
|
||||||
{
|
{
|
||||||
new ModulePrefix() { ModuleName = "Administration", Prefix = "." },
|
new ModulePrefix() { ModuleName = "Administration", Prefix = "." },
|
||||||
new ModulePrefix() { ModuleName = "Searches", Prefix = "~" },
|
new ModulePrefix() { ModuleName = "Searches", Prefix = "~" },
|
||||||
@ -64,7 +64,7 @@ namespace NadekoBot.Services.Database
|
|||||||
new ModulePrefix() { ModuleName = "Utility", Prefix = "." },
|
new ModulePrefix() { ModuleName = "Utility", Prefix = "." },
|
||||||
new ModulePrefix() { ModuleName = "CustomReactions", Prefix = "." }
|
new ModulePrefix() { ModuleName = "CustomReactions", Prefix = "." }
|
||||||
});
|
});
|
||||||
bc.RaceAnimals.AddRange(new HashSet<RaceAnimal>
|
bc.RaceAnimals.AddRange(new ConcurrentHashSet<RaceAnimal>
|
||||||
{
|
{
|
||||||
new RaceAnimal { Icon = "🐼", Name = "Panda" },
|
new RaceAnimal { Icon = "🐼", Name = "Panda" },
|
||||||
new RaceAnimal { Icon = "🐻", Name = "Bear" },
|
new RaceAnimal { Icon = "🐻", Name = "Bear" },
|
||||||
@ -75,7 +75,7 @@ namespace NadekoBot.Services.Database
|
|||||||
new RaceAnimal { Icon = "🦀", Name = "Crab" },
|
new RaceAnimal { Icon = "🦀", Name = "Crab" },
|
||||||
new RaceAnimal { Icon = "🦄", Name = "Unicorn" }
|
new RaceAnimal { Icon = "🦄", Name = "Unicorn" }
|
||||||
});
|
});
|
||||||
bc.EightBallResponses.AddRange(new HashSet<EightBallResponse>
|
bc.EightBallResponses.AddRange(new ConcurrentHashSet<EightBallResponse>
|
||||||
{
|
{
|
||||||
new EightBallResponse() { Text = "Most definitely yes" },
|
new EightBallResponse() { Text = "Most definitely yes" },
|
||||||
new EightBallResponse() { Text = "For sure" },
|
new EightBallResponse() { Text = "For sure" },
|
||||||
|
361
src/NadekoBot/data/typing_articles.json
Normal file
361
src/NadekoBot/data/typing_articles.json
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"Title":"The Gender of Psychology",
|
||||||
|
"Description":"This book addresses the diversity of psychological knowledge and practice through the lens of gender."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Unto Others: The Evolution and Psychology of Unselfish",
|
||||||
|
"Description":"In Unto Others philosopher Elliott Sober and biologist David Sloan Wilson demonstrate once and for all that unselfish behavior is in fact an important feature of both biological and human nature."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Forensic and Legal Psychology",
|
||||||
|
"Description":"Using research in clinical, cognitive, developmental, and social psychology, Forensic and Legal Psychology shows how psychological science can enhance the gathering and presentation of evidence, improve legal decision-making, prevent crime,"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"International Handbook of Psychology in Education",
|
||||||
|
"Description":"Suitable for researchers, practitioners and advisers working in the fields of psychology and education, this title presents an overview of the research within the domain of psychology of education."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Handbook of Personality Psychology",
|
||||||
|
"Description":"This comprehensive reference work on personality psychology discusses the development and measurement of personality, biological and social determinants, dynamic personality processes, the personality's relation to the self, and personality"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Dictionary of Theories, Laws, and Concepts in Psychology",
|
||||||
|
"Description":"A fully cross-referenced and source-referenced dictionary which gives definitions of psychological terms as well as the history, critique, and relevant references for the terms."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Essays on Plato's Psychology",
|
||||||
|
"Description":"With a comprehensive introduction to the major issues of Plato's psychology and an up-to-date bibliography of work on the relevant issues, this much-needed text makes the study of Plato's psychology accessible to scholars in ancient Greek"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology Statistics For Dummies",
|
||||||
|
"Description":"As an alternative to typical, lead-heavy statistics texts or supplements to assigned course reading, this is one book psychology students won't want to be without."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Doing Psychology Experiments",
|
||||||
|
"Description":"David W. Martinâs unique blend of informality, humor, clear instruction, and solid scholarship make this concise text a popular choice for research methods courses in psychology."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"A Handbook of Research Methods for Clinical and Health",
|
||||||
|
"Description":"For both undergraduate and postgraduate students, the book will be essential in making them aware of the full range of techniques available to them, helping them to design scientifically rigorous experiments."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"A History of Psychology",
|
||||||
|
"Description":"First published in 2002. Routledge is an imprint of Taylor & Francis, an informa company."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"An Introduction to the Psychology of Religion",
|
||||||
|
"Description":"The third edition of this successful book, which applies the science of psychology to problems of religion. Dr Thouless explores such questions as: why do people believe? Why are their beliefs often held with irrational strength?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology of Champions: How to Win at Sports and Life",
|
||||||
|
"Description":"In this unprecedented book, two psychologist researchers interview sports legends and super-athletes across sports to explain the thinking that powers stellar performers, pushing them to amazing and historic successes."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Humor: An Integrative Approach",
|
||||||
|
"Description":"This is a singly authored monograph that provides in one source, a summary of information researchers might wish to know about research into the psychology of humor."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology and Deterrence",
|
||||||
|
"Description":"Now available in paperback, Psychology and Deterrence reveals deterrence strategy's hidden and generally simplistic assumptions about the nature of power and aggression, threat and response, and calculation and behavior in the international"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology: An International Perspective",
|
||||||
|
"Description":"Unlike typical American texts, this book provides an international approach to introductory psychology, providing comprehensive and lively coverage of current research from a global perspective, including the UK, Germany, Scandinavia,"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology, Briefer Course",
|
||||||
|
"Description":"Despite its title, 'Psychology: Briefer Course' is more than a simple condensation of the great 'Principles of Psychology."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology, Seventh Edition (High School)",
|
||||||
|
"Description":"This new edition continues the story of psychology with added research and enhanced content from the most dynamic areas of the fieldâcognition, gender and diversity studies, neuroscience and more, while at the same time using the most"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology of Russia: Past, Present, Future",
|
||||||
|
"Description":"This book is for all psychologists and for readers whose interest in Russia exceeds their interest in psychology. Readers of this book will quickly discover a new world of thought."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Barron's AP Psychology",
|
||||||
|
"Description":"Provides information on scoring and structure of the test, offers tips on test-taking strategies, and includes practice examinations and subject review."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology for Inclusive Education: New Directions in",
|
||||||
|
"Description":"International in focus and at the very cutting edge of the field, this is essential reading for all those interested in the development of inclusive education."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Applied Psychology: Putting Theory Into Practice",
|
||||||
|
"Description":"Applied Psychology: Putting theory into practice demonstrates how psychology theory is applied in the real world."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Science: A Reconnaissance",
|
||||||
|
"Description":"' This eBook edition contains the complete 168 page text of the original 1966 hardcover edition. Contents: Preface by Abraham H. Maslow Acknowledgments 1. Mechanistic and Humanistic Science 2."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Filipino American Psychology: A Handbook of Theory,",
|
||||||
|
"Description":"This book is the first of its kind and aims to promote visibility of this invisible group, so that 2.4 million Filipino Americans will have their voices heard."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Visual Illusion",
|
||||||
|
"Description":"Well-rounded perspective on the ambiguities of visual display emphasizes geometrical optical illusions: framing and contrast effects, distortion of angles and direction, and apparent 'movement' of images. 240 drawings. 1972 edition."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Women",
|
||||||
|
"Description":"This highly respected text offers students an enjoyable, extraordinarily well-written introduction to the psychology of women with an up-to-date examination of the field and comprehensive coverage of topics."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology and Race",
|
||||||
|
"Description":"' Psychology and Race is divided into two major parts. The first half of the book looks at the interracial situation itself."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology for A-Level",
|
||||||
|
"Description":"'Precisely targeted at AQA A Level Psychology, specification A. It will also be of interest to those who are new to psychology, and who want to get a flavour of the kinds of topics in which psychologists are interested'--Preface, p. vii."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Biological Psychology",
|
||||||
|
"Description":"Updated with new topics, examples, and recent research findings--and supported by new online bio-labs, part of the strongest media package yet--this text speaks to todayâs students and instructors."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology: Concepts & Connections",
|
||||||
|
"Description":"The theme of this book is applying theories and research to learning and to contemporary life."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Adoption",
|
||||||
|
"Description":"In this volume David Brodzinsky, who has conducted one of the nation's largest studies of adopted children, and Marshall Schechter, a noted child psychiatrist who has been involved with adoption related issues for over forty years, have"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology and Adult Learning",
|
||||||
|
"Description":"This new edition is thoroughly revised and updated in light of the impact of globalising processes and the application of new information technologies, and the influence of postmodernism on psychology."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Gestalt Psychology: An Introduction to New Concepts in",
|
||||||
|
"Description":"The general reader, if he looks to psychology for something more than entertainment or practical advice, will discover in this book a storehouse of searching criticism and brilliant suggestions from the pen of a rare thinker, and one who"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Goals",
|
||||||
|
"Description":"Bringing together leading authorities, this tightly edited volume reviews the breadth of current knowledge about goals and their key role in human behavior."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Metaphors in the History of Psychology",
|
||||||
|
"Description":"Through the identification of these metaphors, the contributors to this volume have provided a remarkably useful guide to the history, current orientations, and future prospects of modern psychology."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Abnormal Psychology: An Integrative Approach",
|
||||||
|
"Description":"ABNORMAL PSYCHOLOGY: AN INTEGRATIVE APPROACH, Seventh Edition, is the perfect book to help you succeed in your abnormal psychology course!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Art and Visual Perception: A Psychology of the Creative Eye",
|
||||||
|
"Description":"Gestalt theory and the psychology of visual perception form the basis for an analysis of art and its basic elements"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology & Christianity: Five Views",
|
||||||
|
"Description":"This revised edition of a widely appreciated text now presents five models for understanding the relationship between psychology and Christianity."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Hope: You Can Get There from Here",
|
||||||
|
"Description":"Why do some people lead positive, hope-filled lives, while others wallow in pessimism? In The Psychology of Hope, a professor of psychology reveals the specific character traits that produce highly hopeful individuals."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Perspectives on Psychology",
|
||||||
|
"Description":"This is a title in the modular 'Principles in Psychology Series', designed for A-level and other introductory courses, aiming to provide students embarking on psychology courses with the necessary background and context."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology the Easy Way",
|
||||||
|
"Description":"Material is presented in a way that makes these books ideal as self-teaching guides, but Easy Way titles are also preferred by many teachers as supplements to classroom textbooks."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Ethics in Psychology: Professional Standards and Cases",
|
||||||
|
"Description":"In this book, their main intent is to present the full range of contemporary ethical issues in psychology as not only relevant and intriguing, but also as integral and unavoidable aspects of the profession."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology Gets in the Game: Sport, Mind, and Behavior,",
|
||||||
|
"Description":"The essays collected in this volume tell the stories not only of these psychologists and their subjects but of the social and academic context that surrounded them, shaping and being shaped by their ideas'--Provided by publisher."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology for Physical Educators: Student in Focus",
|
||||||
|
"Description":"This updated edition focuses on attitude and motivation as important aspects of the physical education curriculum, illustrating practical ideas and pedagogical solutions for any PE setting."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Leadership: New Perspectives and Research",
|
||||||
|
"Description":"In this book, some of the world's leading scholars come together to describe their thinking and research on the topic of the psychology of leadership."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Interpersonal Relations",
|
||||||
|
"Description":"As the title suggests, this book examines the psychology of interpersonal relations. In the context of this book, the term 'interpersonal relations' denotes relations between a few, usually between two, people."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Applied Psychology",
|
||||||
|
"Description":"The chapters on Counselling Psychology and Teaching Psychology are available online via the Student Companion Site at: http://tinyurl.com/c3ztvtj The text is written to be accessible to Level 1 Introductory Psychology students, and also to"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology",
|
||||||
|
"Description":"An exciting read for anyone interested in psychology and research; because of its comprehensive appendix, glossary, and reference section, this book is a must-have desk reference for psychologists and others in the field."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Music",
|
||||||
|
"Description":"On interpreting musical phenomena in terms of mental function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Abnormal Psychology",
|
||||||
|
"Description":"Ron Comer's Abnormal Psychology continues to captivate students with its integrated coverage of theory, diagnosis, and treatment, its inclusive wide-ranging cross-cultural perspective, and its compassionate emphasis on the real impact of"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Food Choice",
|
||||||
|
"Description":"This book brings together theory, research and applications from psychology and behavioural sciences applied to dietary behaviour."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology: brain, behavior, & culture",
|
||||||
|
"Description":"Rather than present psychological science as a series of facts for memorization, this book takes readers on a psychological journey that uncovers things they didn't know or new ways of thinking about things they did know."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"A Brief History of Psychology",
|
||||||
|
"Description":"Due to its brevity and engaging style, the book is often used in introductory courses to introduce students to the field. The enormous index and substantial glossary make this volume a useful desk reference for the entire field."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology AS: The Complete Companion",
|
||||||
|
"Description":"Presented in double-page spreads this book written to the average AS ability level, provides information on psychology in bite-sized chunks with learning and revision features."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology Book: From Shamanism to Cutting-Edge",
|
||||||
|
"Description":"Lavishly illustrated, this new addition in the Sterling's Milestones series chronicles the history of psychology through 250 groundbreaking events, theories, publications, experiments and discoveries."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology Book",
|
||||||
|
"Description":"All the big ideas, simply explained - an innovative and accessible guide to the study of human nature The Psychology Book clearly explains more than 100 groundbreaking ideas in this fascinating field of science."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Handbook of Positive Psychology",
|
||||||
|
"Description":"' The Handbook of Positive Psychology provides a forum for a more positive view of the human condition. In its pages, readers are treated to an analysis of what the foremost experts believe to be the fundamental strengths of humankind."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology of Sustainable Development",
|
||||||
|
"Description":"With contributions from an international team of policy shapers and makers, the book will be an important reference for environmental, developmental, social, and organizational psychologists, in addition to other social scientists concerned"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"An Introduction to the History of Psychology",
|
||||||
|
"Description":"In this Fifth Edition, B.R. Hergenhahn demonstrates that most of the concerns of contemporary psychologists are manifestations of themes that have been part of psychology for hundreds-or even thousands-of years."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Careers in Psychology: Opportunities in a Changing World",
|
||||||
|
"Description":"This text addresses the growing need among students and faculty for information about the careers available in psychology at the bachelorâs and graduate level."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Philosophy of Psychology",
|
||||||
|
"Description":"This is the story of the clattering of elevated subways and the cacophony of crowded neighborhoods, the heady optimism of industrial progress and the despair of economic recession, and the vibrancy of ethnic cultures and the resilience of"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Risk Taking Behavior",
|
||||||
|
"Description":"This book aims to help the reader to understand what motivates people to engage in risk taking behavior, such as participating in traffic, sports, financial investments, or courtship."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Nazi Doctors: Medical Killing and the Psychology of",
|
||||||
|
"Description":"This book explores the psychological conditions that promote the human potential for evil, relating medical killing to broader principles of doubling and genocide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Body and Psychology",
|
||||||
|
"Description":"The material in this volume was previously published as a Special Issue of th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Introduction to Psychology: Gateways to Mind and Behavior",
|
||||||
|
"Description":"Important Notice: Media content referenced within the product description or the product text may not be available in the ebook version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology of Time",
|
||||||
|
"Description":"Basic Structure The book would contain 14 or 15 chapters of roughly 12 000 words. The exact final number of chapters would depend on further discussions with you about the book's basic structure."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Handbook of Psychology, Experimental Psychology",
|
||||||
|
"Description":"Includes established theories and cutting-edge developments. Presents the work of an international group of experts. Presents the nature, origin, implications, and future course of major unresolved issues in the area."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Study Guide for Psychology, Seventh Edition",
|
||||||
|
"Description":"This new edition continues the story of psychology with added research and enhanced content from the most dynamic areas of the field--cognition, gender and diversity studies, neuroscience and more, while at the same time using the most"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Culture and Psychology",
|
||||||
|
"Description":"In addition, the text encourages students to question traditionally held beliefs and theories as and their relevance to different cultural groups today."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Exploring the Psychology of Interest",
|
||||||
|
"Description":"The most comprehensive work of its kind, Exploring the Psychology of Interest will be a valuable resource for student and professional researchers in cognitive, social, and developmental psychology."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Handbook of Adolescent Psychology",
|
||||||
|
"Description":"The study of adolescence in the field of psychology has grown tremendously over the last two decades, necessitating a comprehensive and up-to-date revision of this seminal work."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Diplomacy",
|
||||||
|
"Description":"World class clinicians, researchers, and activists present the psychological dimensions to diplomacy drawn from examples set in the United Nations, Camp David, the Middle East, Japan, South Africa, and elsewhere."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"The Psychology of Social Class",
|
||||||
|
"Description":"By addressing differences in social class, the book broadens the perspective of social psychological research to examine such topics as the effect of achievement motivation and other personality variables on social mobility and the effect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Applied Psychology: Current Issues and New Directions",
|
||||||
|
"Description":"Key features of this book: - Consistently pedagogical throughout - chapter summaries, questions for reflection and discussion and annotated further reading in every chapter - Comprehensive coverage - all areas of applied psychology included"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Popular Psychology: An Encyclopedia",
|
||||||
|
"Description":"Entries cover a variety of topics in the field of popular psychology, including acupuncture, emotional intelligence, brainwashing, chemical inbalance, and seasonal affective disorder."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Advanced Psychology: Applications, Issues and Perspectives",
|
||||||
|
"Description":"The second of two books, Advanced Psychology covers units 4 to 6 for the second year at Advanced Level."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Mindset: The New Psychology of Success",
|
||||||
|
"Description":"This is a book that can change your life, as its ideas have changed mine.ââRobert J. Sternberg, IBM Professor of Education and Psychology at Yale University, director of the PACE Center of Yale University, and author of Successful"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"E-Z Psychology",
|
||||||
|
"Description":"This book covers material as it is taught on a college-101 level."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Myers' Psychology for AP*",
|
||||||
|
"Description":"Already The Bestselling AP* Psychology Author, Myers Writes His First Exclusive AP* Psych Text Watch Dave G. Myers introduce this new text here."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology and Health",
|
||||||
|
"Description":"Part of a series of textbooks which have been written to support A levels in psychology. The books use real life applications to make theories come alive for students and teach them what they need to know."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Applying Psychology in Business: The Handbook for Managers",
|
||||||
|
"Description":"To learn more about Rowman & Littlefield titles please visit us at www.rowmanlittlefield.com."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Influence",
|
||||||
|
"Description":"Influence, the classic book on persuasion, explains the psychology of why people say 'yes'âand how to apply these understandings. Dr. Robert Cialdini is the seminal expert in the rapidly expanding field of influence and persuasion."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology and Policing",
|
||||||
|
"Description":"The book should draw attention to the often unrecognized and valuable contribution that mainstream psychology can make to the knowledge base underpinning a wide variety of policing practices."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Applied Psychology: New Frontiers and Rewarding Careers",
|
||||||
|
"Description":"This book examines how psychological science is, and can be, used to prevent and ameliorate pressing human problems to promote positive social change."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology: Concepts and Applications",
|
||||||
|
"Description":"Nevid developed the effective teaching devices in this text based on a comprehensive system derived from research on learning and memory as well as his own research on textbook pedagogy."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Foundations of Sport and Exercise Psychology, 6E: ",
|
||||||
|
"Description":"This text offers both students and new practitioners a comprehensive view of sport and exercise psychology, drawing connections between research and practice and capturing the excitement of the world of sport and exercise."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Biographical Dictionary of Psychology",
|
||||||
|
"Description":"This Dictionary provides biographical and bibliographical information on over 500 psychologists from all over the world from 1850 to the present day. All branches of psychology and its related disciplines are featured."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"Psychology: A Self-Teaching Guide",
|
||||||
|
"Description":"Frank Bruno explains all the major psychological theories and terms in this book, covering perception, motivation, thinking, personality, sensation, intelligence, research methods, and much more."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"A Dictionary of Psychology",
|
||||||
|
"Description":"Entries are extensively cross-referenced for ease of use, and cover word origins and derivations as well as definitions. Over 80 illustrations complement the text."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title":"An Intellectual History of Psychology",
|
||||||
|
"Description":"Invaluable as a text for students and as a stimulating and insightful overview for scholars and practicing psychologists, this volume can be read either as a history of psychology in both its philosophical and aspiring scientific periods or"
|
||||||
|
}]
|
Loading…
Reference in New Issue
Block a user