Merge pull request #3 from Akka0/development

Development
This commit is contained in:
Akka 2022-05-07 00:19:59 +08:00 committed by GitHub
commit 6b82a5360c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 691 additions and 326 deletions

View File

@ -0,0 +1,15 @@
syntax = "proto3";
option csharp_namespace = "YSFreedom.Common.Protocol";
message WorldPlayerReviveReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 288;
}
}

View File

@ -99,6 +99,7 @@ public class DungeonManager {
// clean temp team if it has // clean temp team if it has
player.getTeamManager().cleanTemporaryTeam(); player.getTeamManager().cleanTemporaryTeam();
player.getTowerManager().clearEntry(); player.getTowerManager().clearEntry();
// Transfer player back to world // Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos); player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp)); player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));

View File

@ -104,6 +104,11 @@ public class EntityAvatar extends GameEntity {
this.killedType = PlayerDieType.PLAYER_DIE_KILL_BY_MONSTER; this.killedType = PlayerDieType.PLAYER_DIE_KILL_BY_MONSTER;
this.killedBy = killerId; this.killedBy = killerId;
} }
public void onDeath(PlayerDieType dieType, int killerId) {
this.killedType = dieType;
this.killedBy = killerId;
}
public SceneAvatarInfo getSceneAvatarInfo() { public SceneAvatarInfo getSceneAvatarInfo() {
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()

View File

@ -1,227 +0,0 @@
package emu.grasscutter.game.managers.MotionManager;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.VectorOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketPlayerPropNotify;
import emu.grasscutter.utils.Position;
import java.util.ArrayList;
import java.lang.Math;
public class MotionManager {
private enum Consumption {
None(0),
// consumers
CLIMB_START(-500),
CLIMBING(-150),
CLIMB_JUMP(-2500),
DASH(-1800),
SPRINT(-360),
FLY(-60),
SWIM_DASH_START(-200),
SWIM_DASH(-200),
SWIMMING(-80),
// restorers
STANDBY(500),
RUN(500),
WALK(500),
STANDBY_MOVE(500);
public final int amount;
Consumption(int amount) {
this.amount = amount;
}
}
private EntityMoveInfoOuterClass.EntityMoveInfo moveInfo;
private MotionState previousState = MotionState.MOTION_STANDBY;
private ArrayList<Position> previousCoordinates = new ArrayList<>();
private final Player player;
private float landSpeed = 0;
public MotionManager(Player player) {
previousCoordinates.add(new Position(0,0,0));
this.player = player;
}
public void handle(GameSession session, GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
MotionState state = moveInfo.getMotionInfo().getState();
setMoveInfo(moveInfo);
if (state == MotionState.MOTION_LAND_SPEED) {
setLandSpeed(moveInfo.getMotionInfo().getSpeed().getY());
}
if (state == MotionState.MOTION_FALL_ON_GROUND) {
handleFallOnGround(session, entity);
}
}
public void tick() {
if(Grasscutter.getConfig().OpenStamina){
EntityMoveInfoOuterClass.EntityMoveInfo mInfo = moveInfo;
if (mInfo == null) {
return;
}
MotionState state = moveInfo.getMotionInfo().getState();
Consumption consumption = Consumption.None;
boolean isMoving = false;
VectorOuterClass.Vector posVector = moveInfo.getMotionInfo().getPos();
Position currentCoordinates = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
float diffX = currentCoordinates.getX() - previousCoordinates.get(0).getX();
float diffY = currentCoordinates.getY() - previousCoordinates.get(0).getY();
float diffZ = currentCoordinates.getZ() - previousCoordinates.get(0).getZ();
if (Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.3 || Math.abs(diffZ) > 0.3) {
isMoving = true;
}
if (isMoving) {
// TODO: refactor these conditions.
// CLIMB
if (state == MotionState.MOTION_CLIMB) {
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_START;
} else {
consumption = Consumption.CLIMBING;
}
}
// JUMP
if (state == MotionState.MOTION_CLIMB_JUMP) {
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_JUMP;
}
}
if (state == MotionState.MOTION_JUMP) {
if (previousState == MotionState.MOTION_CLIMB) {
consumption = Consumption.CLIMB_JUMP;
}
}
// SWIM
if (state == MotionState.MOTION_SWIM_MOVE) {
consumption = Consumption.SWIMMING;
}
if (state == MotionState.MOTION_SWIM_DASH) {
if (previousState != MotionState.MOTION_SWIM_DASH) {
consumption = Consumption.SWIM_DASH_START;
} else {
consumption = Consumption.SWIM_DASH;
}
}
// DASH
if (state == MotionState.MOTION_DASH) {
if (previousState == MotionState.MOTION_DASH) {
consumption = Consumption.SPRINT;
} else {
consumption = Consumption.DASH;
}
}
// RUN and WALK
if (state == MotionState.MOTION_RUN) {
consumption = Consumption.RUN;
}
if (state == MotionState.MOTION_WALK) {
consumption = Consumption.WALK;
}
// FLY
if (state == MotionState.MOTION_FLY) {
consumption = Consumption.FLY;
}
}
// STAND
if (state == MotionState.MOTION_STANDBY) {
consumption = Consumption.STANDBY;
}
if (state == MotionState.MOTION_STANDBY_MOVE) {
consumption = Consumption.STANDBY_MOVE;
}
GameSession session = player.getSession();
updateStamina(session, consumption.amount);
session.send(new PacketPlayerPropNotify(session.getPlayer(), PlayerProperty.PROP_CUR_PERSIST_STAMINA));
Grasscutter.getLogger().debug(session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + " " + state + " " + isMoving + " " + consumption + " " + consumption.amount);
previousState = state;
previousCoordinates.add(currentCoordinates);
if (previousCoordinates.size() > 3) {
previousCoordinates.remove(0);
}
}
}
public void updateStamina(GameSession session, int amount) {
if (amount == 0) {
return;
}
int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
int newStamina = currentStamina + amount;
if (newStamina < 0) {
newStamina = 0;
}
if (newStamina > playerMaxStamina) {
newStamina = playerMaxStamina;
}
session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
}
public void setMoveInfo(EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
this.moveInfo = moveInfo;
}
public EntityMoveInfoOuterClass.EntityMoveInfo getMoveInfo() {
return moveInfo;
}
public void handleFallOnGround(GameSession session, GameEntity entity) {
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float damage = 0;
Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
if (landSpeed < -23.5) {
damage = (float)(maxHP * 0.33);
}
if (landSpeed < -25) {
damage = (float)(maxHP * 0.5);
}
if (landSpeed < -26.5) {
damage = (float)(maxHP * 0.66);
}
if (landSpeed < -28) {
damage = (maxHP * 1);
}
float newHP = currentHP - damage;
if (newHP < 0) {
newHP = 0;
}
Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
if (newHP == 0) {
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
session.getPlayer().getScene().removeEntity(entity);
entity.onDeath(0);
}
}
public void setLandSpeed(float landSpeed) {
this.landSpeed = landSpeed;
}
}

