init
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mask
|
||||
shader_type canvas_item;
|
||||
render_mode blend_mix, unshaded;
|
||||
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
|
||||
void fragment() {
|
||||
COLOR = channel * texture(tex_main, UV).a;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mask + Add
|
||||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
uniform sampler2D tex_mask : filter_linear_mipmap;
|
||||
|
||||
uniform float mask_scale;
|
||||
uniform vec2 canvas_size;
|
||||
uniform vec2 mesh_offset;
|
||||
|
||||
varying vec2 MASK_UV;
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
|
||||
vec2 mask_vtx = VERTEX - mesh_offset;
|
||||
MASK_UV = mask_vtx / mask_size;
|
||||
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color_for_mask = color_tex * color_base * modulate;
|
||||
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
|
||||
|
||||
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
|
||||
|
||||
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
|
||||
color_for_mask.rgb = color_for_mask.rgb * mask_val;
|
||||
COLOR = vec4(color_for_mask.rgb, 0.0);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mask + AddInv
|
||||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
uniform sampler2D tex_mask : filter_linear_mipmap;
|
||||
|
||||
uniform float mask_scale;
|
||||
uniform vec2 canvas_size;
|
||||
uniform vec2 mesh_offset;
|
||||
|
||||
varying vec2 MASK_UV;
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
|
||||
vec2 mask_vtx = VERTEX - mesh_offset;
|
||||
MASK_UV = mask_vtx / mask_size;
|
||||
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color_for_mask = color_tex * color_base * modulate;
|
||||
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
|
||||
|
||||
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
|
||||
|
||||
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
|
||||
color_for_mask.rgb = color_for_mask.rgb * (1.0 - mask_val);
|
||||
COLOR = vec4(color_for_mask.rgb, 0.0);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mask + Mix
|
||||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
uniform sampler2D tex_mask : filter_linear_mipmap;
|
||||
|
||||
uniform float mask_scale;
|
||||
uniform vec2 canvas_size;
|
||||
uniform vec2 mesh_offset;
|
||||
|
||||
varying vec2 MASK_UV;
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
|
||||
vec2 mask_vtx = VERTEX - mesh_offset;
|
||||
MASK_UV = mask_vtx / mask_size;
|
||||
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color_for_mask = color_tex * color_base * modulate;
|
||||
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
|
||||
|
||||
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
|
||||
|
||||
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
|
||||
color_for_mask = color_for_mask * mask_val;
|
||||
COLOR = color_for_mask;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mask + MixInv
|
||||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
uniform sampler2D tex_mask : filter_linear_mipmap;
|
||||
|
||||
uniform float mask_scale;
|
||||
uniform vec2 canvas_size;
|
||||
uniform vec2 mesh_offset;
|
||||
|
||||
varying vec2 MASK_UV;
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
|
||||
vec2 mask_vtx = VERTEX - mesh_offset;
|
||||
MASK_UV = mask_vtx / mask_size;
|
||||
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color_for_mask = color_tex * color_base * modulate;
|
||||
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
|
||||
|
||||
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
|
||||
|
||||
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
|
||||
color_for_mask = color_for_mask * (1.0 - mask_val);
|
||||
COLOR = color_for_mask;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mask + Mul
|
||||
shader_type canvas_item;
|
||||
render_mode blend_mul, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
uniform sampler2D tex_mask : filter_linear_mipmap;
|
||||
|
||||
uniform float mask_scale;
|
||||
uniform vec2 canvas_size;
|
||||
uniform vec2 mesh_offset;
|
||||
|
||||
varying vec2 MASK_UV;
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
|
||||
vec2 mask_vtx = VERTEX - mesh_offset;
|
||||
MASK_UV = mask_vtx / mask_size;
|
||||
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color_for_mask = color_tex * color_base * modulate;
|
||||
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
|
||||
|
||||
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
|
||||
|
||||
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
|
||||
color_for_mask = color_for_mask * mask_val;
|
||||
COLOR = vec4(
|
||||
color_for_mask.r + (1.0 - color_for_mask.a),
|
||||
color_for_mask.g + (1.0 - color_for_mask.a),
|
||||
color_for_mask.b + (1.0 - color_for_mask.a),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mask + MulInv
|
||||
shader_type canvas_item;
|
||||
render_mode blend_mul, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
uniform sampler2D tex_mask : filter_linear_mipmap;
|
||||
|
||||
uniform float mask_scale;
|
||||
uniform vec2 canvas_size;
|
||||
uniform vec2 mesh_offset;
|
||||
|
||||
varying vec2 MASK_UV;
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
|
||||
vec2 mask_vtx = VERTEX - mesh_offset;
|
||||
MASK_UV = mask_vtx / mask_size;
|
||||
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color_for_mask = color_tex * color_base * modulate;
|
||||
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
|
||||
|
||||
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
|
||||
|
||||
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
|
||||
color_for_mask = color_for_mask * (1.0 - mask_val);
|
||||
COLOR = vec4(
|
||||
color_for_mask.r + (1.0 - color_for_mask.a),
|
||||
color_for_mask.g + (1.0 - color_for_mask.a),
|
||||
color_for_mask.b + (1.0 - color_for_mask.a),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Add
|
||||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = (color_tex.rgb + color_screen.rgb) - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color = color_tex * color_base * modulate;
|
||||
COLOR = vec4(color.rgb * color.a, 0.0);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mix
|
||||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = (color_tex.rgb + color_screen.rgb) - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color = color_tex * color_base * modulate;
|
||||
COLOR = vec4(color.rgb * color.a, color.a);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
|
||||
// GDCubism shader: Mul
|
||||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha, unshaded;
|
||||
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
|
||||
varying vec4 modulate;
|
||||
|
||||
void vertex() {
|
||||
modulate = COLOR;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 color_tex = texture(tex_main, UV);
|
||||
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
|
||||
|
||||
// premul alpha
|
||||
color_tex.rgb = (color_tex.rgb + color_screen.rgb) - (color_tex.rgb * color_screen.rgb);
|
||||
vec4 color = color_tex * color_base * modulate;
|
||||
COLOR = vec4(color.rgb * color.a, color.a);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
# Shader contract for gd_cubism (Live2D)
|
||||
|
||||
## Overview
|
||||
|
||||
gd_cubism uses 10 shaders to render Live2D models. Each drawable (mesh part)
|
||||
selects one of these shaders based on its blend mode and mask configuration.
|
||||
A mod can replace any shader by placing a `.gdshader` file with the same name
|
||||
in its `shaders/` directory.
|
||||
|
||||
## Mandatory contract
|
||||
|
||||
### Uniforms — DO NOT remove or rename
|
||||
|
||||
Every shader below MUST declare the uniforms listed in its section. The C++ renderer
|
||||
sets these uniforms by name via `ShaderMaterial::set_shader_parameter()`.
|
||||
Removing or renaming a uniform will break rendering.
|
||||
|
||||
You MAY add new uniforms (e.g. `uniform float pixel_size = 4.0;`), but you
|
||||
must set them manually in code via `model.GetMeshes()`.
|
||||
|
||||
### Vertex shader
|
||||
|
||||
All shaders MUST include `UV.y = 1.0 - UV.y;` in the vertex function.
|
||||
This flips the texture vertically to match Live2D's coordinate system.
|
||||
|
||||
### blend_mode must match
|
||||
|
||||
The `render_mode` line must remain compatible with the blend operation
|
||||
the C++ code expects. Changing `blend_premul_alpha` to `blend_mix`
|
||||
will produce incorrect transparency.
|
||||
|
||||
---
|
||||
|
||||
## Shader catalog
|
||||
|
||||
### 1. `2d_cubism_norm_mix.gdshader` — Normal blend (most drawables)
|
||||
|
||||
render_mode: `blend_premul_alpha, unshaded`
|
||||
|
||||
Mandatory uniforms:
|
||||
```
|
||||
uniform vec4 color_base : source_color;
|
||||
uniform vec4 color_screen : source_color;
|
||||
uniform vec4 color_multiply : source_color;
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
```
|
||||
|
||||
Varying: `varying vec4 modulate` (passed from vertex: `modulate = COLOR`)
|
||||
|
||||
Fragment output: `COLOR = vec4(color.rgb * color.a, color.a);`
|
||||
|
||||
### 2. `2d_cubism_norm_add.gdshader` — Additive blend
|
||||
|
||||
Same uniforms and contract as norm_mix. Fragment output:
|
||||
`COLOR = vec4(color.rgb * color.a, 0.0);` (alpha=0 for additive)
|
||||
|
||||
### 3. `2d_cubism_norm_mul.gdshader` — Multiply blend
|
||||
|
||||
Same uniforms and contract as norm_mix.
|
||||
|
||||
### 4. `2d_cubism_mask.gdshader` — Mask generation (offscreen)
|
||||
|
||||
render_mode: `blend_mix, unshaded`
|
||||
|
||||
Mandatory uniforms:
|
||||
```
|
||||
uniform vec4 channel : source_color;
|
||||
uniform sampler2D tex_main : filter_linear_mipmap;
|
||||
```
|
||||
|
||||
Fragment output: `COLOR = channel * texture(tex_main, UV).a;`
|
||||
|
||||
This shader writes alpha to the mask texture. Not visible on screen.
|
||||
|
||||
### 5–10. Masked drawable shaders
|
||||
|
||||
`2d_cubism_mask_mix`, `mask_add`, `mask_mul`, `mask_mix_inv`, `mask_add_inv`, `mask_mul_inv`
|
||||
|
||||
render_mode: same as their non-mask counterpart
|
||||
|
||||
Mandatory uniforms (all of norm_mix PLUS):
|
||||
```
|
||||
uniform sampler2D tex_mask : filter_linear_mipmap;
|
||||
```
|
||||
|
||||
Additional varyings (computed in vertex):
|
||||
```
|
||||
varying vec2 MASK_UV;
|
||||
```
|
||||
|
||||
Mask UV computation in vertex:
|
||||
```glsl
|
||||
MASK_UV = (VERTEX - mesh_offset) / mask_size;
|
||||
// where mask_size = textureSize(tex_mask, 0) / mask_scale
|
||||
```
|
||||
|
||||
Fragment must sample `tex_mask` and multiply/lerp the main color by
|
||||
the sum of mask channels: `clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a`.
|
||||
The `_inv` variants invert this: `(1.0 - mask_val)`.
|
||||
|
||||
---
|
||||
|
||||
## How to add custom logic
|
||||
|
||||
**Safe additions** (will not break rendering):
|
||||
- Add new `uniform` declarations (set values via `ShaderMaterial.SetShaderParameter`)
|
||||
- Modify internal fragment calculations (e.g. pixel-art downscale)
|
||||
- Add conditional logic (e.g. `if (pixel_size > 1.0) { ... }`)
|
||||
|
||||
**Allowed modifications:**
|
||||
- Change texture filtering: `filter_linear_mipmap` → `filter_nearest`
|
||||
- Add post-processing in fragment (color grading, dithering, outline)
|
||||
- Add noise/procedural effects using new uniforms
|
||||
|
||||
**Forbidden modifications:**
|
||||
- Remove or rename mandatory uniforms
|
||||
- Change `render_mode` blend type
|
||||
- Remove `UV.y = 1.0 - UV.y` from vertex
|
||||
- Change the data type of mandatory uniforms (vec4 → vec3, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Mod override example
|
||||
|
||||
To create a pixel-art Live2D shader:
|
||||
|
||||
1. Copy `2d_cubism_norm_mix.gdshader` to `mods/your_mod/shaders/`
|
||||
2. Add a new uniform: `uniform float pixel_size : hint_range(1.0, 16.0) = 4.0;`
|
||||
3. Modify fragment to downsample:
|
||||
```glsl
|
||||
vec2 tex_size = vec2(textureSize(tex_main, 0));
|
||||
vec2 snapped_uv = floor(UV * tex_size / pixel_size) * pixel_size / tex_size;
|
||||
vec4 color_tex = texture(tex_main, snapped_uv);
|
||||
```
|
||||
4. Keep all mandatory uniforms and the vertex shader intact.
|
||||
5. In code, find the material and set `pixel_size`:
|
||||
```csharp
|
||||
var meshes = model.GetMeshes();
|
||||
foreach (var kv in meshes) {
|
||||
var mesh = (ArrayMesh)kv.Value;
|
||||
var mat = (ShaderMaterial)mesh.SurfaceGetMaterial(0);
|
||||
mat.SetShaderParameter("pixel_size", 4.0f);
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user