mirror of
https://github.com/Melledy/Nebula.git
synced 2026-09-19 09:09:54 +02:00
feat(Activity): implement Soldier
This commit is contained in:
@@ -158,6 +158,21 @@ public class GameData {
|
|||||||
|
|
||||||
// Activity: Tower Defense
|
// Activity: Tower Defense
|
||||||
@Getter private static DataTable<TowerDefenseLevelDef> TowerDefenseLevelDataTable = new DataTable<>();
|
@Getter private static DataTable<TowerDefenseLevelDef> TowerDefenseLevelDataTable = new DataTable<>();
|
||||||
|
|
||||||
|
// Activity: Soldier
|
||||||
|
@Getter private static DataTable<SoldierGradeChallengeDef> SoldierGradeChallengeDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierSeasonDef> SoldierSeasonDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierNodePlanDef> SoldierNodePlanDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierCharacterDef> SoldierCharacterDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierStarterCardDef> SoldierStarterCardDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierFloorDef> SoldierFloorDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierShopPoolDef> SoldierShopPoolDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierShopLevelDef> SoldierShopLevelDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierEventPlanDef> SoldierEventPlanDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierEventBattlePoolDef> SoldierEventBattlePoolDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierStrategyCardDef> SoldierStrategyCardDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierCardEffectDef> SoldierCardEffectDataTable = new DataTable<>();
|
||||||
|
@Getter private static DataTable<SoldierBuffDef> SoldierBuffDataTable = new DataTable<>();
|
||||||
|
|
||||||
// Activity: Trials
|
// Activity: Trials
|
||||||
@Getter private static DataTable<TrialControlDef> TrialControlDataTable = new DataTable<>();
|
@Getter private static DataTable<TrialControlDef> TrialControlDataTable = new DataTable<>();
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierBuff.json")
|
||||||
|
public class SoldierBuffDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int Cond1;
|
||||||
|
private int Cond2;
|
||||||
|
private int Effect1;
|
||||||
|
private int Effect2;
|
||||||
|
private List<String> EffectParams1;
|
||||||
|
private List<String> EffectParams2;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierCardEffect.json")
|
||||||
|
public class SoldierCardEffectDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private List<Integer> BuffIds;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierCharacter.json")
|
||||||
|
public class SoldierCharacterDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int GroupId;
|
||||||
|
private int Rarity;
|
||||||
|
private int Cost;
|
||||||
|
private int MaxStar;
|
||||||
|
private int CharacterType;
|
||||||
|
private boolean BoardChess;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierEventBattlePool.json")
|
||||||
|
public class SoldierEventBattlePoolDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int PoolId;
|
||||||
|
private int LevelName;
|
||||||
|
private int AddDifficult;
|
||||||
|
private int weight;
|
||||||
|
private int FloorGroup;
|
||||||
|
private int coin;
|
||||||
|
private int CharacterCount;
|
||||||
|
private int StrategyCardCount;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWeight() {
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCoin() {
|
||||||
|
return coin;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierEventPlan.json")
|
||||||
|
public class SoldierEventPlanDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int EventGroupId;
|
||||||
|
private int EventType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierFloor.json")
|
||||||
|
public class SoldierFloorDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int Default;
|
||||||
|
private int BattleTime;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierGradeChallenge.json")
|
||||||
|
public class SoldierGradeChallengeDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int KeyGradeId;
|
||||||
|
private int GradeLevel;
|
||||||
|
private int NodeGroupId;
|
||||||
|
private int Score;
|
||||||
|
private int UnlockGradeLevel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierNodePlan.json")
|
||||||
|
public class SoldierNodePlanDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int NodeGroupId;
|
||||||
|
private int Stage;
|
||||||
|
private int Index;
|
||||||
|
private int NodeType;
|
||||||
|
private int EventGroupId;
|
||||||
|
private int DifficultyLevelAdd;
|
||||||
|
private int Coin;
|
||||||
|
private int Experience;
|
||||||
|
private int AddHp;
|
||||||
|
private int LoseHp;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierSeason.json")
|
||||||
|
public class SoldierSeasonDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int StarterGroupId;
|
||||||
|
private int StrategyGroupId;
|
||||||
|
private int ShopPoolGroupId;
|
||||||
|
private int ChessGroupId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierShopLevel.json")
|
||||||
|
public class SoldierShopLevelDef extends BaseDef {
|
||||||
|
private int Level;
|
||||||
|
private int Exp;
|
||||||
|
private int Count;
|
||||||
|
private int Rarity1;
|
||||||
|
private int Rarity2;
|
||||||
|
private int Rarity3;
|
||||||
|
private int Rarity4;
|
||||||
|
private int Rarity5;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int rarityWeight(int rarity) {
|
||||||
|
return switch (rarity) {
|
||||||
|
case 1 -> Rarity1;
|
||||||
|
case 2 -> Rarity2;
|
||||||
|
case 3 -> Rarity3;
|
||||||
|
case 4 -> Rarity4;
|
||||||
|
case 5 -> Rarity5;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierShopPool.json")
|
||||||
|
public class SoldierShopPoolDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int GroupId;
|
||||||
|
private int ChessCharacterId;
|
||||||
|
private int Rarity;
|
||||||
|
private int Weight;
|
||||||
|
private int Cost;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierStarterCard.json")
|
||||||
|
public class SoldierStarterCardDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int GroupId;
|
||||||
|
private int CardEffectId;
|
||||||
|
private int GradeLevelCond;
|
||||||
|
private List<Integer> CharacterShow;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package emu.nebula.data.resources;
|
||||||
|
|
||||||
|
import emu.nebula.data.BaseDef;
|
||||||
|
import emu.nebula.data.ResourceType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ResourceType(name = "SoldierStrategyCard.json")
|
||||||
|
public class SoldierStrategyCardDef extends BaseDef {
|
||||||
|
private int Id;
|
||||||
|
private int GroupId;
|
||||||
|
private int CardEffectId;
|
||||||
|
private int GradeLevelCond;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -138,6 +138,7 @@ public class ActivityManager extends PlayerManager implements GameDatabaseObject
|
|||||||
case Shop -> new ShopActivity(this, data);
|
case Shop -> new ShopActivity(this, data);
|
||||||
case TrekkerVersus -> new TrekkerVersusActivity(this, data);
|
case TrekkerVersus -> new TrekkerVersusActivity(this, data);
|
||||||
case PenguinCard -> new PenguinCardActivity(this, data);
|
case PenguinCard -> new PenguinCardActivity(this, data);
|
||||||
|
case Soldier -> new SoldierActivity(this, data);
|
||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ public class ActivityModule extends GameContextModule {
|
|||||||
|
|
||||||
// Fatebound Stellar Deck
|
// Fatebound Stellar Deck
|
||||||
//this.activities.add(800001); // Causes soft lock in event screen
|
//this.activities.add(800001); // Causes soft lock in event screen
|
||||||
|
|
||||||
|
// Through Hostile Depths (beta)
|
||||||
|
this.activities.add(900001);
|
||||||
|
|
||||||
// Trekker versus (broken)
|
// Trekker versus (broken)
|
||||||
//this.activities.add(600001);
|
//this.activities.add(600001);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
|||||||
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
|
import emu.nebula.game.activity.type.SoldierActivity;
|
||||||
|
import emu.nebula.net.GameSession;
|
||||||
|
import emu.nebula.net.HandlerId;
|
||||||
|
import emu.nebula.net.NetHandler;
|
||||||
|
import emu.nebula.net.NetMsgId;
|
||||||
|
import emu.nebula.proto.SoldierApply.SoldierApplyReq;
|
||||||
|
|
||||||
|
@HandlerId(NetMsgId.soldier_apply_req)
|
||||||
|
public class HandlerSoldierApplyReq extends NetHandler {
|
||||||
|
@Override
|
||||||
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
|
var req = SoldierApplyReq.parseFrom(message);
|
||||||
|
var activity = session.getPlayer().getActivityManager().getActivity(
|
||||||
|
SoldierActivity.class, req.getSeasonId());
|
||||||
|
if (activity == null) {
|
||||||
|
return session.encodeMsg(NetMsgId.soldier_apply_failed_ack);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rsp = activity.apply(req.getSeasonId(), req.getGradeChallengeId());
|
||||||
|
return rsp == null
|
||||||
|
? session.encodeMsg(NetMsgId.soldier_apply_failed_ack)
|
||||||
|
: session.encodeMsg(NetMsgId.soldier_apply_succeed_ack, rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
|
import emu.nebula.game.activity.type.SoldierActivity;
|
||||||
|
import emu.nebula.net.GameSession;
|
||||||
|
import emu.nebula.net.HandlerId;
|
||||||
|
import emu.nebula.net.NetHandler;
|
||||||
|
import emu.nebula.net.NetMsgId;
|
||||||
|
|
||||||
|
@HandlerId(NetMsgId.soldier_give_up_req)
|
||||||
|
public class HandlerSoldierGiveUpReq extends NetHandler {
|
||||||
|
@Override
|
||||||
|
public byte[] handle(GameSession session, byte[] message) {
|
||||||
|
var activity = session.getPlayer().getActivityManager().getFirstActivity(SoldierActivity.class);
|
||||||
|
var rsp = activity == null ? null : activity.giveUp();
|
||||||
|
return rsp == null
|
||||||
|
? session.encodeMsg(NetMsgId.soldier_give_up_failed_ack)
|
||||||
|
: session.encodeMsg(NetMsgId.soldier_give_up_succeed_ack, rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
|
import emu.nebula.game.activity.type.SoldierActivity;
|
||||||
|
import emu.nebula.net.GameSession;
|
||||||
|
import emu.nebula.net.HandlerId;
|
||||||
|
import emu.nebula.net.NetHandler;
|
||||||
|
import emu.nebula.net.NetMsgId;
|
||||||
|
|
||||||
|
@HandlerId(NetMsgId.soldier_info_req)
|
||||||
|
public class HandlerSoldierInfoReq extends NetHandler {
|
||||||
|
@Override
|
||||||
|
public byte[] handle(GameSession session, byte[] message) {
|
||||||
|
var activity = session.getPlayer().getActivityManager().getFirstActivity(SoldierActivity.class);
|
||||||
|
var info = activity == null ? null : activity.info();
|
||||||
|
return info == null
|
||||||
|
? session.encodeMsg(NetMsgId.soldier_info_failed_ack)
|
||||||
|
: session.encodeMsg(NetMsgId.soldier_info_succeed_ack, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
|
import emu.nebula.game.activity.type.SoldierActivity;
|
||||||
|
import emu.nebula.net.GameSession;
|
||||||
|
import emu.nebula.net.HandlerId;
|
||||||
|
import emu.nebula.net.NetHandler;
|
||||||
|
import emu.nebula.net.NetMsgId;
|
||||||
|
import emu.nebula.proto.SoldierInteract.SoldierInteractReq;
|
||||||
|
|
||||||
|
@HandlerId(NetMsgId.soldier_interact_req)
|
||||||
|
public class HandlerSoldierInteractReq extends NetHandler {
|
||||||
|
@Override
|
||||||
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
|
var req = SoldierInteractReq.parseFrom(message);
|
||||||
|
var activity = session.getPlayer().getActivityManager().getFirstActivity(SoldierActivity.class);
|
||||||
|
var rsp = activity == null ? null : activity.interact(req);
|
||||||
|
return rsp == null
|
||||||
|
? session.encodeMsg(NetMsgId.soldier_interact_failed_ack)
|
||||||
|
: session.encodeMsg(NetMsgId.soldier_interact_succeed_ack, rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
|
import emu.nebula.game.activity.type.SoldierActivity;
|
||||||
|
import emu.nebula.net.GameSession;
|
||||||
|
import emu.nebula.net.HandlerId;
|
||||||
|
import emu.nebula.net.NetHandler;
|
||||||
|
import emu.nebula.net.NetMsgId;
|
||||||
|
import emu.nebula.proto.SoldierPartnerTrace.SoldierPartnerTraceReq;
|
||||||
|
|
||||||
|
@HandlerId(NetMsgId.soldier_partner_trace_req)
|
||||||
|
public class HandlerSoldierPartnerTraceReq extends NetHandler {
|
||||||
|
@Override
|
||||||
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
|
var req = SoldierPartnerTraceReq.parseFrom(message);
|
||||||
|
var activity = session.getPlayer().getActivityManager().getFirstActivity(SoldierActivity.class);
|
||||||
|
if (activity == null || !activity.setPartnerTrace(req.getPartnerType(), req.getTrace())) {
|
||||||
|
return session.encodeMsg(NetMsgId.soldier_partner_trace_failed_ack);
|
||||||
|
}
|
||||||
|
return session.encodeMsg(NetMsgId.soldier_partner_trace_succeed_ack);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
|
import emu.nebula.game.activity.type.SoldierActivity;
|
||||||
|
import emu.nebula.net.GameSession;
|
||||||
|
import emu.nebula.net.HandlerId;
|
||||||
|
import emu.nebula.net.NetHandler;
|
||||||
|
import emu.nebula.net.NetMsgId;
|
||||||
|
import emu.nebula.proto.SoldierStepOut.SoldierStepOutReq;
|
||||||
|
|
||||||
|
@HandlerId(NetMsgId.soldier_step_out_req)
|
||||||
|
public class HandlerSoldierStepOutReq extends NetHandler {
|
||||||
|
@Override
|
||||||
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
|
var req = SoldierStepOutReq.parseFrom(message);
|
||||||
|
var activity = session.getPlayer().getActivityManager().getFirstActivity(SoldierActivity.class);
|
||||||
|
if (activity == null || !req.hasDeploy() || !activity.stepOut(req.getDeploy())) {
|
||||||
|
return session.encodeMsg(NetMsgId.soldier_step_out_failed_ack);
|
||||||
|
}
|
||||||
|
return session.encodeMsg(NetMsgId.soldier_step_out_succeed_ack);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user