View File

@ -0,0 +1,359 @@
package emu.grasscutter.game.managers.MovementManager;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.VectorOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
import org.jetbrains.annotations.NotNull;
import java.lang.Math;
import java.util.*;
public class MovementManager {
public HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>();
private enum Consumption {
None(0),
// consume
CLIMB_START(-500),
CLIMBING(-150),
CLIMB_JUMP(-2500),
DASH(-1800),
SPRINT(-360),
FLY(-60),
SWIM_DASH_START(-200),
SWIM_DASH(-200),
SWIMMING(-80),
// restore
STANDBY(500),
RUN(500),
WALK(500),
STANDBY_MOVE(500),
POWERED_FLY(500);
public final int amount;
Consumption(int amount) {
this.amount = amount;
}
}
private MotionState previousState = MotionState.MOTION_STANDBY;
private MotionState currentState = MotionState.MOTION_STANDBY;
private Position previousCoordinates = new Position(0, 0, 0);
private Position currentCoordinates = new Position(0, 0, 0);
private final Player player;
private float landSpeed = 0;
private Timer movementManagerTickTimer;
private GameSession cachedSession = null;
private GameEntity cachedEntity = null;
private int staminaRecoverDelay = 0;
public MovementManager(Player player) {
previousCoordinates.add(new Position(0,0,0));
this.player = player;
MotionStatesCategorized.put("SWIM", new HashSet<>(Arrays.asList(
MotionState.MOTION_SWIM_MOVE,
MotionState.MOTION_SWIM_IDLE,
MotionState.MOTION_SWIM_DASH,
MotionState.MOTION_SWIM_JUMP
)));
MotionStatesCategorized.put("STANDBY", new HashSet<>(Arrays.asList(
MotionState.MOTION_STANDBY,
MotionState.MOTION_STANDBY_MOVE,
MotionState.MOTION_DANGER_STANDBY,
MotionState.MOTION_DANGER_STANDBY_MOVE,
MotionState.MOTION_LADDER_TO_STANDBY,
MotionState.MOTION_JUMP_UP_WALL_FOR_STANDBY
)));
MotionStatesCategorized.put("CLIMB", new HashSet<>(Arrays.asList(
MotionState.MOTION_CLIMB,
MotionState.MOTION_CLIMB_JUMP,
MotionState.MOTION_STANDBY_TO_CLIMB,
MotionState.MOTION_LADDER_IDLE,
MotionState.MOTION_LADDER_MOVE,
MotionState.MOTION_LADDER_SLIP,
MotionState.MOTION_STANDBY_TO_LADDER
)));
MotionStatesCategorized.put("FLY", new HashSet<>(Arrays.asList(
MotionState.MOTION_FLY,
MotionState.MOTION_FLY_IDLE,
MotionState.MOTION_FLY_SLOW,
MotionState.MOTION_FLY_FAST,
MotionState.MOTION_POWERED_FLY
)));
MotionStatesCategorized.put("RUN", new HashSet<>(Arrays.asList(
MotionState.MOTION_DASH,
MotionState.MOTION_DANGER_DASH,
MotionState.MOTION_DASH_BEFORE_SHAKE,
MotionState.MOTION_RUN,
MotionState.MOTION_DANGER_RUN,
MotionState.MOTION_WALK,
MotionState.MOTION_DANGER_WALK
)));
}
public void handle(GameSession session, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo, GameEntity entity) {
if (movementManagerTickTimer == null) {
movementManagerTickTimer = new Timer();
movementManagerTickTimer.scheduleAtFixedRate(new MotionManagerTick(), 0, 200);
}
// cache info for later use in tick
cachedSession = session;
cachedEntity = entity;
MotionInfo motionInfo = moveInfo.getMotionInfo();
moveEntity(entity, moveInfo);
VectorOuterClass.Vector posVector = motionInfo.getPos();
Position newPos = new Position(posVector.getX(),
posVector.getY(), posVector.getZ());;
if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) {
currentCoordinates = newPos;
}
currentState = motionInfo.getState();
Grasscutter.getLogger().debug("" + currentState);
handleFallOnGround(motionInfo);
}
public void resetTimer() {
movementManagerTickTimer.cancel();
movementManagerTickTimer = null;
}
private void moveEntity(GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
entity.getPosition().set(moveInfo.getMotionInfo().getPos());
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
entity.setMotionState(moveInfo.getMotionInfo().getState());
}
private boolean isPlayerMoving() {
float diffX = currentCoordinates.getX() - previousCoordinates.getX();
float diffY = currentCoordinates.getY() - previousCoordinates.getY();
float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ();
// Grasscutter.getLogger().debug("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates + ", " + diffX + ", " + diffY + ", " + diffZ);
return Math.abs(diffX) > 0.2 || Math.abs(diffY) > 0.1 || Math.abs(diffZ) > 0.2;
}
private int getCurrentStamina() {
return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
}
private int getMaximumStamina() {
return player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
}
// Returns new stamina
public int updateStamina(GameSession session, int amount) {
int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
if (amount == 0) {
return currentStamina;
}
int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
int newStamina = currentStamina + amount;
if (newStamina < 0) {
newStamina = 0;
}
if (newStamina > playerMaxStamina) {
newStamina = playerMaxStamina;
}
session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
return newStamina;
}
private void handleFallOnGround(@NotNull MotionInfo motionInfo) {
MotionState state = motionInfo.getState();
// land speed and fall on ground event arrive in different packets
// cache land speed
if (state == MotionState.MOTION_LAND_SPEED) {
landSpeed = motionInfo.getSpeed().getY();
}
if (state == MotionState.MOTION_FALL_ON_GROUND) {
float currentHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float damage = 0;
// Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
if (landSpeed < -23.5) {
damage = (float)(maxHP * 0.33);
}
if (landSpeed < -25) {
damage = (float)(maxHP * 0.5);
}
if (landSpeed < -26.5) {
damage = (float)(maxHP * 0.66);
}
if (landSpeed < -28) {
damage = (maxHP * 1);
}
float newHP = currentHP - damage;
if (newHP < 0) {
newHP = 0;
}
// Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
if (newHP == 0) {
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_FALL);
}
landSpeed = 0;
}
}
private void handleDrowning() {
int stamina = getCurrentStamina();
if (stamina < 10) {
boolean isSwimming = MotionStatesCategorized.get("SWIM").contains(currentState);
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + isSwimming);
if (isSwimming && currentState != MotionState.MOTION_SWIM_IDLE) {
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_DRAWN);
}
}
}
public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) {
cachedSession.send(new PacketAvatarLifeStateChangeNotify(
cachedSession.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(),
LifeState.LIFE_DEAD,
dieType
));
cachedSession.send(new PacketLifeStateChangeNotify(
cachedEntity,
LifeState.LIFE_DEAD,
dieType
));
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0);
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
session.getPlayer().getScene().removeEntity(entity);
((EntityAvatar)entity).onDeath(dieType, 0);
}
private class MotionManagerTick extends TimerTask
{
public void run() {
if (Grasscutter.getConfig().OpenStamina) {
boolean moving = isPlayerMoving();
if (moving || (getCurrentStamina() < getMaximumStamina())) {
Grasscutter.getLogger().debug("Player moving: " + moving + ", stamina full: " + (getCurrentStamina() >= getMaximumStamina()) + ", recalculate stamina");
Consumption consumption = Consumption.None;
// TODO: refactor these conditions.
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
if (currentState == MotionState.MOTION_CLIMB) {
// CLIMB
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_START;
} else {
consumption = Consumption.CLIMBING;
}
}
if (currentState == MotionState.MOTION_CLIMB_JUMP) {
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_JUMP;
}
}
if (currentState == MotionState.MOTION_JUMP) {
if (previousState == MotionState.MOTION_CLIMB) {
consumption = Consumption.CLIMB_JUMP;
}
}
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
// SWIM
if (currentState == MotionState.MOTION_SWIM_MOVE) {
consumption = Consumption.SWIMMING;
}
if (currentState == MotionState.MOTION_SWIM_DASH) {
if (previousState != MotionState.MOTION_SWIM_DASH) {
consumption = Consumption.SWIM_DASH_START;
} else {
consumption = Consumption.SWIM_DASH;
}
}
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
// RUN, DASH and WALK
// DASH
if (currentState == MotionState.MOTION_DASH) {
if (previousState == MotionState.MOTION_DASH) {
consumption = Consumption.SPRINT;
} else {
// cost more to start dashing
consumption = Consumption.DASH;
}
}
// RUN
if (currentState == MotionState.MOTION_RUN) {
consumption = Consumption.RUN;
}
// WALK
if (currentState == MotionState.MOTION_WALK) {
consumption = Consumption.WALK;
}
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
// FLY
consumption = Consumption.FLY;
// POWERED_FLY, e.g. wind tunnel
if (currentState == MotionState.MOTION_POWERED_FLY) {
consumption = Consumption.POWERED_FLY;
}
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
// STAND
if (currentState == MotionState.MOTION_STANDBY) {
consumption = Consumption.STANDBY;
}
if (currentState == MotionState.MOTION_STANDBY_MOVE) {
consumption = Consumption.STANDBY_MOVE;
}
}
// tick triggered
handleDrowning();
if (cachedSession != null) {
if (consumption.amount < 0) {
staminaRecoverDelay = 0;
}
if (consumption.amount > 0) {
if (staminaRecoverDelay < 5) {
staminaRecoverDelay++;
consumption = Consumption.None;
}
}
int newStamina = updateStamina(cachedSession, consumption.amount);
cachedSession.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + "isMoving: " + isPlayerMoving() + "\t" + consumption + "(" + consumption.amount + ")");
}
}
}
previousState = currentState;
previousCoordinates = new Position(currentCoordinates.getX(),
currentCoordinates.getY(), currentCoordinates.getZ());;
}
}
}

