Add r_drawSceneModelBoundingBoxes debugging dvar to draw dobj and scene model bounding boxes
This commit is contained in:
parent
ff8e321836
commit
b21e57ccc2
@ -973,6 +973,41 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Dvar::OnInit([]
|
||||||
|
{
|
||||||
|
Dvar::Register<bool>("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes");
|
||||||
|
});
|
||||||
|
|
||||||
|
Scheduler::OnFrame([]()
|
||||||
|
{
|
||||||
|
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawSceneModelBoundingBoxes").get<bool>()) return;
|
||||||
|
|
||||||
|
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
float blue[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
|
||||||
|
|
||||||
|
auto* scene = Game::scene;
|
||||||
|
|
||||||
|
for(auto i = 0; i < scene->sceneModelCount; i++)
|
||||||
|
{
|
||||||
|
if(!scene->sceneModel[i].model)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto b = scene->sceneModel[i].model->bounds;
|
||||||
|
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
|
||||||
|
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
|
||||||
|
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
|
||||||
|
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
|
||||||
|
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
|
||||||
|
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
|
||||||
|
Game::R_AddDebugBounds(red, &b, &scene->sceneModel[i].placement.base.quat);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto i = 0; i < scene->sceneDObjCount; i++)
|
||||||
|
{
|
||||||
|
Game::R_AddDebugBounds(blue, &scene->sceneDObj[i].cull.bounds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Dvars
|
// Dvars
|
||||||
Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI");
|
Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI");
|
||||||
|
@ -419,6 +419,8 @@ namespace Game
|
|||||||
|
|
||||||
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
|
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
|
||||||
|
|
||||||
|
GfxScene* scene = reinterpret_cast<GfxScene*>(0x6944914);
|
||||||
|
|
||||||
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
|
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
|
||||||
{
|
{
|
||||||
int elSize = DB_GetXAssetSizeHandlers[type]();
|
int elSize = DB_GetXAssetSizeHandlers[type]();
|
||||||
@ -728,6 +730,29 @@ namespace Game
|
|||||||
std::memmove(&solution[0], &res[0], sizeof(res));
|
std::memmove(&solution[0], &res[0], sizeof(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuatRot(vec3_t* vec, const vec4_t* quat)
|
||||||
|
{
|
||||||
|
vec4_t q{ (*quat)[3],(*quat)[0],(*quat)[1],(*quat)[2] };
|
||||||
|
|
||||||
|
vec4_t res{ 0, (*vec)[0], (*vec)[1], (*vec)[2] };
|
||||||
|
vec4_t res2;
|
||||||
|
vec4_t quat_conj{ q[0], -q[1], -q[2], -q[3] };
|
||||||
|
QuatMultiply(&q, &res, &res2);
|
||||||
|
QuatMultiply(&res2, &quat_conj, &res);
|
||||||
|
|
||||||
|
(*vec)[0] = res[1];
|
||||||
|
(*vec)[1] = res[2];
|
||||||
|
(*vec)[2] = res[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res)
|
||||||
|
{
|
||||||
|
(*res)[0] = (*q2)[0] * (*q1)[0] - (*q2)[1] * (*q1)[1] - (*q2)[2] * (*q1)[2] - (*q2)[3] * (*q1)[3];
|
||||||
|
(*res)[1] = (*q2)[0] * (*q1)[1] + (*q2)[1] * (*q1)[0] - (*q2)[2] * (*q1)[3] + (*q2)[3] * (*q1)[2];
|
||||||
|
(*res)[2] = (*q2)[0] * (*q1)[2] + (*q2)[1] * (*q1)[3] + (*q2)[2] * (*q1)[0] - (*q2)[3] * (*q1)[1];
|
||||||
|
(*res)[3] = (*q2)[0] * (*q1)[3] - (*q2)[1] * (*q1)[2] + (*q2)[2] * (*q1)[1] + (*q2)[3] * (*q1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
void SortWorldSurfaces(GfxWorld* world)
|
void SortWorldSurfaces(GfxWorld* world)
|
||||||
{
|
{
|
||||||
DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C);
|
DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C);
|
||||||
@ -806,6 +831,73 @@ namespace Game
|
|||||||
Game::R_AddDebugLine(color, v4, v8);
|
Game::R_AddDebugLine(color, v4, v8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4])
|
||||||
|
{
|
||||||
|
vec3_t v[8];
|
||||||
|
auto* center = b->midPoint;
|
||||||
|
auto* halfSize = b->halfSize;
|
||||||
|
|
||||||
|
v[0][0] = -halfSize[0];
|
||||||
|
v[0][1] = -halfSize[1];
|
||||||
|
v[0][2] = -halfSize[2];
|
||||||
|
|
||||||
|
v[1][0] = halfSize[0];
|
||||||
|
v[1][1] = -halfSize[1];
|
||||||
|
v[1][2] = -halfSize[2];
|
||||||
|
|
||||||
|
v[2][0] = -halfSize[0];
|
||||||
|
v[2][1] = halfSize[1];
|
||||||
|
v[2][2] = -halfSize[2];
|
||||||
|
|
||||||
|
v[3][0] = halfSize[0];
|
||||||
|
v[3][1] = halfSize[1];
|
||||||
|
v[3][2] = -halfSize[2];
|
||||||
|
|
||||||
|
v[4][0] = -halfSize[0];
|
||||||
|
v[4][1] = -halfSize[1];
|
||||||
|
v[4][2] = halfSize[2];
|
||||||
|
|
||||||
|
v[5][0] = halfSize[0];
|
||||||
|
v[5][1] = -halfSize[1];
|
||||||
|
v[5][2] = halfSize[2];
|
||||||
|
|
||||||
|
v[6][0] = -halfSize[0];
|
||||||
|
v[6][1] = halfSize[1];
|
||||||
|
v[6][2] = halfSize[2];
|
||||||
|
|
||||||
|
v[7][0] = halfSize[0];
|
||||||
|
v[7][1] = halfSize[1];
|
||||||
|
v[7][2] = halfSize[2];
|
||||||
|
|
||||||
|
for(auto& vec : v)
|
||||||
|
{
|
||||||
|
QuatRot(&vec, quat);
|
||||||
|
vec[0] += center[0];
|
||||||
|
vec[1] += center[1];
|
||||||
|
vec[2] += center[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom
|
||||||
|
Game::R_AddDebugLine(color, v[0], v[1]);
|
||||||
|
Game::R_AddDebugLine(color, v[1], v[3]);
|
||||||
|
Game::R_AddDebugLine(color, v[3], v[2]);
|
||||||
|
Game::R_AddDebugLine(color, v[2], v[0]);
|
||||||
|
|
||||||
|
// top
|
||||||
|
Game::R_AddDebugLine(color, v[4], v[5]);
|
||||||
|
Game::R_AddDebugLine(color, v[5], v[7]);
|
||||||
|
Game::R_AddDebugLine(color, v[7], v[6]);
|
||||||
|
Game::R_AddDebugLine(color, v[6], v[4]);
|
||||||
|
|
||||||
|
// verticals
|
||||||
|
Game::R_AddDebugLine(color, v[0], v[4]);
|
||||||
|
Game::R_AddDebugLine(color, v[1], v[5]);
|
||||||
|
Game::R_AddDebugLine(color, v[2], v[6]);
|
||||||
|
Game::R_AddDebugLine(color, v[3], v[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma optimize("", off)
|
#pragma optimize("", off)
|
||||||
__declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/)
|
__declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/)
|
||||||
{
|
{
|
||||||
|
@ -858,6 +858,8 @@ namespace Game
|
|||||||
|
|
||||||
extern clientstate_t* clcState;
|
extern clientstate_t* clcState;
|
||||||
|
|
||||||
|
extern GfxScene* scene;
|
||||||
|
|
||||||
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
|
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
|
||||||
void Menu_FreeItemMemory(Game::itemDef_s* item);
|
void Menu_FreeItemMemory(Game::itemDef_s* item);
|
||||||
const char* TableLookup(StringTable* stringtable, int row, int column);
|
const char* TableLookup(StringTable* stringtable, int row, int column);
|
||||||
@ -906,9 +908,12 @@ namespace Game
|
|||||||
void Vec3Normalize(vec3_t& vec);
|
void Vec3Normalize(vec3_t& vec);
|
||||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
|
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
|
||||||
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
|
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
|
||||||
|
void QuatRot(vec3_t* vec, const vec4_t* quat);
|
||||||
|
void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res);
|
||||||
|
|
||||||
void SortWorldSurfaces(GfxWorld* world);
|
void SortWorldSurfaces(GfxWorld* world);
|
||||||
void R_AddDebugLine(float* color, float* v1, float* v2);
|
void R_AddDebugLine(float* color, float* v1, float* v2);
|
||||||
void R_AddDebugString(float *color, float *pos, float scale, const char *str);
|
void R_AddDebugString(float *color, float *pos, float scale, const char *str);
|
||||||
void R_AddDebugBounds(float* color, Bounds* b);
|
void R_AddDebugBounds(float* color, Bounds* b);
|
||||||
|
void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4]);
|
||||||
}
|
}
|
||||||
|
@ -4844,6 +4844,278 @@ namespace Game
|
|||||||
GfxCmdBufState *state;
|
GfxCmdBufState *state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GfxDrawGroupSetupFields
|
||||||
|
{
|
||||||
|
unsigned __int16 materialSortedIndex : 15;
|
||||||
|
unsigned __int16 useHeroLighting : 1;
|
||||||
|
char sceneLightIndex;
|
||||||
|
char surfType;
|
||||||
|
};
|
||||||
|
|
||||||
|
union GfxDrawGroupSetup
|
||||||
|
{
|
||||||
|
GfxDrawGroupSetupFields fields;
|
||||||
|
unsigned int packed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxMarkSurfLightingFields
|
||||||
|
{
|
||||||
|
char lmapIndex;
|
||||||
|
char reflectionProbeIndex;
|
||||||
|
unsigned __int16 modelIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
union GfxMarkSurfLighting
|
||||||
|
{
|
||||||
|
GfxMarkSurfLightingFields fields;
|
||||||
|
unsigned int packed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxMarkSurf
|
||||||
|
{
|
||||||
|
GfxDrawGroupSetup drawGroup;
|
||||||
|
unsigned __int16* indices;
|
||||||
|
unsigned __int16 triCount;
|
||||||
|
char modelType;
|
||||||
|
char pad;
|
||||||
|
GfxMarkSurfLighting lighting;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxCodeSurf
|
||||||
|
{
|
||||||
|
GfxDrawGroupSetup drawGroup;
|
||||||
|
unsigned int triCount;
|
||||||
|
unsigned __int16* indices;
|
||||||
|
unsigned __int16 argOffset;
|
||||||
|
unsigned __int16 argCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __declspec(align(4)) GfxGlassSurf
|
||||||
|
{
|
||||||
|
GfxDrawGroupSetup drawGroup;
|
||||||
|
char pad;
|
||||||
|
char reflectionProbeIndex;
|
||||||
|
unsigned __int16 triCount;
|
||||||
|
unsigned __int16* indices;
|
||||||
|
unsigned __int16 lightingHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxCloudSurfFields
|
||||||
|
{
|
||||||
|
unsigned __int16 materialSortedIndex;
|
||||||
|
char cloudDataIndex;
|
||||||
|
char surfType;
|
||||||
|
};
|
||||||
|
|
||||||
|
union GfxCloudSurf
|
||||||
|
{
|
||||||
|
GfxCloudSurfFields fields;
|
||||||
|
unsigned int packed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxSparkSurfFields
|
||||||
|
{
|
||||||
|
unsigned __int16 materialSortedIndex;
|
||||||
|
unsigned __int16 sparkDataIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
union GfxSparkSurf
|
||||||
|
{
|
||||||
|
GfxSparkSurfFields fields;
|
||||||
|
unsigned int packed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxSceneDef
|
||||||
|
{
|
||||||
|
int time;
|
||||||
|
float floatTime;
|
||||||
|
float viewOffset[3];
|
||||||
|
GfxImage* sunShadowImage;
|
||||||
|
float sunShadowPixelAdjust[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxLight
|
||||||
|
{
|
||||||
|
char type;
|
||||||
|
char canUseShadowMap;
|
||||||
|
char unused[2];
|
||||||
|
float color[3];
|
||||||
|
float dir[3];
|
||||||
|
float origin[3];
|
||||||
|
float radius;
|
||||||
|
float cosHalfFovOuter;
|
||||||
|
float cosHalfFovInner;
|
||||||
|
int exponent;
|
||||||
|
unsigned int spotShadowIndex;
|
||||||
|
GfxLightDef* def;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxVisibleLight
|
||||||
|
{
|
||||||
|
char pad[0x2004];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxEntity
|
||||||
|
{
|
||||||
|
unsigned int renderFxFlags;
|
||||||
|
float materialTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxSkinnedXModelSurfs
|
||||||
|
{
|
||||||
|
void* firstSurf;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxSceneEntityCull
|
||||||
|
{
|
||||||
|
volatile unsigned int state;
|
||||||
|
Bounds bounds;
|
||||||
|
GfxSkinnedXModelSurfs skinnedSurfs;
|
||||||
|
};
|
||||||
|
|
||||||
|
union GfxSceneEntityInfo
|
||||||
|
{
|
||||||
|
void/*cpose_t*/* pose;
|
||||||
|
unsigned __int16* cachedLightingHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxSceneEntity
|
||||||
|
{
|
||||||
|
float lightingOrigin[3];
|
||||||
|
GfxPlacement placement;
|
||||||
|
GfxSceneEntityCull cull;
|
||||||
|
char lods[32];
|
||||||
|
unsigned __int32 gfxEntIndex : 7;
|
||||||
|
unsigned __int32 entnum : 12;
|
||||||
|
unsigned __int32 renderFxFlags : 13;
|
||||||
|
void/*DObj*/* obj;
|
||||||
|
GfxSceneEntityInfo info;
|
||||||
|
char reflectionProbeIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxScaledPlacement
|
||||||
|
{
|
||||||
|
GfxPlacement base;
|
||||||
|
float scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxSceneModel
|
||||||
|
{
|
||||||
|
XModelDrawInfo info;
|
||||||
|
XModel* model;
|
||||||
|
void/*DObj*/* obj;
|
||||||
|
GfxScaledPlacement placement;
|
||||||
|
unsigned __int32 gfxEntIndex : 7;
|
||||||
|
unsigned __int32 entnum : 12;
|
||||||
|
unsigned __int32 renderFxFlags : 13;
|
||||||
|
float radius;
|
||||||
|
unsigned __int16* cachedLightingHandle;
|
||||||
|
float lightingOrigin[3];
|
||||||
|
char reflectionProbeIndex;
|
||||||
|
char lod;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __declspec(align(4)) GfxSceneBrush
|
||||||
|
{
|
||||||
|
BModelDrawInfo info;
|
||||||
|
unsigned __int16 entnum;
|
||||||
|
GfxBrushModel* bmodel;
|
||||||
|
GfxPlacement placement;
|
||||||
|
char reflectionProbeIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
union GfxSceneGlass
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
bool rendered;
|
||||||
|
char reflectionProbeIndex;
|
||||||
|
unsigned __int16 lightingHandle;
|
||||||
|
};
|
||||||
|
unsigned int packed;
|
||||||
|
};
|
||||||
|
|
||||||
|
union GfxEntCellRefInfo
|
||||||
|
{
|
||||||
|
float radius;
|
||||||
|
GfxBrushModel* bmodel;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GfxSceneDpvs
|
||||||
|
{
|
||||||
|
unsigned int localClientNum;
|
||||||
|
char* entVisData[7];
|
||||||
|
unsigned __int16* sceneXModelIndex;
|
||||||
|
unsigned __int16* sceneDObjIndex;
|
||||||
|
GfxEntCellRefInfo* entInfo[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __declspec(align(64)) GfxScene
|
||||||
|
{
|
||||||
|
GfxCodeSurf codeEmissiveSurfs[2048];
|
||||||
|
GfxCodeSurf codeTransSurfs[640];
|
||||||
|
GfxMarkSurf markSurfs[1536];
|
||||||
|
GfxGlassSurf glassSurfs[768];
|
||||||
|
GfxCloudSurf cloudSurfs[256];
|
||||||
|
GfxDrawSurf drawSurfsDepthHack[32];
|
||||||
|
GfxDrawSurf drawSurfsLitOpaque[8192];
|
||||||
|
GfxDrawSurf drawSurfsLitTrans[2048];
|
||||||
|
GfxDrawSurf drawSurfsEmissive[8192];
|
||||||
|
GfxDrawSurf drawSurfsSunShadow0[4096];
|
||||||
|
GfxDrawSurf drawSurfsSunShadow1[8192];
|
||||||
|
GfxDrawSurf drawSurfsSpotShadow0[896];
|
||||||
|
GfxDrawSurf drawSurfsSpotShadow1[896];
|
||||||
|
GfxDrawSurf drawSurfsSpotShadow2[896];
|
||||||
|
GfxDrawSurf drawSurfsSpotShadow3[896];
|
||||||
|
unsigned int sceneLightIsUsed[32];
|
||||||
|
unsigned int cachedSceneLightIsUsed[4][32];
|
||||||
|
GfxSparkSurf sparkSurfs[64];
|
||||||
|
unsigned int drawSurfLimit[10];
|
||||||
|
volatile int drawSurfCount[10];
|
||||||
|
GfxDrawSurf* drawSurfs[10];
|
||||||
|
volatile int codeSurfUser[2];
|
||||||
|
volatile int markMeshGuard;
|
||||||
|
unsigned int codeEmissiveSurfCount;
|
||||||
|
unsigned int codeTransSurfCount;
|
||||||
|
unsigned int markSurfCount;
|
||||||
|
unsigned int glassSurfCount;
|
||||||
|
GfxSceneDef def;
|
||||||
|
unsigned int addedLightCount;
|
||||||
|
GfxLight addedLight[32];
|
||||||
|
bool isAddedLightCulled[32];
|
||||||
|
float dynamicSpotLightNearPlaneOffset;
|
||||||
|
float dynamicSpotLightLength;
|
||||||
|
GfxVisibleLight visLight[4];
|
||||||
|
GfxVisibleLight visLightShadow[1];
|
||||||
|
unsigned int* entOverflowedDrawBuf;
|
||||||
|
volatile int gfxEntCount;
|
||||||
|
GfxEntity gfxEnts[128];
|
||||||
|
int sceneDObjCount;
|
||||||
|
int preClientSceneDObjCount;
|
||||||
|
int sceneDObjCountAtMark;
|
||||||
|
GfxSceneEntity sceneDObj[520];
|
||||||
|
char sceneDObjVisData[7][512];
|
||||||
|
int sceneDObjMarkableViewmodelIndex;
|
||||||
|
unsigned int sceneDObjFirstViewmodelIndex;
|
||||||
|
unsigned int sceneDObjViewmodelCount;
|
||||||
|
volatile int sceneModelCount;
|
||||||
|
int sceneModelCountAtMark;
|
||||||
|
int sceneDObjModelCount;
|
||||||
|
GfxSceneModel sceneModel[1024];
|
||||||
|
char sceneModelVisData[7][1024];
|
||||||
|
volatile int sceneBrushCount;
|
||||||
|
int sceneBrushCountAtMark;
|
||||||
|
GfxSceneBrush sceneBrush[512];
|
||||||
|
char sceneBrushVisData[3][512];
|
||||||
|
GfxSceneGlass sceneGlass[1024];
|
||||||
|
unsigned int sceneDynModelCount;
|
||||||
|
unsigned int sceneDynBrushCount;
|
||||||
|
int gfxEntCountAtMark;
|
||||||
|
GfxSceneDpvs dpvs;
|
||||||
|
int updateSound;
|
||||||
|
int allowAddDObj;
|
||||||
|
};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#ifndef IDA
|
#ifndef IDA
|
||||||
|
Loading…
Reference in New Issue
Block a user