convert rmm into an interface
+ minor cleanups
This commit is contained in:
parent
9728f71c98
commit
3e6c857c8a
@ -1,10 +1,4 @@
|
||||
add_library(solanaceae_message3
|
||||
#./solanaceae/message3/file.hpp
|
||||
#./solanaceae/message3/file_r_mem.hpp
|
||||
#./solanaceae/message3/file_r_file.hpp
|
||||
#./solanaceae/message3/file_w_file.hpp
|
||||
#./solanaceae/message3/file_rw_file.hpp
|
||||
|
||||
./solanaceae/message3/message.hpp
|
||||
./solanaceae/message3/components.hpp
|
||||
./solanaceae/message3/components_id.inl
|
||||
@ -13,6 +7,8 @@ add_library(solanaceae_message3
|
||||
|
||||
./solanaceae/message3/registry_message_model.hpp
|
||||
./solanaceae/message3/registry_message_model.cpp
|
||||
./solanaceae/message3/registry_message_model_impl.hpp
|
||||
./solanaceae/message3/registry_message_model_impl.cpp
|
||||
|
||||
./solanaceae/message3/contact_components.hpp
|
||||
|
||||
|
@ -87,28 +87,11 @@ namespace Message::Components {
|
||||
//failed,
|
||||
//} state = paused;
|
||||
//};
|
||||
struct TagHaveAll {};
|
||||
|
||||
struct BytesSent {
|
||||
uint64_t total {0u};
|
||||
};
|
||||
|
||||
struct BytesReceived {
|
||||
uint64_t total {0u};
|
||||
};
|
||||
|
||||
// TODO: rename to stream?
|
||||
using File = std::unique_ptr<File2I>;
|
||||
|
||||
struct TagReceiving {};
|
||||
struct TagSending {};
|
||||
// TODO: add both?
|
||||
|
||||
// convert to enum?
|
||||
// TODO: local/remote
|
||||
// TODO: invert?
|
||||
struct TagPaused {};
|
||||
|
||||
struct StateCanceled {
|
||||
enum Reason {
|
||||
disconnected,
|
||||
@ -131,28 +114,6 @@ namespace Message::Components {
|
||||
};
|
||||
#endif
|
||||
|
||||
struct FileInfo {
|
||||
struct FileDirEntry {
|
||||
std::string file_name; // full path relative to base
|
||||
uint64_t file_size {0};
|
||||
};
|
||||
std::vector<FileDirEntry> file_list;
|
||||
uint64_t total_size {0};
|
||||
};
|
||||
|
||||
// describes the files locally
|
||||
// filename might be different to non local FileInfo
|
||||
// order is the same
|
||||
struct FileInfoLocal {
|
||||
std::vector<std::string> file_list;
|
||||
};
|
||||
|
||||
// TODO: rename to start? or set or ...
|
||||
struct ActionAccept {
|
||||
std::string save_to_path;
|
||||
bool path_is_file = false; // if the path is not the folder to place the file into, overwrites the name
|
||||
};
|
||||
|
||||
} // Transfer
|
||||
#endif
|
||||
|
||||
|
@ -33,17 +33,9 @@ DEFINE_COMP_ID(Message::Components::TagMessageIsAction)
|
||||
DEFINE_COMP_ID(Message::Components::MessageFileObject)
|
||||
|
||||
#if 0
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::TagHaveAll)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::BytesSent)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::BytesReceived)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::File)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::TagReceiving)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::TagSending)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::TagPaused)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::StateCanceled)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::FileInfo)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::FileInfoLocal)
|
||||
DEFINE_COMP_ID(Message::Components::Transfer::ActionAccept)
|
||||
#endif
|
||||
|
||||
#undef DEFINE_COMP_ID
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
MessageCommandDispatcher::MessageCommandDispatcher(
|
||||
Contact3Registry& cr,
|
||||
RegistryMessageModel& rmm,
|
||||
RegistryMessageModelI& rmm,
|
||||
ConfigModelI& conf
|
||||
) :
|
||||
_cr(cr), _rmm(rmm), _conf(conf), _program_started_at(Message::getTimeMS())
|
||||
|
@ -14,7 +14,7 @@ struct ConfigModelI;
|
||||
|
||||
class MessageCommandDispatcher : public RegistryMessageModelEventI {
|
||||
Contact3Registry& _cr;
|
||||
RegistryMessageModel& _rmm;
|
||||
RegistryMessageModelI& _rmm;
|
||||
ConfigModelI& _conf;
|
||||
|
||||
public:
|
||||
@ -56,7 +56,7 @@ class MessageCommandDispatcher : public RegistryMessageModelEventI {
|
||||
uint64_t _program_started_at {0};
|
||||
|
||||
public:
|
||||
MessageCommandDispatcher(Contact3Registry& cr, RegistryMessageModel& rmm, ConfigModelI& conf);
|
||||
MessageCommandDispatcher(Contact3Registry& cr, RegistryMessageModelI& rmm, ConfigModelI& conf);
|
||||
~MessageCommandDispatcher(void);
|
||||
|
||||
float iterate(float time_delta);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "./components.hpp"
|
||||
|
||||
MessageTimeSort::MessageTimeSort(RegistryMessageModel& rmm) : _rmm(rmm) {
|
||||
MessageTimeSort::MessageTimeSort(RegistryMessageModelI& rmm) : _rmm(rmm) {
|
||||
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
|
||||
_rmm.subscribe(this, RegistryMessageModel_Event::message_updated);
|
||||
_rmm.subscribe(this, RegistryMessageModel_Event::message_destroy);
|
||||
|
@ -5,13 +5,13 @@
|
||||
#include <entt/container/dense_set.hpp>
|
||||
|
||||
class MessageTimeSort : public RegistryMessageModelEventI {
|
||||
RegistryMessageModel& _rmm;
|
||||
RegistryMessageModelI& _rmm;
|
||||
|
||||
// TODO: use contact instead
|
||||
entt::dense_set<Message3Registry*> _to_sort;
|
||||
|
||||
public:
|
||||
MessageTimeSort(RegistryMessageModel& rmm);
|
||||
MessageTimeSort(RegistryMessageModelI& rmm);
|
||||
|
||||
// do the sorting
|
||||
void iterate(void);
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "./registry_message_model.hpp"
|
||||
|
||||
#include <solanaceae/contact/components.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
@ -9,136 +7,3 @@ uint64_t Message::getTimeMS(void) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
Message3Registry* RegistryMessageModel::get(Contact3 c) {
|
||||
if (_cr.valid(c) && !_cr.all_of<Contact::Components::TagBig>(c)) {
|
||||
// TODO: loop upwards
|
||||
if (!_cr.all_of<Contact::Components::Parent>(c)) {
|
||||
return nullptr;
|
||||
}
|
||||
c = _cr.get<Contact::Components::Parent>(c).parent;
|
||||
}
|
||||
|
||||
if (!_cr.valid(c)) {
|
||||
// TODO: throw error
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto it = _contact_messages.find(c);
|
||||
if (it != _contact_messages.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
auto& reg_sh = _contact_messages[c] = std::make_unique<Message3Registry>();
|
||||
reg_sh->ctx().emplace<Contact3>(c);
|
||||
return reg_sh.get();
|
||||
}
|
||||
|
||||
Message3Registry* RegistryMessageModel::get(Contact3 c) const {
|
||||
if (_cr.valid(c) && !_cr.all_of<Contact::Components::TagBig>(c)) {
|
||||
// TODO: loop upwards
|
||||
if (!_cr.all_of<Contact::Components::Parent>(c)) {
|
||||
return nullptr;
|
||||
}
|
||||
c = _cr.get<Contact::Components::Parent>(c).parent;
|
||||
}
|
||||
|
||||
if (!_cr.valid(c)) {
|
||||
// TODO: throw error
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto it = _contact_messages.find(c);
|
||||
if (it != _contact_messages.cend()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RegistryMessageModel::throwEventConstruct(Message3Registry& reg, Message3 e) {
|
||||
std::cout << "RMM debug: event construct " << entt::to_integral(e) << "\n";
|
||||
dispatch(
|
||||
RegistryMessageModel_Event::message_construct,
|
||||
Message::Events::MessageConstruct{
|
||||
Message3Handle{reg, e}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void RegistryMessageModel::throwEventUpdate(Message3Registry& reg, Message3 e) {
|
||||
// the update while update lock is hacky
|
||||
_update_queue.push_back({reg, e});
|
||||
if (!_update_in_progess) {
|
||||
_update_in_progess = true;
|
||||
for (size_t i = 0; i < _update_queue.size(); i++) {
|
||||
// WAY too spammy
|
||||
//std::cout << "RMM debug: event update " << entt::to_integral(e) << "\n";
|
||||
dispatch(
|
||||
RegistryMessageModel_Event::message_updated,
|
||||
Message::Events::MessageUpdated{
|
||||
_update_queue.at(i)
|
||||
}
|
||||
);
|
||||
}
|
||||
_update_queue.clear();
|
||||
_update_in_progess = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RegistryMessageModel::throwEventDestroy(Message3Registry& reg, Message3 e) {
|
||||
std::cout << "RMM debug: event destroy " << entt::to_integral(e) << "\n";
|
||||
dispatch(
|
||||
RegistryMessageModel_Event::message_destroy,
|
||||
Message::Events::MessageDestory{
|
||||
Message3Handle{reg, e}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void RegistryMessageModel::throwEventConstruct(const Contact3 c, Message3 e) {
|
||||
if (auto* reg_ptr = get(c); reg_ptr) {
|
||||
throwEventConstruct(*reg_ptr, e);
|
||||
}
|
||||
}
|
||||
|
||||
void RegistryMessageModel::throwEventUpdate(const Contact3 c, Message3 e) {
|
||||
if (auto* reg_ptr = get(c); reg_ptr) {
|
||||
throwEventUpdate(*reg_ptr, e);
|
||||
}
|
||||
}
|
||||
|
||||
void RegistryMessageModel::throwEventDestroy(const Contact3 c, Message3 e) {
|
||||
if (auto* reg_ptr = get(c); reg_ptr) {
|
||||
throwEventDestroy(*reg_ptr, e);
|
||||
}
|
||||
}
|
||||
|
||||
bool RegistryMessageModel::sendText(const Contact3 c, std::string_view message, bool action) {
|
||||
std::cout << "RMM debug: event send text\n";
|
||||
|
||||
// manual, bc its not an "event"
|
||||
for (auto* zei : _subscribers.at(size_t(RegistryMessageModel_Event::send_text))) {
|
||||
if (zei->sendText(c, message, action)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "RMM error: event send text unhandled\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RegistryMessageModel::sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) {
|
||||
std::cout << "RMM debug: event send file path\n";
|
||||
|
||||
// manual, bc its not an "event"
|
||||
for (auto* zei : _subscribers.at(size_t(RegistryMessageModel_Event::send_file_path))) {
|
||||
if (zei->sendFilePath(c, file_name, file_path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "RMM error: event send file path unhandled\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -7,9 +7,6 @@
|
||||
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/handle.hpp>
|
||||
#include <entt/container/dense_map.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using Message3Registry = entt::basic_registry<Message3>;
|
||||
using Message3Handle = entt::basic_handle<Message3Registry>;
|
||||
@ -69,45 +66,28 @@ struct RegistryMessageModelEventI : public MessageModel3I {
|
||||
};
|
||||
using RegistryMessageModelEventProviderI = EventProviderI<RegistryMessageModelEventI>;
|
||||
|
||||
class RegistryMessageModel : public RegistryMessageModelEventProviderI, public MessageModel3I {
|
||||
class RegistryMessageModelI : public RegistryMessageModelEventProviderI, public MessageModel3I {
|
||||
public:
|
||||
static constexpr const char* version {"2"};
|
||||
|
||||
protected:
|
||||
Contact3Registry& _cr;
|
||||
|
||||
entt::dense_map<Contact3, std::unique_ptr<Message3Registry>> _contact_messages;
|
||||
|
||||
bool _update_in_progess {false};
|
||||
std::vector<Message3Handle> _update_queue {};
|
||||
static constexpr const char* version {"3"};
|
||||
|
||||
// rmm interface
|
||||
public:
|
||||
RegistryMessageModel(Contact3Registry& cr) : _cr(cr) {}
|
||||
virtual ~RegistryMessageModel(void) {}
|
||||
|
||||
// TODO: iterate?
|
||||
|
||||
public:
|
||||
Message3Registry* get(Contact3 c);
|
||||
Message3Registry* get(Contact3 c) const;
|
||||
virtual Message3Registry* get(Contact3 c) = 0;
|
||||
virtual Message3Registry* get(Contact3 c) const = 0;
|
||||
|
||||
public: // dispatcher
|
||||
// !!! remember to manually throw these externally
|
||||
void throwEventConstruct(Message3Registry& reg, Message3 e);
|
||||
void throwEventUpdate(Message3Registry& reg, Message3 e);
|
||||
void throwEventDestroy(Message3Registry& reg, Message3 e);
|
||||
virtual void throwEventConstruct(Message3Registry& reg, Message3 e) = 0;
|
||||
virtual void throwEventUpdate(Message3Registry& reg, Message3 e) = 0;
|
||||
virtual void throwEventDestroy(Message3Registry& reg, Message3 e) = 0;
|
||||
|
||||
void throwEventConstruct(Message3Handle h) { throwEventConstruct(*h.registry(), h.entity()); }
|
||||
void throwEventUpdate(Message3Handle h) { throwEventUpdate(*h.registry(), h.entity()); }
|
||||
void throwEventDestroy(Message3Handle h) { throwEventDestroy(*h.registry(), h.entity()); }
|
||||
|
||||
void throwEventConstruct(const Contact3 c, Message3 e);
|
||||
void throwEventUpdate(const Contact3 c, Message3 e);
|
||||
void throwEventDestroy(const Contact3 c, Message3 e);
|
||||
|
||||
public: // mm3
|
||||
bool sendText(const Contact3 c, std::string_view message, bool action = false) override;
|
||||
bool sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) override;
|
||||
virtual void throwEventConstruct(const Contact3 c, Message3 e) = 0;
|
||||
virtual void throwEventUpdate(const Contact3 c, Message3 e) = 0;
|
||||
virtual void throwEventDestroy(const Contact3 c, Message3 e) = 0;
|
||||
};
|
||||
|
||||
template<>
|
||||
|
140
src/solanaceae/message3/registry_message_model_impl.cpp
Normal file
140
src/solanaceae/message3/registry_message_model_impl.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#include "./registry_message_model_impl.hpp"
|
||||
|
||||
#include <solanaceae/contact/components.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
Message3Registry* RegistryMessageModelImpl::get(Contact3 c) {
|
||||
if (_cr.valid(c) && !_cr.all_of<Contact::Components::TagBig>(c)) {
|
||||
// TODO: loop upwards
|
||||
if (!_cr.all_of<Contact::Components::Parent>(c)) {
|
||||
return nullptr;
|
||||
}
|
||||
c = _cr.get<Contact::Components::Parent>(c).parent;
|
||||
}
|
||||
|
||||
if (!_cr.valid(c)) {
|
||||
// TODO: throw error
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto it = _contact_messages.find(c);
|
||||
if (it != _contact_messages.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
auto& reg_sh = _contact_messages[c] = std::make_unique<Message3Registry>();
|
||||
reg_sh->ctx().emplace<Contact3>(c);
|
||||
return reg_sh.get();
|
||||
}
|
||||
|
||||
Message3Registry* RegistryMessageModelImpl::get(Contact3 c) const {
|
||||
if (_cr.valid(c) && !_cr.all_of<Contact::Components::TagBig>(c)) {
|
||||
// TODO: loop upwards
|
||||
if (!_cr.all_of<Contact::Components::Parent>(c)) {
|
||||
return nullptr;
|
||||
}
|
||||
c = _cr.get<Contact::Components::Parent>(c).parent;
|
||||
}
|
||||
|
||||
if (!_cr.valid(c)) {
|
||||
// TODO: throw error
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto it = _contact_messages.find(c);
|
||||
if (it != _contact_messages.cend()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RegistryMessageModelImpl::throwEventConstruct(Message3Registry& reg, Message3 e) {
|
||||
std::cout << "RMM debug: event construct " << entt::to_integral(e) << "\n";
|
||||
dispatch(
|
||||
RegistryMessageModel_Event::message_construct,
|
||||
Message::Events::MessageConstruct{
|
||||
Message3Handle{reg, e}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void RegistryMessageModelImpl::throwEventUpdate(Message3Registry& reg, Message3 e) {
|
||||
// the update while update lock is hacky
|
||||
_update_queue.push_back({reg, e});
|
||||
if (!_update_in_progess) {
|
||||
_update_in_progess = true;
|
||||
for (size_t i = 0; i < _update_queue.size(); i++) {
|
||||
// WAY too spammy
|
||||
//std::cout << "RMM debug: event update " << entt::to_integral(e) << "\n";
|
||||
dispatch(
|
||||
RegistryMessageModel_Event::message_updated,
|
||||
Message::Events::MessageUpdated{
|
||||
_update_queue.at(i)
|
||||
}
|
||||
);
|
||||
}
|
||||
_update_queue.clear();
|
||||
_update_in_progess = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RegistryMessageModelImpl::throwEventDestroy(Message3Registry& reg, Message3 e) {
|
||||
std::cout << "RMM debug: event destroy " << entt::to_integral(e) << "\n";
|
||||
dispatch(
|
||||
RegistryMessageModel_Event::message_destroy,
|
||||
Message::Events::MessageDestory{
|
||||
Message3Handle{reg, e}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void RegistryMessageModelImpl::throwEventConstruct(const Contact3 c, Message3 e) {
|
||||
if (auto* reg_ptr = get(c); reg_ptr) {
|
||||
throwEventConstruct(*reg_ptr, e);
|
||||
}
|
||||
}
|
||||
|
||||
void RegistryMessageModelImpl::throwEventUpdate(const Contact3 c, Message3 e) {
|
||||
if (auto* reg_ptr = get(c); reg_ptr) {
|
||||
throwEventUpdate(*reg_ptr, e);
|
||||
}
|
||||
}
|
||||
|
||||
void RegistryMessageModelImpl::throwEventDestroy(const Contact3 c, Message3 e) {
|
||||
if (auto* reg_ptr = get(c); reg_ptr) {
|
||||
throwEventDestroy(*reg_ptr, e);
|
||||
}
|
||||
}
|
||||
|
||||
bool RegistryMessageModelImpl::sendText(const Contact3 c, std::string_view message, bool action) {
|
||||
std::cout << "RMM debug: event send text\n";
|
||||
|
||||
// manual, bc its not an "event"
|
||||
for (auto* zei : _subscribers.at(size_t(RegistryMessageModel_Event::send_text))) {
|
||||
if (zei->sendText(c, message, action)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "RMM error: event send text unhandled\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RegistryMessageModelImpl::sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) {
|
||||
std::cout << "RMM debug: event send file path\n";
|
||||
|
||||
// manual, bc its not an "event"
|
||||
for (auto* zei : _subscribers.at(size_t(RegistryMessageModel_Event::send_file_path))) {
|
||||
if (zei->sendFilePath(c, file_name, file_path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "RMM error: event send file path unhandled\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
44
src/solanaceae/message3/registry_message_model_impl.hpp
Normal file
44
src/solanaceae/message3/registry_message_model_impl.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "./registry_message_model.hpp"
|
||||
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/handle.hpp>
|
||||
#include <entt/container/dense_map.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class RegistryMessageModelImpl : public RegistryMessageModelI {
|
||||
protected:
|
||||
Contact3Registry& _cr;
|
||||
|
||||
entt::dense_map<Contact3, std::unique_ptr<Message3Registry>> _contact_messages;
|
||||
|
||||
bool _update_in_progess {false};
|
||||
std::vector<Message3Handle> _update_queue {};
|
||||
|
||||
public:
|
||||
RegistryMessageModelImpl(Contact3Registry& cr) : _cr(cr) {}
|
||||
virtual ~RegistryMessageModelImpl(void) {}
|
||||
|
||||
// TODO: iterate?
|
||||
|
||||
public:
|
||||
Message3Registry* get(Contact3 c) override;
|
||||
Message3Registry* get(Contact3 c) const override;
|
||||
|
||||
public: // dispatcher
|
||||
// !!! remember to manually throw these externally
|
||||
void throwEventConstruct(Message3Registry& reg, Message3 e) override;
|
||||
void throwEventUpdate(Message3Registry& reg, Message3 e) override;
|
||||
void throwEventDestroy(Message3Registry& reg, Message3 e) override;
|
||||
|
||||
void throwEventConstruct(const Contact3 c, Message3 e) override;
|
||||
void throwEventUpdate(const Contact3 c, Message3 e) override;
|
||||
void throwEventDestroy(const Contact3 c, Message3 e) override;
|
||||
|
||||
public: // mm3
|
||||
bool sendText(const Contact3 c, std::string_view message, bool action = false) override;
|
||||
bool sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) override;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user