View File

@ -0,0 +1,147 @@
package emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.List;
// Statue of the Seven Manager
public class SotSManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
private final Player player;
public SotSManager(Player player) {
this.player = player;
}
public boolean getIsAutoRecoveryEnabled() {
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
}
public void setIsAutoRecoveryEnabled(boolean enabled) {
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
}
public int getAutoRecoveryPercentage() {
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
}
public void setAutoRecoveryPercentage(int percentage) {
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
}
// autoRevive automatically revives all team members.
public void autoRevive(GameSession session) {
player.getTeamManager().getActiveTeam().forEach(entity -> {
boolean isAlive = entity.isAlive();
if (!isAlive) {
float maxHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float newHP = (float)(maxHP * 0.3);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
}
});
}
public void scheduleAutoRecover(GameSession session) {
// TODO: play audio effects? possibly client side? - client automatically plays.
// delay 2.5 seconds
new Thread(() -> {
try {
Thread.sleep(2500);
autoRecover(session);
} catch (Exception e) {
Grasscutter.getLogger().error(e.getMessage());
}
}).start();
}
public void refillSpringVolume() {
// TODO: max spring volume depends on level of the statues in Mondstadt and Liyue.
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, 8500000);
long now = System.currentTimeMillis() / 1000;
long secondsSinceLastUsed = now - player.getSpringLastUsed();
float percentageRefilled = (float)secondsSinceLastUsed / 15 / 100; // 15s = 1% max volume
int maxVolume = player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
int currentVolume = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
if (currentVolume < maxVolume) {
int volumeRefilled = (int)(percentageRefilled * maxVolume);
int newVolume = currentVolume + volumeRefilled;
if (currentVolume + volumeRefilled > maxVolume) {
newVolume = maxVolume;
}
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, newVolume);
}
player.setSpringLastUsed(now);
player.save();
}
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public void autoRecover(GameSession session) {
// TODO: In MP, respect SotS settings from the HOST.
boolean isAutoRecoveryEnabled = getIsAutoRecoveryEnabled();
int autoRecoverPercentage = getAutoRecoveryPercentage();
Grasscutter.getLogger().debug("isAutoRecoveryEnabled: " + isAutoRecoveryEnabled + "\tautoRecoverPercentage: " + autoRecoverPercentage);
if (isAutoRecoveryEnabled) {
player.getTeamManager().getActiveTeam().forEach(entity -> {
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
if (currentHP == maxHP) {
return;
}
float targetHP = maxHP * autoRecoverPercentage / 100;
if (targetHP > currentHP) {
float needHP = targetHP - currentHP;
float needSV = needHP * 100; // convert HP needed to Spring Volume needed
int sotsSVBalance = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
if (sotsSVBalance >= needSV) {
// sufficient
sotsSVBalance -= needSV;
} else {
// insufficient balance
needSV = sotsSVBalance;
sotsSVBalance = 0;
}
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, sotsSVBalance);
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
float newHP = currentHP + needSV / 100; // convert SV to HP
// TODO: Figure out why client shows current HP instead of added HP.
// Say an avatar had 12000 and now has 14000, it should show "2000".
// The client always show "+14000" which is incorrect.
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
session.send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
newHP, List.of(3), PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_STATUE_RECOVER,
ChangeHpReasonOuterClass.ChangeHpReason.ChangeHpAddStatue));
session.send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
Avatar avatar = entity.getAvatar();
avatar.setCurrentHp(newHP);
session.send(new PacketAvatarFightPropUpdateNotify(avatar, FightProperty.FIGHT_PROP_CUR_HP));
player.save();
}
});
}
}
}

