mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-24 05:10:42 -04:00
Style: Finish simplifying BlockInfo
This commit is contained in:
parent
00f28e2039
commit
2fa379746a
@ -20,7 +20,7 @@ namespace ClassicalSharp {
|
||||
if( min.Z != 0 ) flags &= ~(1 << Side.Front);
|
||||
if( max.Z != 1 ) flags &= ~(1 << Side.Back);
|
||||
|
||||
if( (min.Y != 0 && max.Y == 1) && Draw[block] != DrawType.air ) {
|
||||
if( (min.Y != 0 && max.Y == 1) && Draw[block] != DrawType.Gas ) {
|
||||
flags &= ~(1 << Side.Top);
|
||||
flags &= ~(1 << Side.Bottom);
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ namespace ClassicalSharp {
|
||||
|
||||
public byte[] CanStretch = new byte[Block.Count];
|
||||
|
||||
public bool[] IsAir = new bool[Block.Count];
|
||||
|
||||
internal void UpdateCulling() {
|
||||
for( int block = 1; block < Block.Count; block++ )
|
||||
CheckOpaque( block );
|
||||
@ -38,7 +36,8 @@ namespace ClassicalSharp {
|
||||
|
||||
void CheckOpaque( int block ) {
|
||||
if( MinBB[block] != Vector3.Zero || MaxBB[block] != Vector3.One ) {
|
||||
IsOpaque[block] = false;
|
||||
if( Draw[block] == DrawType.Opaque )
|
||||
Draw[block] = DrawType.Translucent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +83,8 @@ namespace ClassicalSharp {
|
||||
if( block == other ) return Draw[block] != DrawType.TransparentThick;
|
||||
|
||||
// An opaque neighbour (asides from lava) culls the face.
|
||||
if( IsOpaque[other] && !IsLiquid( other ) ) return true;
|
||||
if( !IsTranslucent[block] || !IsTranslucent[other] ) return false;
|
||||
if( Draw[other] == DrawType.Opaque && !IsLiquid( other ) ) return true;
|
||||
if( Draw[block] != DrawType.Translucent || Draw[other] != DrawType.Translucent ) return false;
|
||||
|
||||
// e.g. for water / ice, don't need to draw water.
|
||||
CollideType bType = Collide[block], oType = Collide[other];
|
||||
|
@ -28,19 +28,6 @@ namespace ClassicalSharp {
|
||||
/// <summary> Stores various properties about the blocks in Minecraft Classic. </summary>
|
||||
public partial class BlockInfo {
|
||||
|
||||
/// <summary> Gets whether the given block id is translucent/partially see through. </summary>
|
||||
/// <remarks>Colour values are blended into both the transparent and opaque blocks behind them. </remarks>
|
||||
public bool[] IsTranslucent = new bool[Block.Count];
|
||||
|
||||
/// <summary> Gets whether the given block id is opaque/not partially see through. </summary>
|
||||
public bool[] IsOpaque = new bool[Block.Count];
|
||||
|
||||
// <summary> Gets whether the given block id is opaque/not partially see through on the y axis. </summary>
|
||||
//public bool[] IsOpaqueY = new bool[Block.Count];
|
||||
|
||||
/// <summary> Gets whether the given block id is a sprite. (e.g. flowers, saplings, fire) </summary>
|
||||
public bool[] IsSprite = new bool[Block.Count];
|
||||
|
||||
/// <summary> Gets whether the given block id is a liquid. (water and lava) </summary>
|
||||
public bool IsLiquid( byte block ) { return block >= Block.Water && block <= Block.StillLava; }
|
||||
|
||||
@ -107,13 +94,9 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
public void SetBlockDraw( byte id, byte draw ) {
|
||||
IsTranslucent[id] = draw == DrawType.Translucent;
|
||||
IsAir[id] = draw == DrawType.Gas;
|
||||
IsSprite[id] = draw == DrawType.Sprite;
|
||||
if( draw == DrawType.Opaque && Collide[id] != CollideType.Solid )
|
||||
draw = DrawType.Transparent;
|
||||
Draw[id] = draw;
|
||||
|
||||
IsTransparent[id] = draw != DrawType.Opaque && draw != DrawType.Translucent;
|
||||
IsOpaque[id] = draw == DrawType.Opaque;
|
||||
}
|
||||
|
||||
public void ResetBlockProps( byte id ) {
|
||||
|
@ -142,7 +142,9 @@ namespace ClassicalSharp.Entities {
|
||||
while( posY >= 0 && index < 4 ) {
|
||||
byte block = GetShadowBlock( blockX, posY, blockZ );
|
||||
posY--;
|
||||
if( info.IsAir[block] || info.IsSprite[block] || info.IsLiquid( block ) ) continue;
|
||||
|
||||
byte draw = info.Draw[block];
|
||||
if( draw == DrawType.Gas || draw == DrawType.Sprite || info.IsLiquid( block ) ) continue;
|
||||
float blockY = posY + 1 + info.MaxBB[block].Y;
|
||||
if( blockY >= Position.Y + 0.01f ) continue;
|
||||
|
||||
@ -165,9 +167,9 @@ namespace ClassicalSharp.Entities {
|
||||
byte GetShadowBlock( int x, int y, int z ) {
|
||||
if( x < 0 || z < 0 || x >= game.World.Width || z >= game.World.Length ) {
|
||||
if( y == game.World.Env.EdgeHeight - 1 )
|
||||
return game.BlockInfo.IsAir[game.World.Env.EdgeBlock] ? Block.Air : Block.Bedrock;
|
||||
return game.BlockInfo.Draw[game.World.Env.EdgeBlock] == DrawType.Gas ? Block.Air : Block.Bedrock;
|
||||
if( y == game.World.Env.SidesHeight - 1 )
|
||||
return game.BlockInfo.IsAir[game.World.Env.SidesBlock] ? Block.Air : Block.Bedrock;
|
||||
return game.BlockInfo.Draw[game.World.Env.SidesBlock] == DrawType.Gas ? Block.Air : Block.Bedrock;
|
||||
return Block.Air;
|
||||
}
|
||||
return game.World.GetBlock( x, y, z );
|
||||
|
@ -78,7 +78,8 @@ namespace ClassicalSharp.Entities {
|
||||
CollideType collide = game.BlockInfo.Collide[b];
|
||||
if( newType != SoundType.None && collide != CollideType.Solid )
|
||||
sndType = newType;
|
||||
if( !game.BlockInfo.IsAir[b] )
|
||||
|
||||
if( game.BlockInfo.Draw[b] != DrawType.Gas )
|
||||
anyNonAir = true;
|
||||
return false;
|
||||
}
|
||||
@ -86,7 +87,8 @@ namespace ClassicalSharp.Entities {
|
||||
bool CheckSoundSolid( byte b ) {
|
||||
SoundType newType = game.BlockInfo.StepSounds[b];
|
||||
if( newType != SoundType.None ) sndType = newType;
|
||||
if( !game.BlockInfo.IsAir[b] )
|
||||
|
||||
if( game.BlockInfo.Draw[b] != DrawType.Gas )
|
||||
anyNonAir = true;
|
||||
return false;
|
||||
}
|
||||
|
@ -185,8 +185,10 @@ namespace ClassicalSharp {
|
||||
|
||||
// Need to render again over top of translucent block, as the selection outline
|
||||
// is drawn without writing to the depth buffer
|
||||
if( SelectedPos.Valid && !HideGui && BlockInfo.IsTranslucent[SelectedPos.Block] )
|
||||
if( SelectedPos.Valid && !HideGui
|
||||
&& BlockInfo.Draw[SelectedPos.Block] == DrawType.Translucent ) {
|
||||
Picking.Render( delta );
|
||||
}
|
||||
|
||||
Entities.DrawShadows();
|
||||
SelectionManager.Render( delta );
|
||||
|
@ -46,7 +46,7 @@ namespace ClassicalSharp {
|
||||
if( !game.World.IsValidPos( pos ) ) return;
|
||||
byte old = game.World.GetBlock( pos );
|
||||
|
||||
if( !info.IsAir[old] && (inv.CanPlace[old] || inv.CanDelete[old]) ) {
|
||||
if( info.Draw[old] != DrawType.Gas && (inv.CanPlace[old] || inv.CanDelete[old]) ) {
|
||||
for( int i = 0; i < inv.Hotbar.Length; i++ ) {
|
||||
if( inv.Hotbar[i] == old ) {
|
||||
inv.HeldBlockIndex = i; return;
|
||||
@ -59,7 +59,7 @@ namespace ClassicalSharp {
|
||||
if( !game.World.IsValidPos( pos ) ) return;
|
||||
byte old = game.World.GetBlock( pos );
|
||||
|
||||
if( !info.IsAir[old] && inv.CanDelete[old] ) {
|
||||
if( info.Draw[old] != DrawType.Gas && inv.CanDelete[old] ) {
|
||||
game.UpdateBlock( pos.X, pos.Y, pos.Z, 0 );
|
||||
game.UserEvents.RaiseBlockChanged( pos, old, 0 );
|
||||
}
|
||||
|
@ -167,10 +167,6 @@ namespace ClassicalSharp.Map {
|
||||
info.MinBB[id] = new Vector3( data[0] / 16f, data[1] / 16f, data[2] / 16f );
|
||||
info.MaxBB[id] = new Vector3( data[3] / 16f, data[4] / 16f, data[5] / 16f );
|
||||
|
||||
if( info.Collide[id] != CollideType.Solid ) {
|
||||
info.IsOpaque[id] = false;
|
||||
}
|
||||
|
||||
info.UpdateCulling( id );
|
||||
info.LightOffset[id] = info.CalcLightOffset( id );
|
||||
game.Events.RaiseBlockDefinitionChanged();
|
||||
|
@ -42,7 +42,7 @@ namespace ClassicalSharp {
|
||||
|
||||
static bool CameraClip( Game game, PickedPos pos ) {
|
||||
BlockInfo info = game.BlockInfo;
|
||||
if( info.IsAir[t.Block] || info.Collide[t.Block] != CollideType.Solid )
|
||||
if( info.Draw[t.Block] == DrawType.Gas || info.Collide[t.Block] != CollideType.Solid )
|
||||
return false;
|
||||
|
||||
float t0, t1;
|
||||
|
@ -59,7 +59,7 @@ namespace ClassicalSharp {
|
||||
int chunkIndex = (yy + 1) * extChunkSize2 + (zz + 1) * extChunkSize + (0 + 1);
|
||||
for( int x = x1, xx = 0; x < xMax; x++, xx++ ) {
|
||||
curBlock = chunk[chunkIndex];
|
||||
if( !info.IsAir[curBlock] ) {
|
||||
if( info.Draw[curBlock] != DrawType.Gas ) {
|
||||
int index = ((yy << 8) | (zz << 4) | xx) * Side.Sides;
|
||||
X = x; Y = y; Z = z;
|
||||
cIndex = chunkIndex;
|
||||
@ -94,11 +94,10 @@ namespace ClassicalSharp {
|
||||
chunkIndex++;
|
||||
if( x < 0 ) continue;
|
||||
if( x >= width ) break;
|
||||
|
||||
byte rawBlock = mapPtr[index];
|
||||
|
||||
allAir = allAir && rawBlock == 0;
|
||||
allSolid = allSolid && info.IsOpaque[rawBlock];
|
||||
allAir = allAir && info.Draw[rawBlock] == DrawType.Gas;
|
||||
allSolid = allSolid && info.Draw[rawBlock] == DrawType.Opaque;
|
||||
chunk[chunkIndex] = rawBlock;
|
||||
}
|
||||
}
|
||||
@ -177,12 +176,12 @@ namespace ClassicalSharp {
|
||||
for( int x = x1, xx = 0; x < xMax; x++, xx++ ) {
|
||||
cIndex++;
|
||||
byte b = chunk[cIndex];
|
||||
if( info.IsAir[b] ) continue;
|
||||
if( info.Draw[b] == DrawType.Gas ) continue;
|
||||
int index = ((yy << 8) + (zz << 4) + xx) * Side.Sides;
|
||||
|
||||
// Sprites only use one face to indicate stretching count, so we can take a shortcut here.
|
||||
// Note that sprites are not drawn with any of the DrawXFace, they are drawn using DrawSprite.
|
||||
if( info.IsSprite[b] ) {
|
||||
if( info.Draw[b] == DrawType.Sprite ) {
|
||||
index += Side.Top;
|
||||
if( counts[index] != 0 ) {
|
||||
X = x; Y = y; Z = z;
|
||||
@ -271,9 +270,12 @@ namespace ClassicalSharp {
|
||||
protected static int[] offsets = { -1, 1, -extChunkSize, extChunkSize, -extChunkSize2, extChunkSize2 };
|
||||
|
||||
protected bool OccludedLiquid( int chunkIndex ) {
|
||||
return info.IsOpaque[chunk[chunkIndex + 324]] && !info.IsAir[chunk[chunkIndex + 324 - 18]] &&
|
||||
!info.IsAir[chunk[chunkIndex + 324 - 1]] && !info.IsAir[chunk[chunkIndex + 324 + 1]] &&
|
||||
!info.IsAir[chunk[chunkIndex + 324 + 18]];
|
||||
return
|
||||
info.Draw[chunk[chunkIndex + 324]] == DrawType.Opaque
|
||||
&& info.Draw[chunk[chunkIndex + 324 - 18]] != DrawType.Gas
|
||||
&& info.Draw[chunk[chunkIndex + 324 - 1]] != DrawType.Gas
|
||||
&& info.Draw[chunk[chunkIndex + 324 + 1]] != DrawType.Gas
|
||||
&& info.Draw[chunk[chunkIndex + 324 + 18]] != DrawType.Gas;
|
||||
}
|
||||
|
||||
protected bool IsLit( int x, int y, int z, int face, byte type ) {
|
||||
|
@ -20,7 +20,7 @@ namespace ClassicalSharp {
|
||||
int chunkIndex = (y + 1) * extChunkSize2 + (z + 1) * extChunkSize + (0 + 1);
|
||||
for( int x = 0; x < 16; x++ ) {
|
||||
byte block = chunk[chunkIndex];
|
||||
if( info.IsOpaque[block] ) {
|
||||
if( info.Draw[block] == DrawType.Opaque ) {
|
||||
didFlags[flagIndex] |= (1 << x);
|
||||
} else if( (didFlags[flagIndex] & (1 << x)) == 0 ) {
|
||||
FloodFill( didFlags, stack, i, ref solidX, ref solidY, ref solidZ );
|
||||
@ -51,7 +51,7 @@ namespace ClassicalSharp {
|
||||
|
||||
int chunkIndex = (y + 1) * extChunkSize2 + (z + 1) * extChunkSize + (x + 1);
|
||||
byte block = chunk[chunkIndex];
|
||||
if( !info.IsOpaque[block] ) {
|
||||
if( info.Draw[block] != DrawType.Opaque ) {
|
||||
if( x == 0 )
|
||||
tX0 = true;
|
||||
else if( (didFlags[flagIndex] & (1 << (x - 1))) == 0 )
|
||||
|
@ -78,7 +78,7 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
void RenderTile( int index ) {
|
||||
if( info.IsSprite[curBlock] ) {
|
||||
if( info.Draw[curBlock] == DrawType.Sprite ) {
|
||||
fullBright = info.FullBright[curBlock];
|
||||
int count = counts[index + Side.Top];
|
||||
if( count != 0 ) DrawSprite( count );
|
||||
@ -92,7 +92,7 @@ namespace ClassicalSharp {
|
||||
backCount == 0 && bottomCount == 0 && topCount == 0 ) return;
|
||||
|
||||
fullBright = info.FullBright[curBlock];
|
||||
isTranslucent = info.IsTranslucent[curBlock];
|
||||
isTranslucent = info.Draw[curBlock] == DrawType.Translucent;
|
||||
lightFlags = info.LightOffset[curBlock];
|
||||
|
||||
Vector3 min = info.MinBB[curBlock], max = info.MaxBB[curBlock];
|
||||
@ -157,7 +157,7 @@ namespace ClassicalSharp {
|
||||
|
||||
unsafe void AddVertices( byte block, int count, int face ) {
|
||||
int i = atlas.Get1DIndex( info.GetTextureLoc( block, face ) );
|
||||
DrawInfo part = info.IsTranslucent[block] ? translucentParts[i] : normalParts[i];
|
||||
DrawInfo part = info.Draw[block] == DrawType.Translucent ? translucentParts[i] : normalParts[i];
|
||||
part.iCount += 6;
|
||||
|
||||
DrawInfoFaceData counts = part.vCount;
|
||||
|
@ -70,9 +70,6 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
|
||||
info.Name[id] = reader.ReadCp437String();
|
||||
info.Collide[id] = (CollideType)reader.ReadUInt8();
|
||||
if( info.Collide[id] != CollideType.Solid ) {
|
||||
info.IsOpaque[id] = false;
|
||||
}
|
||||
|
||||
info.SpeedMultiplier[id] = (float)Math.Pow( 2, (reader.ReadUInt8() - 128) / 64f );
|
||||
info.SetTex( reader.ReadUInt8(), Side.Top, id );
|
||||
|
@ -272,7 +272,9 @@ namespace ClassicalSharp.Renderers {
|
||||
ResetNeighbour( x, y, z + 1, block, cx, cy, cz + 1, minCy, maxCy );
|
||||
}
|
||||
|
||||
bool Needs( byte block, byte other ) { return !info.IsOpaque[block] || !info.IsAir[other]; }
|
||||
bool Needs( byte block, byte other ) {
|
||||
return info.Draw[block] != DrawType.Opaque || info.Draw[other] != DrawType.Gas;
|
||||
}
|
||||
|
||||
void ResetNeighbour( int x, int y, int z, byte block,
|
||||
int cx, int cy, int cz, int minCy, int maxCy ) {
|
||||
@ -297,7 +299,7 @@ namespace ClassicalSharp.Renderers {
|
||||
// Update if any blocks in the chunk are affected by light change
|
||||
for( ; y >= minY; y--) {
|
||||
byte other = world.blocks[index];
|
||||
bool affected = y == nY ? Needs( block, other ) : !info.IsAir[other];
|
||||
bool affected = y == nY ? Needs( block, other ) : info.Draw[other] != DrawType.Gas;
|
||||
if( affected ) { ResetChunk( cx, cy, cz ); return; }
|
||||
index -= world.Width * world.Length;
|
||||
}
|
||||
|
@ -208,14 +208,18 @@ namespace ClassicalSharp.Renderers {
|
||||
float HorOffset( byte block ) {
|
||||
BlockInfo info = game.BlockInfo;
|
||||
if( info.IsLiquid( block ) ) return -0.1f/16;
|
||||
if( info.IsTranslucent[block] && info.Collide[block] != CollideType.Solid ) return 0.1f/16;
|
||||
|
||||
if( info.Draw[block] == DrawType.Translucent
|
||||
&& info.Collide[block] != CollideType.Solid ) return 0.1f/16;
|
||||
return 0;
|
||||
}
|
||||
|
||||
float YOffset( byte block ) {
|
||||
BlockInfo info = game.BlockInfo;
|
||||
if( info.IsLiquid( block ) ) return -1.5f/16;
|
||||
if( info.IsTranslucent[block] && info.Collide[block] != CollideType.Solid ) return -0.1f/16;
|
||||
|
||||
if( info.Draw[block] == DrawType.Translucent
|
||||
&& info.Collide[block] != CollideType.Solid ) return -0.1f/16;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -168,8 +168,8 @@ namespace ClassicalSharp.Renderers {
|
||||
int CalcHeightAt( int x, int maxY, int z, int index ) {
|
||||
int mapIndex = ( maxY * length + z ) * width + x;
|
||||
for( int y = maxY; y >= 0; y-- ) {
|
||||
byte block = map.blocks[mapIndex];
|
||||
if( !(info.IsAir[block] || info.IsSprite[block]) ) {
|
||||
byte draw = info.Draw[map.blocks[mapIndex]];
|
||||
if( !(draw == DrawType.Gas || draw == DrawType.Sprite) ) {
|
||||
heightmap[index] = (short)y;
|
||||
return y;
|
||||
}
|
||||
@ -180,9 +180,9 @@ namespace ClassicalSharp.Renderers {
|
||||
}
|
||||
|
||||
internal void UpdateHeight( int x, int y, int z, byte oldBlock, byte newBlock ) {
|
||||
bool didBlock = !(info.IsAir[oldBlock] || info.IsSprite[oldBlock]);
|
||||
bool nowBlocks = !(info.IsAir[newBlock] || info.IsSprite[newBlock]);
|
||||
if( didBlock == nowBlocks ) return;
|
||||
bool didBlock = !(info.Draw[oldBlock] == DrawType.Gas || info.Draw[oldBlock] == DrawType.Sprite);
|
||||
bool nowBlock = !(info.Draw[newBlock] == DrawType.Gas || info.Draw[newBlock] == DrawType.Sprite);
|
||||
if( didBlock == nowBlock ) return;
|
||||
|
||||
int index = (x * length) + z;
|
||||
int height = heightmap[index];
|
||||
@ -192,7 +192,7 @@ namespace ClassicalSharp.Renderers {
|
||||
// useless if there is another block higher than block.y that stops rain.
|
||||
CalcHeightAt( x, maxY, z, index );
|
||||
} else if( y >= height ) {
|
||||
if( nowBlocks ) {
|
||||
if( nowBlock ) {
|
||||
heightmap[index] = (short)y;
|
||||
} else {
|
||||
// Part of the column is now visible to rain, we don't know how exactly how high it should be though.
|
||||
|
@ -47,7 +47,7 @@ namespace ClassicalSharp.Renderers {
|
||||
SetMatrix();
|
||||
game.Graphics.SetMatrixMode( MatrixType.Projection );
|
||||
game.Graphics.LoadMatrix( ref heldBlockProj );
|
||||
bool translucent = game.BlockInfo.IsTranslucent[type];
|
||||
bool translucent = game.BlockInfo.Draw[type] == DrawType.Translucent;
|
||||
|
||||
game.Graphics.Texturing = true;
|
||||
if( translucent ) game.Graphics.AlphaBlending = true;
|
||||
@ -79,13 +79,14 @@ namespace ClassicalSharp.Renderers {
|
||||
void SetPos() {
|
||||
// Based off details from http://pastebin.com/KFV0HkmD (Thanks goodlyay!)
|
||||
BlockInfo info = game.BlockInfo;
|
||||
Vector3 offset = info.IsSprite[type] ? sOffset : nOffset;
|
||||
bool sprite = info.Draw[type] == DrawType.Sprite;
|
||||
Vector3 offset = sprite ? sOffset : nOffset;
|
||||
Player p = game.LocalPlayer;
|
||||
held.ModelScale = 0.4f;
|
||||
|
||||
held.Position = p.EyePosition + anim.pos;
|
||||
held.Position += offset;
|
||||
if( info.Draw[type] != DrawType.Sprite ) {
|
||||
if( !sprite ) {
|
||||
float height = info.MaxBB[type].Y - info.MinBB[type].Y;
|
||||
held.Position.Y += 0.2f * (1 - height);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ namespace ClassicalSharp.Renderers {
|
||||
|
||||
byte block = game.World.SafeGetBlock( coords );
|
||||
bool outside = !game.World.IsValidPos( Vector3I.Floor( pos ) );
|
||||
inTranslucent = game.BlockInfo.IsTranslucent[block]
|
||||
inTranslucent = game.BlockInfo.Draw[block] == DrawType.Translucent
|
||||
|| (pos.Y < env.EdgeHeight && outside);
|
||||
|
||||
// If we are under water, render weather before to blend properly
|
||||
|
@ -50,7 +50,8 @@ namespace ClassicalSharp.Renderers {
|
||||
if( info.IsLiquid( selected.Block ) ) {
|
||||
p1.X -= 0.1f/16; p2.X -= 0.1f/16;
|
||||
p1.Z -= 0.1f/16; p2.Z -= 0.1f/16;
|
||||
} else if( info.IsTranslucent[selected.Block] && info.Collide[selected.Block] != CollideType.Solid ) {
|
||||
} else if( info.Draw[selected.Block] == DrawType.Translucent
|
||||
&& info.Collide[selected.Block] != CollideType.Solid ) {
|
||||
p1.X += 0.1f/16; p2.X += 0.1f/16;
|
||||
p1.Z += 0.1f/16; p2.Z += 0.1f/16;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user