mirror of
https://github.com/SFML/CSFML.git
synced 2026-10-27 21:36:13 +01:00
Add readiness types and callbacks to sfSocketSelector
This commit is contained in:
4 files changed
+278
-22
No files matched your search
@@ -32,6 +32,31 @@
|
|||||||
#include <CSFML/Network/Types.h>
|
#include <CSFML/Network/Types.h>
|
||||||
#include <CSFML/System/Time.h>
|
#include <CSFML/System/Time.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Readiness types a socket selector can check for
|
||||||
|
///
|
||||||
|
/// The values can be combined with a bitwise OR.
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
sfSocketSelectorReceive = 1 << 0, ///< Check if sockets are ready to be received from
|
||||||
|
sfSocketSelectorSend = 1 << 1 ///< Check if sockets are ready to be sent to
|
||||||
|
} sfSocketSelectorReadiness;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Callback that is called when a socket is ready
|
||||||
|
///
|
||||||
|
/// \param readiness Readiness of the socket, a combination of sfSocketSelectorReadiness values
|
||||||
|
/// \param userData User data passed when adding the socket
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
typedef void (*sfSocketSelectorCallback)(uint32_t readiness, void* userData);
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Create a new selector
|
/// \brief Create a new selector
|
||||||
@@ -66,13 +91,64 @@ CSFML_NETWORK_API void sfSocketSelector_destroy(const sfSocketSelector* selector
|
|||||||
/// so you have to make sure that the socket is not destroyed
|
/// so you have to make sure that the socket is not destroyed
|
||||||
/// while it is stored in the selector.
|
/// while it is stored in the selector.
|
||||||
///
|
///
|
||||||
|
/// The socket is checked for being ready to receive.
|
||||||
|
///
|
||||||
/// \param selector Socket selector object
|
/// \param selector Socket selector object
|
||||||
/// \param socket Pointer to the socket to add
|
/// \param socket Pointer to the socket to add
|
||||||
///
|
///
|
||||||
|
/// \return True if the socket was successfully added
|
||||||
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
CSFML_NETWORK_API void sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket);
|
||||||
CSFML_NETWORK_API void sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket);
|
||||||
CSFML_NETWORK_API void sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Add a new socket to a socket selector with the readiness to check for and an optional callback
|
||||||
|
///
|
||||||
|
/// This function keeps a weak pointer to the socket,
|
||||||
|
/// so you have to make sure that the socket is not destroyed
|
||||||
|
/// while it is stored in the selector.
|
||||||
|
///
|
||||||
|
/// The readiness to wait for can be specified as a combination
|
||||||
|
/// of sfSocketSelectorReceive and sfSocketSelectorSend.
|
||||||
|
///
|
||||||
|
/// If a callback is provided, it will be called with the
|
||||||
|
/// readiness of the socket when sfSocketSelector_dispatchReadyCallbacks
|
||||||
|
/// is called after sfSocketSelector_wait returned true. Using
|
||||||
|
/// callbacks scales better than checking every socket with
|
||||||
|
/// sfSocketSelector_isXxxReadyFor.
|
||||||
|
///
|
||||||
|
/// Adding a socket that has already been added updates its
|
||||||
|
/// readiness and callback.
|
||||||
|
///
|
||||||
|
/// \param selector Socket selector object
|
||||||
|
/// \param socket Pointer to the socket to add
|
||||||
|
/// \param readiness Readiness to wait for, combination of sfSocketSelectorReadiness values
|
||||||
|
/// \param callback Callback to call when the socket is ready, can be NULL
|
||||||
|
/// \param userData User data that will be passed to the callback
|
||||||
|
///
|
||||||
|
/// \return True if the socket was successfully added
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
CSFML_NETWORK_API bool sfSocketSelector_addTcpListenerWithReadiness(
|
||||||
|
sfSocketSelector* selector,
|
||||||
|
sfTcpListener* socket,
|
||||||
|
uint32_t readiness,
|
||||||
|
sfSocketSelectorCallback callback,
|
||||||
|
void* userData);
|
||||||
|
CSFML_NETWORK_API bool sfSocketSelector_addTcpSocketWithReadiness(
|
||||||
|
sfSocketSelector* selector,
|
||||||
|
sfTcpSocket* socket,
|
||||||
|
uint32_t readiness,
|
||||||
|
sfSocketSelectorCallback callback,
|
||||||
|
void* userData);
|
||||||
|
CSFML_NETWORK_API bool sfSocketSelector_addUdpSocketWithReadiness(
|
||||||
|
sfSocketSelector* selector,
|
||||||
|
sfUdpSocket* socket,
|
||||||
|
uint32_t readiness,
|
||||||
|
sfSocketSelectorCallback callback,
|
||||||
|
void* userData);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Remove a socket from a socket selector
|
/// \brief Remove a socket from a socket selector
|
||||||
@@ -83,10 +159,12 @@ CSFML_NETWORK_API void sfSocketSelector_addUdpSocket(sfSocketSelector* selector,
|
|||||||
/// \param selector Socket selector object
|
/// \param selector Socket selector object
|
||||||
/// \param socket Pointer to the socket to remove
|
/// \param socket Pointer to the socket to remove
|
||||||
///
|
///
|
||||||
|
/// \return True if the socket was successfully removed
|
||||||
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
CSFML_NETWORK_API void sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket);
|
||||||
CSFML_NETWORK_API void sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket);
|
||||||
CSFML_NETWORK_API void sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Remove all the sockets stored in a selector
|
/// \brief Remove all the sockets stored in a selector
|
||||||
@@ -101,11 +179,13 @@ CSFML_NETWORK_API void sfSocketSelector_removeUdpSocket(sfSocketSelector* select
|
|||||||
CSFML_NETWORK_API void sfSocketSelector_clear(sfSocketSelector* selector);
|
CSFML_NETWORK_API void sfSocketSelector_clear(sfSocketSelector* selector);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Wait until one or more sockets are ready to receive
|
/// \brief Wait until one or more sockets are ready
|
||||||
///
|
///
|
||||||
/// This function returns as soon as at least one socket has
|
/// This function returns as soon as at least one socket is
|
||||||
/// some data available to be received. To know which sockets are
|
/// ready for the readiness it was added with (by default
|
||||||
/// ready, use the sfSocketSelector_isXxxReady functions.
|
/// having some data available to be received). To know which
|
||||||
|
/// sockets are ready, use the sfSocketSelector_isXxxReady
|
||||||
|
/// functions or sfSocketSelector_dispatchReadyCallbacks.
|
||||||
/// If you use a timeout and no socket is ready before the timeout
|
/// If you use a timeout and no socket is ready before the timeout
|
||||||
/// is over, the function returns false.
|
/// is over, the function returns false.
|
||||||
///
|
///
|
||||||
@@ -136,3 +216,41 @@ CSFML_NETWORK_API bool sfSocketSelector_wait(sfSocketSelector* selector, sfTime
|
|||||||
CSFML_NETWORK_API bool sfSocketSelector_isTcpListenerReady(const sfSocketSelector* selector, sfTcpListener* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_isTcpListenerReady(const sfSocketSelector* selector, sfTcpListener* socket);
|
||||||
CSFML_NETWORK_API bool sfSocketSelector_isTcpSocketReady(const sfSocketSelector* selector, sfTcpSocket* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_isTcpSocketReady(const sfSocketSelector* selector, sfTcpSocket* socket);
|
||||||
CSFML_NETWORK_API bool sfSocketSelector_isUdpSocketReady(const sfSocketSelector* selector, sfUdpSocket* socket);
|
CSFML_NETWORK_API bool sfSocketSelector_isUdpSocketReady(const sfSocketSelector* selector, sfUdpSocket* socket);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Test a socket to know if it is ready for the given readiness
|
||||||
|
///
|
||||||
|
/// This function must be used after a call to
|
||||||
|
/// sfSocketSelector_wait, to know which sockets are ready.
|
||||||
|
///
|
||||||
|
/// \param selector Socket selector object
|
||||||
|
/// \param socket Socket to test
|
||||||
|
/// \param readiness Readiness to check for, combination of sfSocketSelectorReadiness values
|
||||||
|
///
|
||||||
|
/// \return true if the socket is ready, false otherwise
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
CSFML_NETWORK_API bool sfSocketSelector_isTcpListenerReadyFor(const sfSocketSelector* selector,
|
||||||
|
sfTcpListener* socket,
|
||||||
|
uint32_t readiness);
|
||||||
|
CSFML_NETWORK_API bool sfSocketSelector_isTcpSocketReadyFor(const sfSocketSelector* selector,
|
||||||
|
sfTcpSocket* socket,
|
||||||
|
uint32_t readiness);
|
||||||
|
CSFML_NETWORK_API bool sfSocketSelector_isUdpSocketReadyFor(const sfSocketSelector* selector,
|
||||||
|
sfUdpSocket* socket,
|
||||||
|
uint32_t readiness);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Call the callbacks of all sockets that are ready
|
||||||
|
///
|
||||||
|
/// After sfSocketSelector_wait returned true, at least one
|
||||||
|
/// socket is ready. Calling this function will call the
|
||||||
|
/// callbacks of all the sockets that became ready during
|
||||||
|
/// the wait. Calling this function multiple times after a
|
||||||
|
/// single call to sfSocketSelector_wait will run the callbacks
|
||||||
|
/// multiple times.
|
||||||
|
///
|
||||||
|
/// \param selector Socket selector object
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
CSFML_NETWORK_API void sfSocketSelector_dispatchReadyCallbacks(sfSocketSelector* selector);
|
||||||
@@ -31,6 +31,22 @@
|
|||||||
#include <CSFML/Network/TcpSocketStruct.hpp>
|
#include <CSFML/Network/TcpSocketStruct.hpp>
|
||||||
#include <CSFML/Network/UdpSocketStruct.hpp>
|
#include <CSFML/Network/UdpSocketStruct.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
[[nodiscard]] std::function<void(sf::SocketSelector::ReadinessType)> convertCallback(sfSocketSelectorCallback callback,
|
||||||
|
void* userData)
|
||||||
|
{
|
||||||
|
if (!callback)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return [callback, userData](sf::SocketSelector::ReadinessType readiness) { callback(readiness, userData); };
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
sfSocketSelector* sfSocketSelector_create()
|
sfSocketSelector* sfSocketSelector_create()
|
||||||
@@ -55,44 +71,86 @@ void sfSocketSelector_destroy(const sfSocketSelector* selector)
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket)
|
bool sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket)
|
||||||
{
|
{
|
||||||
assert(selector);
|
assert(selector);
|
||||||
assert(socket);
|
assert(socket);
|
||||||
selector->add(*socket);
|
return selector->add(*socket);
|
||||||
}
|
}
|
||||||
void sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket)
|
bool sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket)
|
||||||
{
|
{
|
||||||
assert(selector);
|
assert(selector);
|
||||||
assert(socket);
|
assert(socket);
|
||||||
selector->add(*socket);
|
return selector->add(*socket);
|
||||||
}
|
}
|
||||||
void sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket)
|
bool sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket)
|
||||||
{
|
{
|
||||||
assert(selector);
|
assert(selector);
|
||||||
assert(socket);
|
assert(socket);
|
||||||
selector->add(*socket);
|
return selector->add(*socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket)
|
bool sfSocketSelector_addTcpListenerWithReadiness(
|
||||||
|
sfSocketSelector* selector,
|
||||||
|
sfTcpListener* socket,
|
||||||
|
uint32_t readiness,
|
||||||
|
sfSocketSelectorCallback callback,
|
||||||
|
void* userData)
|
||||||
{
|
{
|
||||||
assert(selector);
|
assert(selector);
|
||||||
assert(socket);
|
assert(socket);
|
||||||
selector->remove(*socket);
|
return selector->add(*socket, readiness, convertCallback(callback, userData));
|
||||||
}
|
}
|
||||||
void sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket)
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool sfSocketSelector_addTcpSocketWithReadiness(
|
||||||
|
sfSocketSelector* selector,
|
||||||
|
sfTcpSocket* socket,
|
||||||
|
uint32_t readiness,
|
||||||
|
sfSocketSelectorCallback callback,
|
||||||
|
void* userData)
|
||||||
{
|
{
|
||||||
assert(selector);
|
assert(selector);
|
||||||
assert(socket);
|
assert(socket);
|
||||||
selector->remove(*socket);
|
return selector->add(*socket, readiness, convertCallback(callback, userData));
|
||||||
}
|
}
|
||||||
void sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket)
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool sfSocketSelector_addUdpSocketWithReadiness(
|
||||||
|
sfSocketSelector* selector,
|
||||||
|
sfUdpSocket* socket,
|
||||||
|
uint32_t readiness,
|
||||||
|
sfSocketSelectorCallback callback,
|
||||||
|
void* userData)
|
||||||
{
|
{
|
||||||
assert(selector);
|
assert(selector);
|
||||||
assert(socket);
|
assert(socket);
|
||||||
selector->remove(*socket);
|
return selector->add(*socket, readiness, convertCallback(callback, userData));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket)
|
||||||
|
{
|
||||||
|
assert(selector);
|
||||||
|
assert(socket);
|
||||||
|
return selector->remove(*socket);
|
||||||
|
}
|
||||||
|
bool sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket)
|
||||||
|
{
|
||||||
|
assert(selector);
|
||||||
|
assert(socket);
|
||||||
|
return selector->remove(*socket);
|
||||||
|
}
|
||||||
|
bool sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket)
|
||||||
|
{
|
||||||
|
assert(selector);
|
||||||
|
assert(socket);
|
||||||
|
return selector->remove(*socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -131,3 +189,38 @@ bool sfSocketSelector_isUdpSocketReady(const sfSocketSelector* selector, sfUdpSo
|
|||||||
assert(socket);
|
assert(socket);
|
||||||
return selector->isReady(*socket);
|
return selector->isReady(*socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool sfSocketSelector_isTcpListenerReadyFor(const sfSocketSelector* selector, sfTcpListener* socket, uint32_t readiness)
|
||||||
|
{
|
||||||
|
assert(selector);
|
||||||
|
assert(socket);
|
||||||
|
return selector->isReady(*socket, readiness);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool sfSocketSelector_isTcpSocketReadyFor(const sfSocketSelector* selector, sfTcpSocket* socket, uint32_t readiness)
|
||||||
|
{
|
||||||
|
assert(selector);
|
||||||
|
assert(socket);
|
||||||
|
return selector->isReady(*socket, readiness);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool sfSocketSelector_isUdpSocketReadyFor(const sfSocketSelector* selector, sfUdpSocket* socket, uint32_t readiness)
|
||||||
|
{
|
||||||
|
assert(selector);
|
||||||
|
assert(socket);
|
||||||
|
return selector->isReady(*socket, readiness);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void sfSocketSelector_dispatchReadyCallbacks(sfSocketSelector* selector)
|
||||||
|
{
|
||||||
|
assert(selector);
|
||||||
|
selector->dispatchReadyCallbacks();
|
||||||
|
}
|
||||||
@@ -70,6 +70,7 @@ add_executable(test-csfml-network
|
|||||||
Network/Ftp.test.cpp
|
Network/Ftp.test.cpp
|
||||||
Network/Http.test.cpp
|
Network/Http.test.cpp
|
||||||
Network/IpAddress.test.cpp
|
Network/IpAddress.test.cpp
|
||||||
|
Network/SocketSelector.test.cpp
|
||||||
Network/SocketStatus.test.cpp
|
Network/SocketStatus.test.cpp
|
||||||
Network/TcpSocket.test.cpp
|
Network/TcpSocket.test.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#include <CSFML/Network/SocketSelector.h>
|
||||||
|
#include <CSFML/Network/TcpListener.h>
|
||||||
|
#include <CSFML/Network/TcpSocket.h>
|
||||||
|
|
||||||
|
#include <SFML/Network/SocketSelector.hpp>
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
TEST_CASE("[Network] sfSocketSelector")
|
||||||
|
{
|
||||||
|
SECTION("sfSocketSelectorReadiness")
|
||||||
|
{
|
||||||
|
STATIC_CHECK(sfSocketSelectorReceive == static_cast<int>(sf::SocketSelector::Receive));
|
||||||
|
STATIC_CHECK(sfSocketSelectorSend == static_cast<int>(sf::SocketSelector::Send));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Ready callbacks")
|
||||||
|
{
|
||||||
|
sfTcpListener* listener = sfTcpListener_create();
|
||||||
|
REQUIRE(sfTcpListener_listen(listener, 0, sfIpAddress_LocalHost) == sfSocketDone);
|
||||||
|
|
||||||
|
sfTcpSocket* client = sfTcpSocket_create();
|
||||||
|
REQUIRE(sfTcpSocket_connect(client, sfIpAddress_LocalHost, sfTcpListener_getLocalPort(listener), sfSeconds(1)) ==
|
||||||
|
sfSocketDone);
|
||||||
|
|
||||||
|
sfSocketSelector* selector = sfSocketSelector_create();
|
||||||
|
|
||||||
|
uint32_t readiness = 0;
|
||||||
|
const auto callback = [](uint32_t ready, void* userData) { *static_cast<uint32_t*>(userData) |= ready; };
|
||||||
|
CHECK(sfSocketSelector_addTcpListenerWithReadiness(selector, listener, sfSocketSelectorReceive, callback, &readiness));
|
||||||
|
|
||||||
|
REQUIRE(sfSocketSelector_wait(selector, sfSeconds(1)));
|
||||||
|
CHECK(sfSocketSelector_isTcpListenerReady(selector, listener));
|
||||||
|
CHECK(sfSocketSelector_isTcpListenerReadyFor(selector, listener, sfSocketSelectorReceive));
|
||||||
|
sfSocketSelector_dispatchReadyCallbacks(selector);
|
||||||
|
CHECK(readiness == sfSocketSelectorReceive);
|
||||||
|
|
||||||
|
CHECK(sfSocketSelector_removeTcpListener(selector, listener));
|
||||||
|
|
||||||
|
sfSocketSelector_destroy(selector);
|
||||||
|
sfTcpSocket_destroy(client);
|
||||||
|
sfTcpListener_destroy(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user