View File

@ -21,7 +21,8 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.Inventory;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.mail.MailHandler; import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.MotionManager.MotionManager; import emu.grasscutter.game.managers.MovementManager.MovementManager;
import emu.grasscutter.game.managers.SotSManager.SotSManager;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
@ -90,6 +91,8 @@ public class Player {
@Transient private MailHandler mailHandler; @Transient private MailHandler mailHandler;
@Transient private MessageHandler messageHandler; @Transient private MessageHandler messageHandler;
@Transient private SotSManager sotsManager;
private TeamManager teamManager; private TeamManager teamManager;
private TowerManager towerManager; private TowerManager towerManager;
@ -126,7 +129,9 @@ public class Player {
@Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler; @Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
private MapMarksManager mapMarksManager; private MapMarksManager mapMarksManager;
@Transient private MotionManager motionManager; @Transient private MovementManager movementManager;
private long springLastUsed;
@Deprecated @Deprecated
@ -168,7 +173,8 @@ public class Player {
this.shopLimit = new ArrayList<>(); this.shopLimit = new ArrayList<>();
this.messageHandler = null; this.messageHandler = null;
this.mapMarksManager = new MapMarksManager(); this.mapMarksManager = new MapMarksManager();
this.motionManager = new MotionManager(this); this.movementManager = new MovementManager(this);
this.sotsManager = new SotSManager(this);
} }
// On player creation // On player creation
@ -196,7 +202,8 @@ public class Player {
this.getRotation().set(0, 307, 0); this.getRotation().set(0, 307, 0);
this.messageHandler = null; this.messageHandler = null;
this.mapMarksManager = new MapMarksManager(); this.mapMarksManager = new MapMarksManager();
this.motionManager = new MotionManager(this); this.movementManager = new MovementManager(this);
this.sotsManager = new SotSManager(this);
} }
public int getUid() { public int getUid() {
@ -531,6 +538,14 @@ public class Player {
} }
} }
public long getSpringLastUsed() {
return springLastUsed;
}
public void setSpringLastUsed(long val) {
springLastUsed = val;
}
public SceneLoadState getSceneLoadState() { public SceneLoadState getSceneLoadState() {
return sceneState; return sceneState;
} }
@ -983,7 +998,9 @@ public class Player {
return mapMarksManager; return mapMarksManager;
} }
public MotionManager getMotionManager() { return motionManager; } public MovementManager getMovementManager() { return movementManager; }
public SotSManager getSotSManager() { return sotsManager; }
public synchronized void onTick() { public synchronized void onTick() {
// Check ping // Check ping
@ -1013,33 +1030,9 @@ public class Player {
this.resetSendPlayerLocTime(); this.resetSendPlayerLocTime();
} }
} }
scheduleStaminaNotify();
} }
private void scheduleStaminaNotify() {
// stamina tick
EntityMoveInfoOuterClass.EntityMoveInfo moveInfo = getMotionManager().getMoveInfo();
if (moveInfo == null) {
return;
}
if (getMotionManager().getMoveInfo().getMotionInfo().getState() == MotionStateOuterClass.MotionState.MOTION_STANDBY) {
if (getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) == getProperty(PlayerProperty.PROP_MAX_STAMINA) ) {
return;
}
}
for (int i = 0; i <= 1000; i+=200) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
getMotionManager().tick();
}
}, i);
}
}
public void resetSendPlayerLocTime() { public void resetSendPlayerLocTime() {

View File

@ -18,6 +18,7 @@ import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp; import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
@ -467,27 +468,37 @@ public class TeamManager {
if (deadAvatar.isAlive() || deadAvatar.getId() != dieGuid) { if (deadAvatar.isAlive() || deadAvatar.getId() != dieGuid) {
return; return;
} }
// Replacement avatar PlayerDieType dieType = deadAvatar.getKilledType();
EntityAvatar replacement = null; int killedBy = deadAvatar.getKilledBy();
int replaceIndex = -1;
if (dieType == PlayerDieType.PLAYER_DIE_DRAWN) {
for (int i = 0; i < this.getActiveTeam().size(); i++) { // Died in water. Do not replace
EntityAvatar entity = this.getActiveTeam().get(i); // The official server has skipped this notify and will just respawn the team immediately after the animation.
if (entity.isAlive()) { // TODO: Perhaps find a way to get vanilla experience?
replaceIndex = i; getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy));
replacement = entity;
break;
}
}
if (replacement == null) {
// No more living team members...
getPlayer().sendPacket(new PacketWorldPlayerDieNotify(deadAvatar.getKilledType(), deadAvatar.getKilledBy()));
} else { } else {
// Set index and spawn replacement member // Replacement avatar
this.setCurrentCharacterIndex(replaceIndex); EntityAvatar replacement = null;
getPlayer().getScene().addEntity(replacement); int replaceIndex = -1;
for (int i = 0; i < this.getActiveTeam().size(); i++) {
EntityAvatar entity = this.getActiveTeam().get(i);
if (entity.isAlive()) {
replaceIndex = i;
replacement = entity;
break;
}
}
if (replacement == null) {
// No more living team members...
getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy));
} else {
// Set index and spawn replacement member
this.setCurrentCharacterIndex(replaceIndex);
getPlayer().getScene().addEntity(replacement);
}
} }
// Response packet // Response packet
@ -540,11 +551,13 @@ public class TeamManager {
public void respawnTeam() { public void respawnTeam() {
// Make sure all team members are dead // Make sure all team members are dead
for (EntityAvatar entity : getActiveTeam()) { // Drowning needs revive when there may be other team members still alive.
if (entity.isAlive()) { // for (EntityAvatar entity : getActiveTeam()) {
return; // if (entity.isAlive()) {
} // return;
} // }
// }
player.getMovementManager().resetTimer(); // prevent drowning immediately after respawn
// Revive all team members // Revive all team members
for (EntityAvatar entity : getActiveTeam()) { for (EntityAvatar entity : getActiveTeam()) {

View File

@ -8,18 +8,21 @@ import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.dungeons.TowerDungeonSettleListener; import emu.grasscutter.game.dungeons.TowerDungeonSettleListener;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify; import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify;
import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp; import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp;
import java.util.List; import java.util.List;
@Entity @Entity
public class TowerManager { public class TowerManager {
@Transient
private Player player; @Transient private final Player player;
public TowerManager(Player player) { public TowerManager(Player player) {
this.player = player; this.player = player;
} }
public void setPlayer(Player player) { public void setPlayer(Player player) {
this.player = player; this.player = player;
} }
@ -52,6 +55,7 @@ public class TowerManager {
entryScene = player.getSceneId(); entryScene = player.getSceneId();
} }
player.getTeamManager().setupTemporaryTeam(towerTeams); player.getTeamManager().setupTemporaryTeam(towerTeams);
} }

View File

@ -1,5 +1,8 @@
package emu.grasscutter.net.packet; package emu.grasscutter.net.packet;
import java.util.Arrays;
import java.util.List;
public class PacketOpcodes { public class PacketOpcodes {
// Empty // Empty
public static final int NONE = 0; public static final int NONE = 0;
@ -1566,4 +1569,6 @@ public class PacketOpcodes {
public static final int UNKNOWN_43 = 8877; public static final int UNKNOWN_43 = 8877;
public static final int UNKNOWN_44 = 8983; public static final int UNKNOWN_44 = 8983;
public static final int UNKNOWN_45 = 943; public static final int UNKNOWN_45 = 943;
public static final List<Integer> BANNED_PACKETS = Arrays.asList(PacketOpcodes.WindSeedClientNotify, PacketOpcodes.PlayerLuaShellNotify);
} }

View File

@ -157,6 +157,12 @@ public class GameSession extends KcpChannel {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!"); Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return; return;
} }
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if(PacketOpcodes.BANNED_PACKETS.contains(packet.getOpcode())) {
return;
}
// Header // Header
if (packet.shouldBuildHeader()) { if (packet.shouldBuildHeader()) {

View File

@ -1,9 +1,6 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.managers.MotionManager.MotionManager;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify; import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
@ -11,9 +8,7 @@ import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo; import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo; import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*;
@Opcodes(PacketOpcodes.CombatInvocationsNotify) @Opcodes(PacketOpcodes.CombatInvocationsNotify)
public class HandlerCombatInvocationsNotify extends PacketHandler { public class HandlerCombatInvocationsNotify extends PacketHandler {
@ -33,13 +28,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData()); EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId()); GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
if (entity != null) { if (entity != null) {
//move session.getPlayer().getMovementManager().handle(session, moveInfo, entity);
entity.getPosition().set(moveInfo.getMotionInfo().getPos());
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
entity.setMotionState(moveInfo.getMotionInfo().getState());
session.getPlayer().getMotionManager().handle(session, entity, moveInfo);
} }
break; break;
default: default:
@ -52,7 +41,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
if (notif.getInvokeListList().size() > 0) { if (notif.getInvokeListList().size() > 0) {
session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); session.getPlayer().getCombatInvokeHandler().update(session.getPlayer());
} }
// Handle attack results last // Handle attack results last
while (!session.getPlayer().getAttackResults().isEmpty()) { while (!session.getPlayer().getAttackResults().isEmpty()) {
session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll());
} }

View File

@ -1,5 +1,7 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.managers.SotSManager.SotSManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
@ -18,26 +20,12 @@ import java.util.List;
public class HandlerEnterTransPointRegionNotify extends PacketHandler { public class HandlerEnterTransPointRegionNotify extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{ public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{
session.getPlayer().getTeamManager().getActiveTeam().forEach(entity -> { Player player = session.getPlayer();
boolean isAlive = entity.isAlive(); SotSManager sotsManager = player.getSotSManager();
if(entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) != entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP)){
Float hp = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP)-entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
session.send(new PacketEntityFightPropUpdateNotify(entity,FightProperty.FIGHT_PROP_MAX_HP));
session.send(new PacketEntityFightPropChangeReasonNotify( sotsManager.refillSpringVolume();
entity, FightProperty.FIGHT_PROP_CUR_HP, hp, List.of(3), sotsManager.autoRevive(session);
PropChangeReason.PROP_CHANGE_STATUE_RECOVER, ChangeHpReason.ChangeHpAddStatue)); sotsManager.scheduleAutoRecover(session);
// TODO: allow interaction with the SotS?
entity.setFightProperty(
FightProperty.FIGHT_PROP_CUR_HP,
entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP)
);
session.send(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
if (!isAlive) {
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
}
}
});
} }
} }

