mirror of
https://github.com/Kbz-8/42_vox.git
synced 2026-01-11 14:43:34 +00:00
working on chunk generation
This commit is contained in:
6
Application/Block.h
git.filemode.normal_file
6
Application/Block.h
git.filemode.normal_file
@@ -0,0 +1,6 @@
|
||||
#ifndef BLOCK_H
|
||||
#define BLOCK_H
|
||||
|
||||
#include <ScopGraphics.h>
|
||||
|
||||
#endif
|
||||
29
Application/Chunk.cpp
git.filemode.normal_file
29
Application/Chunk.cpp
git.filemode.normal_file
@@ -0,0 +1,29 @@
|
||||
#include <Chunk.h>
|
||||
#include <algorithm>
|
||||
|
||||
Chunk::Chunk(Scop::Scene& world, Scop::Vec2ui offset) : m_data(CHUNK_VOLUME), m_offset(offset), m_position(std::move(offset) * Scop::Vec2ui{ CHUNK_SIZE })
|
||||
{
|
||||
}
|
||||
|
||||
void Chunk::GenerateChunk()
|
||||
{
|
||||
std::fill(m_data.begin(), m_data.end(), 0);
|
||||
|
||||
for(std::uint32_t x = 0; x < CHUNK_SIZE.x; x++)
|
||||
{
|
||||
for(std::uint32_t z = 0; z < CHUNK_SIZE.z; z++)
|
||||
{
|
||||
std::uint32_t pos_x = m_position.x + x;
|
||||
std::uint32_t pos_z = m_position.y + z;
|
||||
|
||||
for(std::uint32_t y = 0; y < CHUNK_SIZE.y; y++)
|
||||
{
|
||||
std::uint32_t index = (z * CHUNK_SIZE.x * CHUNK_SIZE.y) + (y * CHUNK_SIZE.x) + x;
|
||||
|
||||
// Implement noise here
|
||||
|
||||
m_data[index] = y < 10 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Application/Chunk.h
git.filemode.normal_file
27
Application/Chunk.h
git.filemode.normal_file
@@ -0,0 +1,27 @@
|
||||
#ifndef CHUNK_H
|
||||
#define CHUNK_H
|
||||
|
||||
#include <vector>
|
||||
#include <ScopGraphics.h>
|
||||
#include <ScopMaths.h>
|
||||
|
||||
constexpr Scop::Vec3ui CHUNK_SIZE = Scop::Vec3ui{ 16, 256, 16 };
|
||||
constexpr std::uint32_t CHUNK_VOLUME = CHUNK_SIZE.x * CHUNK_SIZE.y * CHUNK_SIZE.z;
|
||||
|
||||
class Chunk
|
||||
{
|
||||
public:
|
||||
Chunk(Scop::Scene& world, Scop::Vec2ui offset);
|
||||
|
||||
void GenerateChunk();
|
||||
|
||||
~Chunk() = default;
|
||||
|
||||
private:
|
||||
std::vector<std::uint32_t> m_data;
|
||||
Scop::Vec2ui m_offset; // In chunks
|
||||
Scop::Vec2ui m_position; // In blocks
|
||||
Scop::NonOwningPtr<Scop::Actor> p_actor = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,118 +0,0 @@
|
||||
#include <ScriptSubRoutines.h>
|
||||
|
||||
constexpr const float SPEED = 40.0f;
|
||||
|
||||
void WireframeHandler(Scop::NonOwningPtr<Scop::Scene> scene, Scop::Inputs& inputs)
|
||||
{
|
||||
static bool key_pressed_last_frame = false;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_F))
|
||||
key_pressed_last_frame = true;
|
||||
else if(key_pressed_last_frame)
|
||||
{
|
||||
scene->GetForwardData().wireframe = !scene->GetForwardData().wireframe;
|
||||
Scop::RenderCore::Get().WaitDeviceIdle();
|
||||
scene->GetPipeline().Destroy();
|
||||
key_pressed_last_frame = false;
|
||||
}
|
||||
static Scop::Vec3f rotations{ 0.0f, 0.0f, 0.0f };
|
||||
}
|
||||
|
||||
void MovementHandler(Scop::NonOwningPtr<Scop::Actor> actor, Scop::Inputs& inputs, float delta)
|
||||
{
|
||||
static Scop::Vec3f position{ 0.0f, 0.0f, 0.0f };
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_I))
|
||||
position.x += SPEED * delta;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_K))
|
||||
position.x -= SPEED * delta;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_L))
|
||||
position.z += SPEED * delta;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_J))
|
||||
position.z -= SPEED * delta;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_U))
|
||||
position.y += SPEED * delta;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_O))
|
||||
position.y -= SPEED * delta;
|
||||
actor->SetPosition(position);
|
||||
}
|
||||
|
||||
void ColorsTransitionHandler(Scop::NonOwningPtr<Scop::Actor> actor, Scop::Inputs& inputs, float delta, Scop::MaterialData& data)
|
||||
{
|
||||
static bool colors_transition = false;
|
||||
static bool colors_key_pressed_last_frame = false;
|
||||
static std::uint8_t colors_transition_way = 0;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_C) && !colors_transition)
|
||||
colors_key_pressed_last_frame = true;
|
||||
else if(colors_key_pressed_last_frame)
|
||||
{
|
||||
colors_key_pressed_last_frame = false;
|
||||
colors_transition = true;
|
||||
colors_transition_way = (data.dissolve_black_white_colors_factor >= 0.5f);
|
||||
}
|
||||
if(colors_transition)
|
||||
{
|
||||
if(colors_transition_way == 1)
|
||||
{
|
||||
data.dissolve_black_white_colors_factor -= 1.0f * delta;
|
||||
colors_transition = (data.dissolve_black_white_colors_factor > 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.dissolve_black_white_colors_factor += 1.0f * delta;
|
||||
colors_transition = (data.dissolve_black_white_colors_factor < 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static bool normals_transition = false;
|
||||
static bool normals_key_pressed_last_frame = false;
|
||||
static std::uint8_t normals_transition_way = 0;
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_N) && !normals_transition)
|
||||
normals_key_pressed_last_frame = true;
|
||||
else if(normals_key_pressed_last_frame)
|
||||
{
|
||||
normals_key_pressed_last_frame = false;
|
||||
normals_transition = true;
|
||||
normals_transition_way = (data.dissolve_normals_colors_factor >= 0.5f);
|
||||
}
|
||||
if(normals_transition)
|
||||
{
|
||||
if(normals_transition_way == 1)
|
||||
{
|
||||
data.dissolve_normals_colors_factor -= 1.0f * delta;
|
||||
normals_transition = (data.dissolve_normals_colors_factor > 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.dissolve_normals_colors_factor += 1.0f * delta;
|
||||
normals_transition = (data.dissolve_normals_colors_factor < 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextureTransitionHandler(Scop::NonOwningPtr<Scop::Actor> actor, Scop::Inputs& inputs, float delta, Scop::MaterialData& data)
|
||||
{
|
||||
static bool texture_transition = false;
|
||||
static bool key_pressed_last_frame = false;
|
||||
static std::uint8_t transition_way = 0;
|
||||
|
||||
if(inputs.IsKeyPressed(SDL_SCANCODE_T) && !texture_transition)
|
||||
key_pressed_last_frame = true;
|
||||
else if(key_pressed_last_frame)
|
||||
{
|
||||
texture_transition = true;
|
||||
key_pressed_last_frame = false;
|
||||
transition_way = (data.dissolve_texture_factor >= 0.5f);
|
||||
}
|
||||
if(texture_transition)
|
||||
{
|
||||
if(transition_way == 1)
|
||||
{
|
||||
data.dissolve_texture_factor -= 1.0f * delta;
|
||||
texture_transition = (data.dissolve_texture_factor > 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.dissolve_texture_factor += 1.0f * delta;
|
||||
texture_transition = (data.dissolve_texture_factor < 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef __SCRIPT_SUB_ROUTINES__
|
||||
#define __SCRIPT_SUB_ROUTINES__
|
||||
|
||||
#include <ScopCore.h>
|
||||
#include <ScopGraphics.h>
|
||||
|
||||
void WireframeHandler(Scop::NonOwningPtr<Scop::Scene> scene, Scop::Inputs& inputs);
|
||||
void MovementHandler(Scop::NonOwningPtr<Scop::Actor> actor, Scop::Inputs& inputs, float delta);
|
||||
void ColorsTransitionHandler(Scop::NonOwningPtr<Scop::Actor> actor, Scop::Inputs& inputs, float delta, Scop::MaterialData& data);
|
||||
void TextureTransitionHandler(Scop::NonOwningPtr<Scop::Actor> actor, Scop::Inputs& inputs, float delta, Scop::MaterialData& data);
|
||||
|
||||
#endif
|
||||
20
Application/Utils.h
git.filemode.normal_file
20
Application/Utils.h
git.filemode.normal_file
@@ -0,0 +1,20 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <climits>
|
||||
#include <filesystem>
|
||||
|
||||
inline std::filesystem::path GetExecutablePath()
|
||||
{
|
||||
char result[PATH_MAX];
|
||||
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
|
||||
return std::string(result, (count > 0) ? count : 0);
|
||||
}
|
||||
|
||||
inline std::filesystem::path GetResourcesPath()
|
||||
{
|
||||
return GetExecutablePath().parent_path().parent_path() / "Resources";
|
||||
}
|
||||
|
||||
#endif
|
||||
18
Application/World.cpp
git.filemode.normal_file
18
Application/World.cpp
git.filemode.normal_file
@@ -0,0 +1,18 @@
|
||||
#include <ScopCore.h>
|
||||
|
||||
#include <World.h>
|
||||
#include <Utils.h>
|
||||
|
||||
World::World(Scop::Scene& scene) : m_scene(scene), m_narrator(scene.CreateNarrator())
|
||||
{
|
||||
Scop::Vec2ui32 map_size;
|
||||
Scop::MaterialTextures material_params;
|
||||
material_params.albedo = std::make_shared<Scop::Texture>(Scop::LoadBMPFile(GetResourcesPath() / "prototype.bmp", map_size), map_size.x, map_size.y);
|
||||
p_block_material = std::make_shared<Scop::Material>(material_params);
|
||||
|
||||
auto narrator_update = [](Scop::NonOwningPtr<Scop::Scene> scene, Scop::Inputs& input, float delta)
|
||||
{
|
||||
};
|
||||
|
||||
m_narrator.AttachScript(std::make_shared<Scop::NativeNarratorScript>(std::function<void()>{}, narrator_update, std::function<void()>{}));
|
||||
}
|
||||
28
Application/World.h
git.filemode.normal_file
28
Application/World.h
git.filemode.normal_file
@@ -0,0 +1,28 @@
|
||||
#ifndef WORLD_H
|
||||
#define WORLD_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <ScopGraphics.h>
|
||||
|
||||
#include <Chunk.h>
|
||||
|
||||
class World
|
||||
{
|
||||
public:
|
||||
World(Scop::Scene& scene);
|
||||
|
||||
[[nodiscard]] inline Scop::Scene& GetScene() noexcept { return m_scene; }
|
||||
|
||||
~World() = default;
|
||||
|
||||
private:
|
||||
static inline constexpr std::size_t CHUNKS_SIZE = 16;
|
||||
|
||||
std::vector<std::vector<Chunk>> m_chunks;
|
||||
std::shared_ptr<Scop::Material> p_block_material;
|
||||
Scop::Narrator& m_narrator;
|
||||
Scop::Scene& m_scene;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,27 +1,9 @@
|
||||
#include <ScopCore.h>
|
||||
#include <ScopGraphics.h>
|
||||
|
||||
#include <ScriptSubRoutines.h>
|
||||
#include <Splash.h>
|
||||
|
||||
#include <climits>
|
||||
#include <memory>
|
||||
#include <unistd.h>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
std::filesystem::path GetExecutablePath()
|
||||
{
|
||||
char result[PATH_MAX];
|
||||
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
|
||||
return std::string(result, (count > 0) ? count : 0);
|
||||
}
|
||||
|
||||
std::filesystem::path GetResourcesPath()
|
||||
{
|
||||
return GetExecutablePath().parent_path().parent_path() / "Resources";
|
||||
}
|
||||
#include <Utils.h>
|
||||
#include <World.h>
|
||||
|
||||
int main(int ac, char** av)
|
||||
{
|
||||
@@ -37,6 +19,16 @@ int main(int ac, char** av)
|
||||
Scop::Vec2ui32 skybox_size;
|
||||
main_scene.AddSkybox(std::make_shared<Scop::CubeTexture>(Scop::LoadBMPFile(GetResourcesPath() / "skybox.bmp", skybox_size), skybox_size.x, skybox_size.y));
|
||||
|
||||
World world(main_scene);
|
||||
|
||||
Scop::Actor& object = main_scene.CreateActor(Scop::CreateCube());
|
||||
|
||||
Scop::Vec2ui32 map_size;
|
||||
Scop::MaterialTextures material_params;
|
||||
material_params.albedo = std::make_shared<Scop::Texture>(Scop::LoadBMPFile(GetResourcesPath() / "prototype.bmp", map_size), map_size.x, map_size.y);
|
||||
std::shared_ptr<Scop::Material> material = std::make_shared<Scop::Material>(material_params);
|
||||
object.GetModelRef().SetMaterial(material, 0);
|
||||
|
||||
engine.RegisterMainScene(splash_scene.get());
|
||||
engine.Run();
|
||||
return 0;
|
||||
|
||||
BIN
Resources/prototype.bmp
git.filemode.normal_file
BIN
Resources/prototype.bmp
git.filemode.normal_file
Binary file not shown.
|
After Width: | Height: | Size: 768 KiB |
@@ -44,6 +44,25 @@ namespace Scop
|
||||
std::function<void(NonOwningPtr<class Scene>, NonOwningPtr<class Sprite>, class Inputs&, float)> f_on_update;
|
||||
std::function<void(NonOwningPtr<class Sprite>)> f_on_quit;
|
||||
};
|
||||
|
||||
class NativeNarratorScript : public NarratorScript
|
||||
{
|
||||
public:
|
||||
NativeNarratorScript(std::function<void()> on_init, std::function<void(NonOwningPtr<class Scene>, class Inputs&, float)> on_update, std::function<void()> on_quit)
|
||||
: f_on_init(std::move(on_init)), f_on_update(std::move(on_update)), f_on_quit(std::move(on_quit))
|
||||
{}
|
||||
|
||||
inline void OnInit() override { if(f_on_init) f_on_init(); }
|
||||
inline void OnUpdate(NonOwningPtr<class Scene> scene, class Inputs& input, float delta) override { if(f_on_update) f_on_update(scene, input, delta); }
|
||||
inline void OnQuit() override { if(f_on_quit) f_on_quit(); }
|
||||
|
||||
~NativeNarratorScript() = default;
|
||||
|
||||
private:
|
||||
std::function<void()> f_on_init;
|
||||
std::function<void(NonOwningPtr<class Scene>, class Inputs&, float)> f_on_update;
|
||||
std::function<void()> f_on_quit;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,18 @@ namespace Scop
|
||||
|
||||
virtual ~SpriteScript() = default;
|
||||
};
|
||||
|
||||
class NarratorScript
|
||||
{
|
||||
public:
|
||||
NarratorScript() = default;
|
||||
|
||||
virtual void OnInit() = 0;
|
||||
virtual void OnUpdate(NonOwningPtr<class Scene> scene, class Inputs& input, float delta) = 0;
|
||||
virtual void OnQuit() = 0;
|
||||
|
||||
virtual ~NarratorScript() = default;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef __SCOP_RENDERER_ACTOR__
|
||||
#define __SCOP_RENDERER_ACTOR__
|
||||
#ifndef __SCOP_GRAPHICS_ACTOR__
|
||||
#define __SCOP_GRAPHICS_ACTOR__
|
||||
|
||||
#include <Maths/Vec3.h>
|
||||
#include <Maths/Vec4.h>
|
||||
@@ -11,12 +11,13 @@ namespace Scop
|
||||
{
|
||||
class Actor
|
||||
{
|
||||
friend Scene;
|
||||
|
||||
public:
|
||||
Actor();
|
||||
Actor(Model model);
|
||||
|
||||
inline void AttachScript(std::shared_ptr<ActorScript> script) { p_script = script; }
|
||||
void Update(NonOwningPtr<class Scene> scene, class Inputs& input, float timestep);
|
||||
|
||||
inline void SetColor(Vec4f color) noexcept { m_color = color; }
|
||||
inline void SetPosition(Vec3f position) noexcept { m_position = position; }
|
||||
@@ -32,6 +33,9 @@ namespace Scop
|
||||
|
||||
~Actor();
|
||||
|
||||
public:
|
||||
void Update(NonOwningPtr<class Scene> scene, class Inputs& input, float timestep);
|
||||
|
||||
private:
|
||||
Model m_model;
|
||||
Quatf m_orientation = Quatf::Identity();
|
||||
|
||||
38
ScopEngine/Runtime/Includes/Graphics/Narrator.h
git.filemode.normal_file
38
ScopEngine/Runtime/Includes/Graphics/Narrator.h
git.filemode.normal_file
@@ -0,0 +1,38 @@
|
||||
#ifndef __SCOP_GRAPHICS_NARRATOR__
|
||||
#define __SCOP_GRAPHICS_NARRATOR__
|
||||
|
||||
#include <Maths/Vec3.h>
|
||||
#include <Maths/Vec4.h>
|
||||
#include <Maths/Quaternions.h>
|
||||
#include <Core/Script.h>
|
||||
#include <Graphics/Model.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
class Narrator
|
||||
{
|
||||
friend Scene;
|
||||
|
||||
public:
|
||||
Narrator() = default;
|
||||
inline void AttachScript(std::shared_ptr<NarratorScript> script) { p_script = script; }
|
||||
inline ~Narrator()
|
||||
{
|
||||
if(p_script)
|
||||
p_script->OnQuit();
|
||||
}
|
||||
|
||||
private:
|
||||
inline void Update(NonOwningPtr<class Scene> scene, class Inputs& input, float timestep)
|
||||
{
|
||||
if(p_script)
|
||||
p_script->OnUpdate(scene, input, timestep);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<NarratorScript> p_script;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <Utils/NonOwningPtr.h>
|
||||
|
||||
#include <Graphics/Actor.h>
|
||||
#include <Graphics/Narrator.h>
|
||||
#include <Graphics/Sprite.h>
|
||||
#include <Renderer/Buffer.h>
|
||||
#include <Renderer/Descriptor.h>
|
||||
@@ -47,6 +48,9 @@ namespace Scop
|
||||
Actor& CreateActor(Model model) noexcept;
|
||||
Actor& CreateActor(std::string_view name, Model model);
|
||||
|
||||
Narrator& CreateNarrator() noexcept;
|
||||
Narrator& CreateNarrator(std::string_view name);
|
||||
|
||||
Sprite& CreateSprite(std::shared_ptr<Texture> texture) noexcept;
|
||||
Sprite& CreateSprite(std::string_view name, std::shared_ptr<Texture> texture);
|
||||
|
||||
@@ -82,6 +86,7 @@ namespace Scop
|
||||
std::shared_ptr<CubeTexture> p_skybox;
|
||||
std::vector<std::shared_ptr<Actor>> m_actors;
|
||||
std::vector<std::shared_ptr<Sprite>> m_sprites;
|
||||
std::vector<std::shared_ptr<Narrator>> m_narrators;
|
||||
std::vector<Scene> m_scene_children;
|
||||
std::string m_name;
|
||||
NonOwningPtr<Scene> p_parent;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef __SCOPE_MATHS_ENUMS__
|
||||
#define __SCOPE_MATHS_ENUMS__
|
||||
#ifndef __SCOP_MATHS_ENUMS__
|
||||
#define __SCOP_MATHS_ENUMS__
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
#include <Graphics/Cameras/FirstPerson3D.h>
|
||||
#include <Graphics/Loaders/OBJ.h>
|
||||
#include <Graphics/Loaders/BMP.h>
|
||||
#include <Graphics/Narrator.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,20 @@ namespace Scop
|
||||
return *actor;
|
||||
}
|
||||
|
||||
Narrator& Scene::CreateNarrator() noexcept
|
||||
{
|
||||
std::shared_ptr<Narrator> narrator = std::make_shared<Narrator>();
|
||||
m_narrators.push_back(narrator);
|
||||
return *narrator;
|
||||
}
|
||||
|
||||
Narrator& Scene::CreateNarrator(std::string_view name)
|
||||
{
|
||||
std::shared_ptr<Narrator> narrator = std::make_shared<Narrator>();
|
||||
m_narrators.push_back(narrator);
|
||||
return *narrator;
|
||||
}
|
||||
|
||||
Sprite& Scene::CreateSprite(std::shared_ptr<Texture> texture) noexcept
|
||||
{
|
||||
std::shared_ptr<Sprite> sprite = std::make_shared<Sprite>(texture);
|
||||
@@ -92,6 +106,7 @@ namespace Scop
|
||||
m_forward.matrices_set->Update(i);
|
||||
}
|
||||
m_forward.albedo_set = std::make_shared<DescriptorSet>(m_descriptor.fragment_shader->GetShaderLayout().set_layouts[0].second, m_descriptor.fragment_shader->GetPipelineLayout().set_layouts[0], ShaderType::Fragment);
|
||||
|
||||
for(auto& child : m_scene_children)
|
||||
child.Init(renderer);
|
||||
}
|
||||
@@ -100,6 +115,8 @@ namespace Scop
|
||||
{
|
||||
for(auto actor : m_actors)
|
||||
actor->Update(this, input, timestep);
|
||||
for(auto narrator : m_narrators)
|
||||
narrator->Update(this, input, timestep);
|
||||
for(auto sprite : m_sprites)
|
||||
sprite->Update(this, input, timestep);
|
||||
if(m_descriptor.camera)
|
||||
@@ -112,6 +129,7 @@ namespace Scop
|
||||
p_skybox.reset();
|
||||
m_depth.Destroy();
|
||||
m_actors.clear();
|
||||
m_narrators.clear();
|
||||
m_sprites.clear();
|
||||
m_pipeline.Destroy();
|
||||
m_descriptor.fragment_shader.reset();
|
||||
|
||||
Reference in New Issue
Block a user