mirror of
https://github.com/glest/glest-source.git
synced 2025-02-24 11:42:31 +01:00
saved games for rejoining in progress games saves to .zip and sends that to clients.
This commit is contained in:
parent
480c77949c
commit
c30d78ff74
@ -136,7 +136,8 @@ IF(BUILD_MEGAGLEST)
|
||||
|
||||
SET(GLEST_LIB_INCLUDE_ROOT "../shared_lib/include/")
|
||||
SET(GLEST_LIB_INCLUDE_DIRS
|
||||
${GLEST_LIB_INCLUDE_ROOT}platform/common
|
||||
${GLEST_LIB_INCLUDE_ROOT}compression
|
||||
${GLEST_LIB_INCLUDE_ROOT}platform/common
|
||||
${GLEST_LIB_INCLUDE_ROOT}platform/posix
|
||||
${GLEST_LIB_INCLUDE_ROOT}util
|
||||
${GLEST_LIB_INCLUDE_ROOT}graphics
|
||||
|
@ -25,12 +25,14 @@
|
||||
#include "auto_test.h"
|
||||
#include "menu_state_keysetup.h"
|
||||
#include "video_player.h"
|
||||
#include "compression_utils.h"
|
||||
|
||||
#include "leak_dumper.h"
|
||||
|
||||
using namespace Shared::Graphics;
|
||||
using namespace Shared::Util;
|
||||
using namespace Shared::Platform;
|
||||
using namespace Shared::CompressionUtil;
|
||||
|
||||
namespace Glest{ namespace Game{
|
||||
|
||||
@ -2355,6 +2357,26 @@ void Game::update() {
|
||||
//printf("Saved network game to disk\n");
|
||||
|
||||
string file = this->saveGame(GameConstants::saveNetworkGameFileServer,"temp/");
|
||||
|
||||
string saveGameFilePath = "temp/";
|
||||
string saveGameFileCompressed = saveGameFilePath + string(GameConstants::saveNetworkGameFileServerCompressed);
|
||||
if(getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) != "") {
|
||||
saveGameFilePath = getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) + saveGameFilePath;
|
||||
saveGameFileCompressed = saveGameFilePath + string(GameConstants::saveNetworkGameFileServerCompressed);
|
||||
}
|
||||
else {
|
||||
string userData = Config::getInstance().getString("UserData_Root","");
|
||||
if(userData != "") {
|
||||
endPathWithSlash(userData);
|
||||
}
|
||||
saveGameFilePath = userData + saveGameFilePath;
|
||||
saveGameFileCompressed = saveGameFilePath + string(GameConstants::saveNetworkGameFileServerCompressed);
|
||||
}
|
||||
|
||||
bool compressed_result = compressFileToZIPFile(
|
||||
file, saveGameFileCompressed);
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Saved game [%s] compressed to [%s] returned: %d\n",file.c_str(),saveGameFileCompressed.c_str(), compressed_result);
|
||||
|
||||
char szBuf[8096]="";
|
||||
Lang &lang= Lang::getInstance();
|
||||
snprintf(szBuf,8096,lang.get("GameSaved","",true).c_str(),file.c_str());
|
||||
|
@ -164,7 +164,9 @@ public:
|
||||
|
||||
static const char *application_name;
|
||||
|
||||
static const char *saveNetworkGameFileServerCompressed;
|
||||
static const char *saveNetworkGameFileServer;
|
||||
static const char *saveNetworkGameFileClientCompressed;
|
||||
static const char *saveNetworkGameFileClient;
|
||||
static const char *saveGameFileDefault;
|
||||
static const char *saveGameFileAutoTestDefault;
|
||||
|
@ -69,8 +69,12 @@ const char *GameConstants::path_data_CacheLookupKey = "data";
|
||||
const char *GameConstants::path_ini_CacheLookupKey = "ini";
|
||||
const char *GameConstants::path_logs_CacheLookupKey = "logs";
|
||||
|
||||
const char *GameConstants::saveNetworkGameFileServer = "megaglest-saved-server.xml";
|
||||
const char *GameConstants::saveNetworkGameFileClient = "megaglest-saved-client.xml";
|
||||
const char *GameConstants::saveNetworkGameFileServer = "megaglest-saved-server.xml";
|
||||
const char *GameConstants::saveNetworkGameFileServerCompressed = "megaglest-saved-server.zip";
|
||||
|
||||
const char *GameConstants::saveNetworkGameFileClient = "megaglest-saved-client.xml";
|
||||
const char *GameConstants::saveNetworkGameFileClientCompressed = "megaglest-saved-client.zip";
|
||||
|
||||
const char *GameConstants::saveGameFileDefault = "megaglest-saved.xml";
|
||||
const char *GameConstants::saveGameFileAutoTestDefault = "megaglest-auto-saved_%s.xml";
|
||||
const char *GameConstants::saveGameFilePattern = "megaglest-saved_%s.xml";
|
||||
|
@ -31,16 +31,19 @@
|
||||
#include "string_utils.h"
|
||||
#include "map_preview.h"
|
||||
#include <iterator>
|
||||
#include "compression_utils.h"
|
||||
|
||||
#include "leak_dumper.h"
|
||||
|
||||
using namespace Shared::Util;
|
||||
using namespace Shared::CompressionUtil;
|
||||
|
||||
namespace Glest{ namespace Game{
|
||||
|
||||
static const string ITEM_MISSING = "***missing***";
|
||||
const int HEADLESSSERVER_BROADCAST_SETTINGS_SECONDS = 4;
|
||||
static const char *HEADLESS_SAVED_GAME_FILENAME = "lastHeadlessGameSettings.mgg";
|
||||
|
||||
using namespace Shared::Util;
|
||||
|
||||
struct FormatString {
|
||||
void operator()(string &s) {
|
||||
s = formatString(s);
|
||||
@ -3211,7 +3214,12 @@ void MenuStateConnectedGame::update() {
|
||||
if(getInProgressSavedGameFromFTPServer == "") {
|
||||
//printf("Requesting saved game file\n");
|
||||
|
||||
ftpClientThread->addTempFileToRequests(GameConstants::saveNetworkGameFileClient,GameConstants::saveNetworkGameFileServer);
|
||||
// ftpClientThread->addTempFileToRequests(
|
||||
// GameConstants::saveNetworkGameFileClient,
|
||||
// GameConstants::saveNetworkGameFileServer);
|
||||
ftpClientThread->addTempFileToRequests(
|
||||
GameConstants::saveNetworkGameFileClientCompressed,
|
||||
GameConstants::saveNetworkGameFileServerCompressed);
|
||||
|
||||
getInProgressSavedGameFromFTPServer = GameConstants::saveNetworkGameFileClient;
|
||||
fileFTPProgressList[getInProgressSavedGameFromFTPServer] = pair<int,string>(0,"");
|
||||
@ -4188,6 +4196,28 @@ void MenuStateConnectedGame::FTPClient_CallbackEvent(string itemName,
|
||||
clientInterface->sendTextMessage(szMsg,-1, lang.isLanguageLocal(languageList[i]),languageList[i]);
|
||||
}
|
||||
|
||||
if(itemName == GameConstants::saveNetworkGameFileClientCompressed) {
|
||||
string saveGameFilePath = "temp/";
|
||||
string saveGameFile = saveGameFilePath + string(GameConstants::saveNetworkGameFileClientCompressed);
|
||||
if(getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) != "") {
|
||||
saveGameFilePath = getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) + saveGameFilePath;
|
||||
saveGameFile = saveGameFilePath + string(GameConstants::saveNetworkGameFileClientCompressed);
|
||||
}
|
||||
else {
|
||||
string userData = Config::getInstance().getString("UserData_Root","");
|
||||
if(userData != "") {
|
||||
endPathWithSlash(userData);
|
||||
}
|
||||
saveGameFilePath = userData + saveGameFilePath;
|
||||
saveGameFile = saveGameFilePath + string(GameConstants::saveNetworkGameFileClientCompressed);
|
||||
}
|
||||
|
||||
string extractedFileName = saveGameFilePath + string(GameConstants::saveNetworkGameFileClient);
|
||||
bool extract_result = extractFileFromZIPFile(
|
||||
saveGameFile,extractedFileName);
|
||||
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Saved game [%s] compressed to [%s] returned: %d\n",saveGameFile.c_str(),extractedFileName.c_str(), extract_result);
|
||||
}
|
||||
readyToJoinInProgressGame = true;
|
||||
|
||||
//printf("Success downloading saved game file: [%s]\n",itemName.c_str());
|
||||
|
@ -277,6 +277,7 @@ option(ENABLE_FRIBIDI "Enable FriBIDi support" ON)
|
||||
# megaglest lib
|
||||
|
||||
SET(DIRS_WITH_SRC
|
||||
compression
|
||||
feathery_ftp
|
||||
glew
|
||||
graphics
|
||||
@ -357,6 +358,7 @@ option(ENABLE_FRIBIDI "Enable FriBIDi support" ON)
|
||||
SET(MG_SOURCE_FILES "")
|
||||
INCLUDE_DIRECTORIES( ${MG_INCLUDES_ROOT}platform/sdl )
|
||||
INCLUDE_DIRECTORIES( ${MG_INCLUDES_ROOT}xml/rapidxml )
|
||||
INCLUDE_DIRECTORIES("sources/")
|
||||
|
||||
option(DISABLE_IRCCLIENT "Disable libircclient support" OFF)
|
||||
|
||||
|
26
source/shared_lib/include/compression/compression_utils.h
Normal file
26
source/shared_lib/include/compression/compression_utils.h
Normal file
@ -0,0 +1,26 @@
|
||||
// ==============================================================
|
||||
// This file is part of MegaGlest Shared Library (www.megaglest.org)
|
||||
//
|
||||
// Copyright (C) 2013 Mark Vejvoda
|
||||
//
|
||||
// You can redistribute this code and/or modify it under
|
||||
// the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version
|
||||
// ==============================================================
|
||||
|
||||
#ifndef _SHARED_COMPRESSION_UTIL_CHECKSUM_H_
|
||||
#define _SHARED_COMPRESSION_UTIL_CHECKSUM_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Shared{ namespace CompressionUtil{
|
||||
|
||||
bool compressFileToZIPFile(string inFile, string outFile, int compressionLevel=5);
|
||||
bool extractFileFromZIPFile(string inFile, string outFile);
|
||||
|
||||
}};
|
||||
|
||||
#endif
|
292
source/shared_lib/sources/compression/compression_utils.cpp
Normal file
292
source/shared_lib/sources/compression/compression_utils.cpp
Normal file
@ -0,0 +1,292 @@
|
||||
// ==============================================================
|
||||
// This file is part of MegaGlest Shared Library (www.megaglest.org)
|
||||
//
|
||||
// Copyright (C) 2013 Mark Vejvoda
|
||||
//
|
||||
// You can redistribute this code and/or modify it under
|
||||
// the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version
|
||||
// ==============================================================
|
||||
|
||||
// This file is based on example3.c - Demonstrates how to use miniz.c's deflate()
|
||||
// and inflate() functions for simple file compression.
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense"
|
||||
// statement at the end of tinfl.c.
|
||||
// For simplicity, this example is limited to files smaller than 4GB, but this
|
||||
// is not a limitation of miniz.c.
|
||||
|
||||
#include "compression_utils.h"
|
||||
#include "miniz/miniz.c"
|
||||
#include <limits.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "conversion.h"
|
||||
|
||||
using namespace Shared::Util;
|
||||
|
||||
namespace Shared{ namespace CompressionUtil{
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
#define BUF_SIZE (1024 * 1024)
|
||||
static uint8 s_inbuf[BUF_SIZE];
|
||||
static uint8 s_outbuf[BUF_SIZE];
|
||||
|
||||
int zipfile_tool(int argc, const char *argv[]) {
|
||||
const char *pMode = NULL;
|
||||
FILE *pInfile = NULL,
|
||||
*pOutfile = NULL;
|
||||
uint infile_size = 0;
|
||||
int level = Z_BEST_COMPRESSION;
|
||||
z_stream stream;
|
||||
int p = 1;
|
||||
const char *pSrc_filename = NULL;
|
||||
const char *pDst_filename = NULL;
|
||||
long file_loc = 0;
|
||||
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("miniz.c version: %s\n", MZ_VERSION);
|
||||
|
||||
if (argc < 4) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) {
|
||||
printf("Usage: example3 [options] [mode:c or d] infile outfile\n");
|
||||
printf("\nModes:\n");
|
||||
printf("c - Compresses file infile to a zlib stream in file outfile\n");
|
||||
printf("d - Decompress zlib stream in file infile to file outfile\n");
|
||||
printf("\nOptions:\n");
|
||||
printf("-l[0-10] - Compression level, higher values are slower.\n");
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((p < argc) && (argv[p][0] == '-')) {
|
||||
switch (argv[p][1]) {
|
||||
case 'l':
|
||||
{
|
||||
level = atoi(&argv[1][2]);
|
||||
if ((level < 0) || (level > 10)) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Invalid level!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Invalid option: %s\n", argv[p]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if ((argc - p) < 3) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Must specify mode, input filename, and output filename after options!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if ((argc - p) > 3) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Too many filenames!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pMode = argv[p++];
|
||||
if (!strchr("cCdD", pMode[0])) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Invalid mode!\n");
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pSrc_filename = argv[p++];
|
||||
pDst_filename = argv[p++];
|
||||
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED)printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename);
|
||||
|
||||
// Open input file.
|
||||
pInfile = fopen(pSrc_filename, "rb");
|
||||
if (!pInfile) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Failed opening input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Determine input file's size.
|
||||
fseek(pInfile, 0, SEEK_END);
|
||||
file_loc = ftell(pInfile);
|
||||
fseek(pInfile, 0, SEEK_SET);
|
||||
|
||||
if ((file_loc < 0) || (file_loc > INT_MAX)) {
|
||||
// This is not a limitation of miniz or tinfl, but this example.
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("File is too large to be processed by this example.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile_size = (uint)file_loc;
|
||||
|
||||
// Open output file.
|
||||
pOutfile = fopen(pDst_filename, "wb");
|
||||
if (!pOutfile) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Failed opening output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Input file size: %u\n", infile_size);
|
||||
|
||||
// Init the z_stream
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = 0;
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
|
||||
if ((pMode[0] == 'c') || (pMode[0] == 'C')) {
|
||||
// Compression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
if (deflateInit(&stream, level) != Z_OK) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("deflateInit() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for ( ; ; ) {
|
||||
int status;
|
||||
if (!stream.avail_in) {
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
//printf("Input bytes remaining: %u\n", infile_remaining);
|
||||
}
|
||||
|
||||
status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH);
|
||||
|
||||
if ((status == Z_STREAM_END) || (!stream.avail_out)) {
|
||||
// Output buffer is full, or compression is done, so write buffer to output file.
|
||||
uint n = BUF_SIZE - stream.avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == Z_STREAM_END) {
|
||||
break;
|
||||
}
|
||||
else if (status != Z_OK) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("deflate() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (deflateEnd(&stream) != Z_OK) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("deflateEnd() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if ((pMode[0] == 'd') || (pMode[0] == 'D')) {
|
||||
// Decompression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
if (inflateInit(&stream)) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("inflateInit() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for ( ; ; ) {
|
||||
int status;
|
||||
if (!stream.avail_in) {limited to files smaller than 4GB,
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
}
|
||||
|
||||
status = inflate(&stream, Z_SYNC_FLUSH);
|
||||
|
||||
if ((status == Z_STREAM_END) || (!stream.avail_out)) {
|
||||
// Output buffer is full, or decompression is done, so write buffer to output file.
|
||||
uint n = BUF_SIZE - stream.avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == Z_STREAM_END) {
|
||||
break;
|
||||
}
|
||||
else if (status != Z_OK) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("inflate() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (inflateEnd(&stream) != Z_OK) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("inflateEnd() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(pInfile);
|
||||
if (EOF == fclose(pOutfile)) {
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) {
|
||||
printf("Total input bytes: %u\n", (mz_uint32)stream.total_in);
|
||||
printf("Total output bytes: %u\n", (mz_uint32)stream.total_out);
|
||||
printf("Success.\n");
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
bool compressFileToZIPFile(string inFile, string outFile, int compressionLevel) {
|
||||
string options = "-l" + intToStr(compressionLevel);
|
||||
std::vector<const char *> argv;
|
||||
argv.push_back(options.c_str());
|
||||
argv.push_back("c");
|
||||
argv.push_back(inFile.c_str());
|
||||
argv.push_back(outFile.c_str());
|
||||
|
||||
int result = zipfile_tool(argv.size(), &argv[0]);
|
||||
return(result == EXIT_SUCCESS ? true : false);
|
||||
}
|
||||
|
||||
bool extractFileFromZIPFile(string inFile, string outFile) {
|
||||
std::vector<const char *> argv;
|
||||
argv.push_back("-l10");
|
||||
argv.push_back("d");
|
||||
argv.push_back(inFile.c_str());
|
||||
argv.push_back(outFile.c_str());
|
||||
|
||||
int result = zipfile_tool(argv.size(), &argv[0]);
|
||||
return(result == EXIT_SUCCESS ? true : false);
|
||||
}
|
||||
|
||||
}}
|
4834
source/shared_lib/sources/miniz/miniz.c
Normal file
4834
source/shared_lib/sources/miniz/miniz.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user