View File

@ -3,7 +3,9 @@ package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.WorldPlayerDieNotifyOuterClass;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketWorldPlayerReviveRsp;
@Opcodes(PacketOpcodes.WorldPlayerReviveReq) @Opcodes(PacketOpcodes.WorldPlayerReviveReq)
public class HandlerWorldPlayerReviveReq extends PacketHandler { public class HandlerWorldPlayerReviveReq extends PacketHandler {
@ -11,6 +13,7 @@ public class HandlerWorldPlayerReviveReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.getPlayer().getTeamManager().respawnTeam(); session.getPlayer().getTeamManager().respawnTeam();
session.send(new PacketWorldPlayerReviveRsp());
} }
} }

View File

@ -9,6 +9,10 @@ import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarLifeStateChangeNotifyOuterClass.AvatarLifeStateChangeNotify; import emu.grasscutter.net.proto.AvatarLifeStateChangeNotifyOuterClass.AvatarLifeStateChangeNotify;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.ServerBuffOuterClass;
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
import java.util.ArrayList;
public class PacketAvatarLifeStateChangeNotify extends BasePacket { public class PacketAvatarLifeStateChangeNotify extends BasePacket {
@ -22,7 +26,7 @@ public class PacketAvatarLifeStateChangeNotify extends BasePacket {
this.setData(proto); this.setData(proto);
} }
public PacketAvatarLifeStateChangeNotify(Avatar avatar,int attackerId,LifeState lifeState) { public PacketAvatarLifeStateChangeNotify(Avatar avatar, int attackerId, LifeState lifeState) {
super(PacketOpcodes.AvatarLifeStateChangeNotify); super(PacketOpcodes.AvatarLifeStateChangeNotify);
AvatarLifeStateChangeNotify proto = AvatarLifeStateChangeNotify.newBuilder() AvatarLifeStateChangeNotify proto = AvatarLifeStateChangeNotify.newBuilder()
@ -33,4 +37,26 @@ public class PacketAvatarLifeStateChangeNotify extends BasePacket {
this.setData(proto); this.setData(proto);
} }
public PacketAvatarLifeStateChangeNotify(Avatar avatar, LifeState lifeState, PlayerDieType dieType) {
this(avatar, lifeState, null, "", dieType);
}
public PacketAvatarLifeStateChangeNotify(Avatar avatar, LifeState lifeState, GameEntity sourceEntity,
String attackTag, PlayerDieType dieType) {
super(PacketOpcodes.AvatarLifeStateChangeNotify);
AvatarLifeStateChangeNotify.Builder proto = AvatarLifeStateChangeNotify.newBuilder();
proto.setAvatarGuid(avatar.getGuid());
proto.setLifeState(lifeState.getValue());
if (sourceEntity != null) {
proto.setSourceEntityId(sourceEntity.getId());
}
proto.setDieType(dieType);
proto.setAttackTag((attackTag));
this.setData(proto.build());
}
} }

View File

@ -1,10 +1,15 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify; import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
import java.util.ArrayList;
public class PacketLifeStateChangeNotify extends BasePacket { public class PacketLifeStateChangeNotify extends BasePacket {
public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) { public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) {
@ -26,7 +31,29 @@ public class PacketLifeStateChangeNotify extends BasePacket {
.setLifeState(lifeState.getValue()) .setLifeState(lifeState.getValue())
.setSourceEntityId(attackerId) .setSourceEntityId(attackerId)
.build(); .build();
this.setData(proto); this.setData(proto);
} }
public PacketLifeStateChangeNotify(GameEntity entity, LifeState lifeState, PlayerDieType dieType) {
this(entity, lifeState, null, "", dieType);
}
public PacketLifeStateChangeNotify(GameEntity entity, LifeState lifeState, GameEntity sourceEntity,
String attackTag, PlayerDieType dieType) {
super(PacketOpcodes.LifeStateChangeNotify);
LifeStateChangeNotify.Builder proto = LifeStateChangeNotify.newBuilder();
proto.setEntityId(entity.getId());
proto.setLifeState(lifeState.getValue());
if (sourceEntity != null) {
proto.setSourceEntityId(sourceEntity.getId());
}
proto.setAttackTag(attackTag);
proto.setDieType(dieType);
this.setData(proto.build());
}
} }

View File

@ -15,14 +15,7 @@ public class PacketTakeAchievementRewardReq extends BasePacket {
public PacketTakeAchievementRewardReq(GameSession session) { public PacketTakeAchievementRewardReq(GameSession session) {
super(PacketOpcodes.TakeAchievementRewardReq); super(PacketOpcodes.TakeAchievementRewardReq);
List<AchievementInfo> a_list = new ArrayList<>(); TakeAchievementRewardReq proto = TakeAchievementRewardReq.newBuilder().build();
a_list.add(AchievementInfo.newBuilder().setId(82044).setStatusValue(2).setCurrent(0).setGoal(1).build());
a_list.add(AchievementInfo.newBuilder().setId(81205).setStatusValue(2).setCurrent(0).setGoal(1).build());
TakeAchievementRewardReq proto = TakeAchievementRewardReq.newBuilder()
.addAllAList(a_list)
.build();
this.setData(proto); this.setData(proto);
} }

View File

@ -0,0 +1,18 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.WorldPlayerReviveRspOuterClass.WorldPlayerReviveRsp;
public class PacketWorldPlayerReviveRsp extends BasePacket {
public PacketWorldPlayerReviveRsp() {
super(PacketOpcodes.WorldPlayerReviveRsp);
WorldPlayerReviveRsp.Builder proto = WorldPlayerReviveRsp.newBuilder();
this.setData(proto.build());
}
}