Merge branch 'development' into more-events

# Conflicts:
#	src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java
#	src/main/java/emu/grasscutter/game/entity/EntityAvatar.java
#	src/main/java/emu/grasscutter/game/entity/GameEntity.java
#	src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java
This commit is contained in:
KingRainbow44
2022-07-22 17:52:58 -04:00
257 changed files with 17788 additions and 16667 deletions

View File

@@ -0,0 +1,13 @@
package emu.grasscutter.server.game;
public abstract class BaseGameSystem {
protected final GameServer server;
public BaseGameSystem(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return this.server;
}
}

View File

@@ -5,27 +5,27 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.battlepass.BattlePassMissionManager;
import emu.grasscutter.game.battlepass.BattlePassSystem;
import emu.grasscutter.game.combine.CombineManger;
import emu.grasscutter.game.drop.DropManager;
import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.drop.DropSystem;
import emu.grasscutter.game.dungeons.DungeonSystem;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.expedition.ExpeditionManager;
import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.managers.AnnouncementManager;
import emu.grasscutter.game.expedition.ExpeditionSystem;
import emu.grasscutter.game.gacha.GachaSystem;
import emu.grasscutter.game.managers.CookingManager;
import emu.grasscutter.game.managers.InventoryManager;
import emu.grasscutter.game.managers.MultiplayerManager;
import emu.grasscutter.game.managers.chat.ChatManager;
import emu.grasscutter.game.managers.chat.ChatManagerHandler;
import emu.grasscutter.game.managers.energy.EnergyManager;
import emu.grasscutter.game.managers.stamina.StaminaManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.ServerQuestHandler;
import emu.grasscutter.game.shop.ShopManager;
import emu.grasscutter.game.tower.TowerScheduleManager;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.shop.ShopSystem;
import emu.grasscutter.game.systems.AnnouncementSystem;
import emu.grasscutter.game.systems.InventorySystem;
import emu.grasscutter.game.systems.MultiplayerSystem;
import emu.grasscutter.game.tower.TowerSystem;
import emu.grasscutter.game.world.World;
import emu.grasscutter.game.world.WorldDataManager;
import emu.grasscutter.game.world.WorldDataSystem;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.server.event.types.ServerEvent;
@@ -44,172 +44,177 @@ import java.time.OffsetDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
import static emu.grasscutter.Configuration.*;
@Getter
public final class GameServer extends KcpServer {
private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler;
private final ServerQuestHandler questHandler;
@Getter private final ServerTaskScheduler scheduler;
// Game server base
private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler;
private final Map<Integer, Player> players;
private final Set<World> worlds;
private final Map<Integer, Player> players;
private final Set<World> worlds;
// Server systems
private final InventorySystem inventorySystem;
private final GachaSystem gachaSystem;
private final ShopSystem shopSystem;
private final MultiplayerSystem multiplayerSystem;
private final DungeonSystem dungeonSystem;
private final ExpeditionSystem expeditionSystem;
private final DropSystem dropSystem;
private final WorldDataSystem worldDataSystem;
private final BattlePassSystem battlePassSystem;
private final CombineManger combineSystem;
private final TowerSystem towerSystem;
private final AnnouncementSystem announcementSystem;
private final QuestSystem questSystem;
private ChatManagerHandler chatManager;
@Getter private final InventoryManager inventoryManager;
@Getter private final GachaManager gachaManager;
@Getter private final ShopManager shopManager;
@Getter private final MultiplayerManager multiplayerManager;
@Getter private final DungeonManager dungeonManager;
@Getter private final ExpeditionManager expeditionManager;
@Getter private final CommandMap commandMap;
@Getter private final TaskMap taskMap;
@Getter private final DropManager dropManager;
@Getter private final WorldDataManager worldDataManager;
@Getter private final BattlePassMissionManager battlePassMissionManager;
@Getter private final CombineManger combineManger;
@Getter private final TowerScheduleManager towerScheduleManager;
@Getter private final AnnouncementManager announcementManager;
// Extra
private final ServerTaskScheduler scheduler;
private final CommandMap commandMap;
private final TaskMap taskMap;
public GameServer() {
this(getAdapterInetSocketAddress());
}
private ChatManagerHandler chatManager;
public GameServer(InetSocketAddress address) {
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true,40,2,true);
channelConfig.setMtu(1400);
channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30*1000);//30s
channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(false);
public GameServer() {
this(getAdapterInetSocketAddress());
}
this.init(GameSessionManager.getListener(),channelConfig,address);
public GameServer(InetSocketAddress address) {
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, GAME_INFO.kcpInterval, 2, true);
channelConfig.setMtu(1400);
channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30 * 1000);//30s
channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(false);
DungeonChallenge.initialize();
EnergyManager.initialize();
StaminaManager.initialize();
CookingManager.initialize();
CombineManger.initialize();
this.init(GameSessionManager.getListener(),channelConfig,address);
this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.questHandler = new ServerQuestHandler();
DungeonChallenge.initialize();
EnergyManager.initialize();
StaminaManager.initialize();
CookingManager.initialize();
CombineManger.initialize();
// Game Server base
this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>());
// Extra
this.scheduler = new ServerTaskScheduler();
this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>());
this.commandMap = new CommandMap(true);
this.taskMap = new TaskMap(true);
this.chatManager = new ChatManager(this);
this.inventoryManager = new InventoryManager(this);
this.gachaManager = new GachaManager(this);
this.shopManager = new ShopManager(this);
this.multiplayerManager = new MultiplayerManager(this);
this.dungeonManager = new DungeonManager(this);
this.commandMap = new CommandMap(true);
this.taskMap = new TaskMap(true);
this.dropManager = new DropManager(this);
this.expeditionManager = new ExpeditionManager(this);
this.combineManger = new CombineManger(this);
this.towerScheduleManager = new TowerScheduleManager(this);
this.worldDataManager = new WorldDataManager(this);
this.battlePassMissionManager = new BattlePassMissionManager(this);
this.announcementManager = new AnnouncementManager(this);
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
}
// Create game systems
this.inventorySystem = new InventorySystem(this);
this.gachaSystem = new GachaSystem(this);
this.shopSystem = new ShopSystem(this);
this.multiplayerSystem = new MultiplayerSystem(this);
this.dungeonSystem = new DungeonSystem(this);
this.dropSystem = new DropSystem(this);
this.expeditionSystem = new ExpeditionSystem(this);
this.combineSystem = new CombineManger(this);
this.towerSystem = new TowerSystem(this);
this.worldDataSystem = new WorldDataSystem(this);
this.battlePassSystem = new BattlePassSystem(this);
this.announcementSystem = new AnnouncementSystem(this);
this.questSystem = new QuestSystem(this);
public GameServerPacketHandler getPacketHandler() {
return packetHandler;
}
// Chata manager
this.chatManager = new ChatManager(this);
public ServerQuestHandler getQuestHandler() {
return questHandler;
}
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
}
public Map<Integer, Player> getPlayers() {
return players;
}
@Deprecated
public ChatManagerHandler getChatManager() {
return chatManager;
}
public Set<World> getWorlds() {
return worlds;
}
@Deprecated
public void setChatManager(ChatManagerHandler chatManager) {
this.chatManager = chatManager;
}
public ChatManagerHandler getChatManager() {
return chatManager;
}
public ChatManagerHandler getChatSystem() {
return chatManager;
}
public void setChatManager(ChatManagerHandler chatManager) {
this.chatManager = chatManager;
}
public void setChatSystem(ChatManagerHandler chatManager) {
this.chatManager = chatManager;
}
private static InetSocketAddress getAdapterInetSocketAddress() {
InetSocketAddress inetSocketAddress;
if (GAME_INFO.bindAddress.equals("")) {
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
}else {
inetSocketAddress=new InetSocketAddress(
GAME_INFO.bindAddress,
GAME_INFO.bindPort
);
}
return inetSocketAddress;
}
private static InetSocketAddress getAdapterInetSocketAddress(){
InetSocketAddress inetSocketAddress;
if(GAME_INFO.bindAddress.equals("")){
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
}else{
inetSocketAddress=new InetSocketAddress(
GAME_INFO.bindAddress,
GAME_INFO.bindPort
);
}
return inetSocketAddress;
}
public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player);
}
public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player);
}
public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false);
}
public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false);
}
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check
if (id == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check
if (id == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
// Get from online players
Player player = this.getPlayers().get(id);
// Get from online players
Player player = this.getPlayers().get(id);
if (!allowOfflinePlayers) {
return player;
}
if (!allowOfflinePlayers) {
return player;
}
// Check database if character isnt here
if (player == null) {
player = DatabaseHelper.getPlayerByUid(id);
}
// Check database if character isnt here
if (player == null) {
player = DatabaseHelper.getPlayerByUid(id);
}
return player;
}
return player;
}
public Player getPlayerByAccountId(String accountId) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
return playerOpt.orElse(null);
}
public Player getPlayerByAccountId(String accountId) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
return playerOpt.orElse(null);
}
public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players
Player player = this.getPlayerByUid(id, true);
public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players
Player player = this.getPlayerByUid(id, true);
if (player == null) {
return null;
}
if (player == null) {
return null;
}
return player.getSocialDetail();
}
return player.getSocialDetail();
}
public Account getAccountByName(String username) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
if (playerOpt.isPresent()) {
return playerOpt.get().getAccount();
}
return DatabaseHelper.getAccountByName(username);
}
public Account getAccountByName(String username) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
if (playerOpt.isPresent()) {
return playerOpt.get().getAccount();
}
return DatabaseHelper.getAccountByName(username);
}
public synchronized void onTick() {
var tickStart = Instant.now();
@@ -239,43 +244,43 @@ public final class GameServer extends KcpServer {
event.call();
}
public void registerWorld(World world) {
this.getWorlds().add(world);
}
public void registerWorld(World world) {
this.getWorlds().add(world);
}
public void deregisterWorld(World world) {
// TODO Auto-generated method stub
public void deregisterWorld(World world) {
// TODO Auto-generated method stub
}
}
public void start() {
// Schedule game loop.
Timer gameLoop = new Timer();
gameLoop.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
onTick();
} catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
}
}
}, new Date(), 1000L);
Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call();
}
public void start() {
// Schedule game loop.
Timer gameLoop = new Timer();
gameLoop.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
onTick();
} catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
}
}
}, new Date(), 1000L);
Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call();
}
public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
// Kick and save all players
List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values());
// Kick and save all players
List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values());
for (Player player : list) {
player.getSession().close();
}
}
for (Player player : list) {
player.getSession().close();
}
}
}

View File

@@ -1,5 +1,7 @@
package emu.grasscutter.server.game;
import static emu.grasscutter.config.Configuration.*;
import java.util.Set;
import emu.grasscutter.server.event.game.ReceivePacketEvent;
@@ -14,88 +16,86 @@ import emu.grasscutter.server.game.GameSession.SessionState;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static emu.grasscutter.Configuration.*;
@SuppressWarnings("unchecked")
public class GameServerPacketHandler {
private final Int2ObjectMap<PacketHandler> handlers;
private final Int2ObjectMap<PacketHandler> handlers;
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
this.handlers = new Int2ObjectOpenHashMap<>();
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
this.handlers = new Int2ObjectOpenHashMap<>();
this.registerHandlers(handlerClass);
}
this.registerHandlers(handlerClass);
}
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
}
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
}
// Debug
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
}
// Debug
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
}
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
PacketHandler handler = this.handlers.get(opcode);
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
PacketHandler handler = this.handlers.get(opcode);
if (handler != null) {
try {
// Make sure session is ready for packets
SessionState state = session.getState();
if (handler != null) {
try {
// Make sure session is ready for packets
SessionState state = session.getState();
if (opcode == PacketOpcodes.PingReq) {
// Always continue if packet is ping request
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
if (state != SessionState.WAITING_FOR_TOKEN) {
return;
}
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
if (state != SessionState.WAITING_FOR_LOGIN) {
return;
}
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
if (state != SessionState.PICKING_CHARACTER) {
return;
}
} else {
if (state != SessionState.ACTIVE) {
return;
}
}
if (opcode == PacketOpcodes.PingReq) {
// Always continue if packet is ping request
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
if (state != SessionState.WAITING_FOR_TOKEN) {
return;
}
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
if (state != SessionState.WAITING_FOR_LOGIN) {
return;
}
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
if (state != SessionState.PICKING_CHARACTER) {
return;
}
} else {
if (state != SessionState.ACTIVE) {
return;
}
}
// Invoke event.
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
if(!event.isCanceled()) // If event is not canceled, continue.
handler.handle(session, header, event.getPacketData());
} catch (Exception ex) {
// TODO Remove this when no more needed
ex.printStackTrace();
}
return; // Packet successfully handled
}
// Invoke event.
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
if (!event.isCanceled()) // If event is not canceled, continue.
handler.handle(session, header, event.getPacketData());
} catch (Exception ex) {
// TODO Remove this when no more needed
ex.printStackTrace();
}
return; // Packet successfully handled
}
// Log unhandled packets
if (SERVER.debugLevel == ServerDebugMode.MISSING) {
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtil.getOpcodeName(opcode));
}
}
// Log unhandled packets
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) {
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
}
}
}

View File

@@ -9,7 +9,7 @@ import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketOpcodesUtil;
import emu.grasscutter.net.packet.PacketOpcodesUtils;
import emu.grasscutter.server.event.game.SendPacketEvent;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils;
@@ -17,272 +17,275 @@ import emu.grasscutter.utils.Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
import static emu.grasscutter.Configuration.*;
public class GameSession implements GameSessionManager.KcpChannel {
private final GameServer server;
private GameSessionManager.KcpTunnel tunnel;
private final GameServer server;
private GameSessionManager.KcpTunnel tunnel;
private Account account;
private Player player;
private Account account;
private Player player;
private boolean useSecretKey;
private SessionState state;
private boolean useSecretKey;
private SessionState state;
private int clientTime;
private long lastPingTime;
private int lastClientSeq = 10;
private int clientTime;
private long lastPingTime;
private int lastClientSeq = 10;
public GameSession(GameServer server) {
this.server = server;
this.state = SessionState.WAITING_FOR_TOKEN;
this.lastPingTime = System.currentTimeMillis();
}
public GameSession(GameServer server) {
this.server = server;
this.state = SessionState.WAITING_FOR_TOKEN;
this.lastPingTime = System.currentTimeMillis();
}
public GameServer getServer() {
return server;
}
public GameServer getServer() {
return server;
}
public InetSocketAddress getAddress() {
try{
return tunnel.getAddress();
}catch (Throwable ignore){
return null;
}
}
public InetSocketAddress getAddress() {
try {
return tunnel.getAddress();
}catch (Throwable ignore) {
return null;
}
}
public boolean useSecretKey() {
return useSecretKey;
}
public boolean useSecretKey() {
return useSecretKey;
}
public Account getAccount() {
return account;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountId() {
return this.getAccount().getId();
}
public String getAccountId() {
return this.getAccount().getId();
}
public Player getPlayer() {
return player;
}
public Player getPlayer() {
return player;
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.player.setSession(this);
this.player.setAccount(this.getAccount());
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.player.setSession(this);
this.player.setAccount(this.getAccount());
}
public SessionState getState() {
return state;
}
public SessionState getState() {
return state;
}
public void setState(SessionState state) {
this.state = state;
}
public void setState(SessionState state) {
this.state = state;
}
public boolean isLoggedIn() {
return this.getPlayer() != null;
}
public boolean isLoggedIn() {
return this.getPlayer() != null;
}
public void setUseSecretKey(boolean useSecretKey) {
this.useSecretKey = useSecretKey;
}
public void setUseSecretKey(boolean useSecretKey) {
this.useSecretKey = useSecretKey;
}
public int getClientTime() {
return this.clientTime;
}
public int getClientTime() {
return this.clientTime;
}
public long getLastPingTime() {
return lastPingTime;
}
public long getLastPingTime() {
return lastPingTime;
}
public void updateLastPingTime(int clientTime) {
this.clientTime = clientTime;
this.lastPingTime = System.currentTimeMillis();
}
public void updateLastPingTime(int clientTime) {
this.clientTime = clientTime;
this.lastPingTime = System.currentTimeMillis();
}
public int getNextClientSequence() {
return ++lastClientSeq;
}
public int getNextClientSequence() {
return ++lastClientSeq;
}
public void replayPacket(int opcode, String name) {
String filePath = PACKET(name);
File p = new File(filePath);
String filePath = PACKET(name);
File p = new File(filePath);
if (!p.exists()) return;
if (!p.exists()) return;
byte[] packet = FileUtils.read(p);
byte[] packet = FileUtils.read(p);
BasePacket basePacket = new BasePacket(opcode);
basePacket.setData(packet);
BasePacket basePacket = new BasePacket(opcode);
basePacket.setData(packet);
send(basePacket);
send(basePacket);
}
public void logPacket( String sendOrRecv, int opcode, byte[] payload) {
Grasscutter.getLogger().info(sendOrRecv + ": " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")");
Grasscutter.getLogger().info(sendOrRecv + ": " + PacketOpcodesUtils.getOpcodeName(opcode) + " (" + opcode + ")");
System.out.println(Utils.bytesToHex(payload));
}
public void send(BasePacket packet) {
// Test
if (packet.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return;
}
// Test
if (packet.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
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;
}
// 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 (PacketOpcodesUtils.BANNED_PACKETS.contains(packet.getOpcode())) {
return;
}
// Header
if (packet.shouldBuildHeader()) {
packet.buildHeader(this.getNextClientSequence());
}
// Header
if (packet.shouldBuildHeader()) {
packet.buildHeader(this.getNextClientSequence());
}
// Log
if (SERVER.debugLevel == ServerDebugMode.ALL) {
if (!loopPacket.contains(packet.getOpcode())) {
logPacket("SEND",packet.getOpcode(), packet.getData());
}
}
// Log
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case BLACKLIST-> {
if (!SERVER.debugBlacklist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
default -> {}
}
if (SERVER.debugLevel == ServerDebugMode.WHITELIST && SERVER.DebugWhitelist.contains(packet.getOpcode())) {
logPacket("SEND",packet.getOpcode(), packet.getData());
}
if (SERVER.debugLevel == ServerDebugMode.BLACKLIST && !(SERVER.DebugBlacklist.contains(packet.getOpcode()))) {
logPacket("SEND",packet.getOpcode(), packet.getData());
}
// Invoke event.
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
if(!event.isCanceled()) { // If event is not cancelled, continue.
tunnel.writeData(event.getPacket().build());
}
// Invoke event.
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
if (!event.isCanceled()) { // If event is not cancelled, continue.
tunnel.writeData(event.getPacket().build());
}
}
private static final Set<Integer> loopPacket = Set.of(
PacketOpcodes.PingReq,
PacketOpcodes.PingRsp,
PacketOpcodes.WorldPlayerRTTNotify,
PacketOpcodes.UnionCmdNotify,
PacketOpcodes.QueryPathReq
);
@Override
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
this.tunnel = tunnel;
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
}
@Override
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
this.tunnel = tunnel;
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
}
@Override
public void handleReceive(byte[] bytes) {
// Decrypt and turn back into a packet
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
@Override
public void handleReceive(byte[] bytes) {
// Decrypt and turn back into a packet
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
// Log
//logPacket(packet);
// Handle
try {
boolean allDebug = SERVER.debugLevel == ServerDebugMode.ALL;
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 12) {
return;
}
// Packet sanity check
int const1 = packet.readShort();
if (const1 != 17767) {
if(allDebug){
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
}
return; // Bad packet
}
// Data
int opcode = packet.readShort();
int headerLength = packet.readShort();
int payloadLength = packet.readInt();
byte[] header = new byte[headerLength];
byte[] payload = new byte[payloadLength];
// Log
//logPacket(packet);
// Handle
try {
boolean allDebug = GAME_INFO.logPackets == ServerDebugMode.ALL;
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 12) {
return;
}
// Packet sanity check
int const1 = packet.readShort();
if (const1 != 17767) {
if (allDebug) {
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
}
return; // Bad packet
}
// Data
int opcode = packet.readShort();
int headerLength = packet.readShort();
int payloadLength = packet.readInt();
byte[] header = new byte[headerLength];
byte[] payload = new byte[payloadLength];
packet.readBytes(header);
packet.readBytes(payload);
// Sanity check #2
int const2 = packet.readShort();
if (const2 != -30293) {
if(allDebug){
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
}
return; // Bad packet
}
// Log packet
if (allDebug) {
if (!loopPacket.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
packet.readBytes(header);
packet.readBytes(payload);
// Sanity check #2
int const2 = packet.readShort();
if (const2 != -30293) {
if (allDebug) {
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
}
return; // Bad packet
}
if (SERVER.debugLevel == ServerDebugMode.WHITELIST && SERVER.DebugWhitelist.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
// Log packet
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case BLACKLIST-> {
if (!(SERVER.debugBlacklist.contains(opcode))) {
logPacket("RECV",opcode, payload);
}
}
default -> {}
}
if (SERVER.debugLevel == ServerDebugMode.BLACKLIST && !(SERVER.DebugBlacklist.contains(opcode))) {
logPacket("RECV",opcode, payload);
}
// Handle
getServer().getPacketHandler().handle(this, opcode, header, payload);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//byteBuf.release(); //Needn't
packet.release();
}
}
// Handle
getServer().getPacketHandler().handle(this, opcode, header, payload);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//byteBuf.release(); //Needn't
packet.release();
}
}
@Override
public void handleClose() {
setState(SessionState.INACTIVE);
//send disconnection pack in case of reconnection
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
// Save after disconnecting
if (this.isLoggedIn()) {
Player player = getPlayer();
// Call logout event.
player.onLogout();
}
try {
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
}catch (Throwable ignore) {
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
}
tunnel = null;
}
@Override
public void handleClose() {
setState(SessionState.INACTIVE);
//send disconnection pack in case of reconnection
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
// Save after disconnecting
if (this.isLoggedIn()) {
Player player = getPlayer();
// Call logout event.
player.onLogout();
}
try {
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
}catch (Throwable ignore){
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
}
tunnel = null;
}
public void close() {
tunnel.close();
}
public void close() {
tunnel.close();
}
public boolean isActive() {
return getState() == SessionState.ACTIVE;
}
public boolean isActive() {
return getState() == SessionState.ACTIVE;
}
public enum SessionState {
INACTIVE,
WAITING_FOR_TOKEN,
WAITING_FOR_LOGIN,
PICKING_CHARACTER,
ACTIVE
}
public enum SessionState {
INACTIVE,
WAITING_FOR_TOKEN,
WAITING_FOR_LOGIN,
PICKING_CHARACTER,
ACTIVE
}
}

View File

@@ -11,9 +11,9 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import static emu.grasscutter.Configuration.*;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
/**
@@ -30,22 +30,22 @@ public final class HttpServer {
this.express = new Express(config -> {
// Set the Express HTTP server.
config.server(HttpServer::createServer);
// Configure encryption/HTTPS/SSL.
config.enforceSsl = HTTP_ENCRYPTION.useEncryption;
// Configure HTTP policies.
if(HTTP_POLICIES.cors.enabled) {
if (HTTP_POLICIES.cors.enabled) {
var allowedOrigins = HTTP_POLICIES.cors.allowedOrigins;
if (allowedOrigins.length > 0)
config.enableCorsForOrigin(allowedOrigins);
else config.enableCorsForAllOrigins();
}
// Configure debug logging.
if(SERVER.debugLevel == ServerDebugMode.ALL)
if (DISPATCH_INFO.logRequests == ServerDebugMode.ALL)
config.enableDevLogging();
// Disable compression on static files.
config.precompressStaticFiles = false;
});
@@ -60,26 +60,26 @@ public final class HttpServer {
Server server = new Server();
ServerConnector serverConnector
= new ServerConnector(server);
if(HTTP_ENCRYPTION.useEncryption) {
if (HTTP_ENCRYPTION.useEncryption) {
var sslContextFactory = new SslContextFactory.Server();
var keystoreFile = new File(HTTP_ENCRYPTION.keystore);
if(!keystoreFile.exists()) {
if (!keystoreFile.exists()) {
HTTP_ENCRYPTION.useEncryption = false;
HTTP_ENCRYPTION.useInRouting = false;
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.no_keystore_error"));
} else try {
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
sslContextFactory.setKeyStorePassword(HTTP_ENCRYPTION.keystorePassword);
} catch (Exception ignored) {
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.password_error"));
try {
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
sslContextFactory.setKeyStorePassword("123456");
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.default_password"));
} catch (Exception exception) {
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.general_error"), exception);
@@ -88,10 +88,10 @@ public final class HttpServer {
serverConnector = new ServerConnector(server, sslContextFactory);
}
}
serverConnector.setPort(HTTP_INFO.bindPort);
server.setConnectors(new ServerConnector[]{serverConnector});
return server;
}
@@ -112,9 +112,9 @@ public final class HttpServer {
public HttpServer addRouter(Class<? extends Router> router, Object... args) {
// Get all constructor parameters.
Class<?>[] types = new Class<?>[args.length];
for(var argument : args)
for (var argument : args)
types[args.length - 1] = argument.getClass();
try { // Create a router instance & apply routes.
var constructor = router.getDeclaredConstructor(types); // Get the constructor.
var routerInstance = constructor.newInstance(args); // Create instance.
@@ -126,15 +126,16 @@ public final class HttpServer {
/**
* Starts listening on the HTTP server.
* @throws UnsupportedEncodingException
*/
public void start() {
public void start() throws UnsupportedEncodingException {
// Attempt to start the HTTP server.
if(HTTP_INFO.bindAddress.equals("")){
if (HTTP_INFO.bindAddress.equals("")) {
this.express.listen(HTTP_INFO.bindPort);
}else{
}else {
this.express.listen(HTTP_INFO.bindAddress, HTTP_INFO.bindPort);
}
// Log bind information.
Grasscutter.getLogger().info(translate("messages.dispatch.port_bind", Integer.toString(this.express.raw().port())));
}
@@ -146,7 +147,7 @@ public final class HttpServer {
@Override public void applyRoutes(Express express, Javalin handle) {
express.get("/", (request, response) -> {
File file = new File(HTTP_STATIC_FILES.indexFile);
if(!file.exists())
if (!file.exists())
response.send("""
<!DOCTYPE html>
<html>
@@ -172,19 +173,19 @@ public final class HttpServer {
public static class UnhandledRequestRouter implements Router {
@Override public void applyRoutes(Express express, Javalin handle) {
handle.error(404, context -> {
if(SERVER.debugLevel == ServerDebugMode.MISSING)
if (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
Grasscutter.getLogger().info(translate("messages.dispatch.unhandled_request_error", context.method(), context.url()));
context.contentType("text/html");
File file = new File(HTTP_STATIC_FILES.errorFile);
if(!file.exists())
if (!file.exists())
context.result("""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<body>
<img src="https://http.cat/404" />
</body>

View File

@@ -1,16 +1,15 @@
package emu.grasscutter.server.http.dispatch;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.*;
import emu.grasscutter.net.proto.RegionInfoOuterClass;
import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp;
import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo;
import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent;
import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent;
import emu.grasscutter.server.http.Router;
import emu.grasscutter.server.http.objects.QueryCurRegionRspJson;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils;
@@ -19,15 +18,19 @@ import express.http.Request;
import express.http.Response;
import io.javalin.Javalin;
import java.io.File;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.security.Signature;
import static emu.grasscutter.Configuration.*;
import static emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.*;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp;
/**
* Handles requests related to region queries.
@@ -35,7 +38,7 @@ import static emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.*;
public final class RegionHandler implements Router {
private static final Map<String, RegionData> regions = new ConcurrentHashMap<>();
private static String regionListResponse;
public RegionHandler() {
try { // Read & initialize region data.
this.initialize();
@@ -51,33 +54,33 @@ public final class RegionHandler implements Router {
String dispatchDomain = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort);
// Create regions.
List<RegionSimpleInfo> servers = new ArrayList<>();
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts.
var configuredRegions = new ArrayList<>(List.of(DISPATCH_INFO.regions));
if(SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
if (SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
System.exit(1);
} else if (configuredRegions.size() == 0)
} else if (configuredRegions.size() == 0)
configuredRegions.add(new Region("os_usa", DISPATCH_INFO.defaultName,
lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress),
lr(GAME_INFO.accessPort, GAME_INFO.bindPort)));
lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress),
lr(GAME_INFO.accessPort, GAME_INFO.bindPort)));
configuredRegions.forEach(region -> {
if (usedNames.contains(region.Name)) {
Grasscutter.getLogger().error("Region name already in use.");
return;
}
// Create a region identifier.
var identifier = RegionSimpleInfo.newBuilder()
.setName(region.Name).setTitle(region.Title).setType("DEV_PUBLIC")
.setDispatchUrl(dispatchDomain + "/query_cur_region/" + region.Name)
.build();
usedNames.add(region.Name); servers.add(identifier);
// Create a region info object.
var regionInfo = RegionInfo.newBuilder()
.setGateserverIp(region.Ip).setGateserverPort(region.Port)
@@ -87,22 +90,22 @@ public final class RegionHandler implements Router {
var updatedQuery = QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(regionInfo).build();
regions.put(region.Name, new RegionData(updatedQuery, Utils.base64Encode(updatedQuery.toByteString().toByteArray())));
});
// Create a config object.
byte[] customConfig = "{\"sdkenv\":\"2\",\"checkdevice\":\"false\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}".getBytes();
Crypto.xor(customConfig, Crypto.DISPATCH_KEY); // XOR the config with the key.
// Create an updated region list.
QueryRegionListHttpRsp updatedRegionList = QueryRegionListHttpRsp.newBuilder()
.addAllRegionList(servers)
.setClientSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.setClientCustomConfigEncrypted(ByteString.copyFrom(customConfig))
.setEnableLoginPc(true).build();
// Set the region list response.
regionListResponse = Utils.base64Encode(updatedRegionList.toByteString().toByteArray());
}
@Override public void applyRoutes(Express express, Javalin handle) {
express.get("/query_region_list", RegionHandler::queryRegionList);
express.get("/query_cur_region/:region", RegionHandler::queryCurrentRegion );
@@ -116,7 +119,7 @@ public final class RegionHandler implements Router {
QueryAllRegionsEvent event = new QueryAllRegionsEvent(regionListResponse); event.call();
// Respond with event result.
response.send(event.getRegionList());
// Log to console.
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_region_list", request.ip()));
}
@@ -127,19 +130,71 @@ public final class RegionHandler implements Router {
private static void queryCurrentRegion(Request request, Response response) {
// Get region to query.
String regionName = request.params("region");
String versionName = request.query("version");
var region = regions.get(regionName);
// Get region data.
String regionData = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==";
if (request.query().values().size() > 0) {
var region = regions.get(regionName);
if(region != null) regionData = region.getBase64();
if (region != null)
regionData = region.getBase64();
}
// Invoke event.
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
// Respond with event result.
response.send(event.getRegionInfo());
if ( versionName.contains("2.7.5") || versionName.contains("2.8.")) {
try {
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
if (request.query("dispatchSeed") == null) {
// More love for UA Patch players
var rsp = new QueryCurRegionRspJson();
rsp.content = event.getRegionInfo();
rsp.sign = "TW9yZSBsb3ZlIGZvciBVQSBQYXRjaCBwbGF5ZXJz";
response.send(rsp);
return;
}
String key_id = request.query("key_id");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key_id.equals("3") ? Crypto.CUR_OS_ENCRYPT_KEY : Crypto.CUR_CN_ENCRYPT_KEY);
var regionInfo = Utils.base64Decode(event.getRegionInfo());
//Encrypt regionInfo in chunks
ByteArrayOutputStream encryptedRegionInfoStream = new ByteArrayOutputStream();
//Thank you so much GH Copilot
int chunkSize = 256 - 11;
int regionInfoLength = regionInfo.length;
int numChunks = (int) Math.ceil(regionInfoLength / (double) chunkSize);
for (int i = 0; i < numChunks; i++) {
byte[] chunk = Arrays.copyOfRange(regionInfo, i * chunkSize, Math.min((i + 1) * chunkSize, regionInfoLength));
byte[] encryptedChunk = cipher.doFinal(chunk);
encryptedRegionInfoStream.write(encryptedChunk);
}
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(Crypto.CUR_SIGNING_KEY);
privateSignature.update(regionInfo);
var rsp = new QueryCurRegionRspJson();
rsp.content = Utils.base64Encode(encryptedRegionInfoStream.toByteArray());
rsp.sign = Utils.base64Encode(privateSignature.sign());
response.send(rsp);
}
catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while handling query_cur_region.", e);
}
}
else {
// Invoke event.
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
// Respond with event result.
response.send(event.getRegionInfo());
}
// Log to console.
Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region/%s", request.ip(), regionName));
}

View File

@@ -1,16 +1,17 @@
package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.Configuration.RESOURCE;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.utils.Utils;
import static emu.grasscutter.Configuration.DOCUMENT_LANGUAGE;
import express.http.Request;
import express.http.Response;
import static emu.grasscutter.config.Configuration.DOCUMENT_LANGUAGE;
import static emu.grasscutter.config.Configuration.RESOURCE;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

View File

@@ -1,6 +1,6 @@
package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.Configuration.*;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
import com.google.gson.reflect.TypeToken;
@@ -81,10 +81,10 @@ final class HandbookRequestHandler implements DocumentationHandler {
.replace("{{HEADER_SCENE}}", translate("documentation.handbook.header_scene"))
.replace("{{HEADER_MONSTER}}", translate("documentation.handbook.header_monster"))
// Commands table
.replace("{{COMMANDS_TABLE}}", cmdMap.getAnnotationsAsList()
.replace("{{COMMANDS_TABLE}}", cmdMap.getHandlersAsList()
.stream()
.map(cmd -> "<tr><td><code>" + cmd.label() + "</code></td><td>" +
cmd.description() + "</td></tr>")
.map(cmd -> "<tr><td><code>" + cmd.getLabel() + "</code></td><td>" +
cmd.getDescriptionString(null) + "</td></tr>")
.collect(Collectors.joining("\n")))
// Avatars table
.replace("{{AVATARS_TABLE}}", GameData.getAvatarDataMap().keySet()

View File

@@ -1,6 +1,6 @@
package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.Configuration.DATA;
import static emu.grasscutter.config.Configuration.DATA;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter;

View File

@@ -12,6 +12,8 @@ import express.http.Request;
import express.http.Response;
import io.javalin.Javalin;
import static emu.grasscutter.config.Configuration.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -19,8 +21,6 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import static emu.grasscutter.Configuration.*;
/**
* Handles requests related to the announcements page.
*/
@@ -39,14 +39,14 @@ public final class AnnouncementsHandler implements Router {
express.get("/hk4e/announcement/*", AnnouncementsHandler::getPageResources);
}
private static void getAnnouncement(Request request, Response response) {
String data = "";
if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnContent")) {
try {
data = FileUtils.readToString(DataLoader.load("GameAnnouncement.json"));
} catch (Exception e) {
if(e.getClass() == IOException.class) {
if (e.getClass() == IOException.class) {
Grasscutter.getLogger().info("Unable to read file 'GameAnnouncementList.json'. \n" + e);
}
}
@@ -54,7 +54,7 @@ public final class AnnouncementsHandler implements Router {
try {
data = FileUtils.readToString(DataLoader.load("GameAnnouncementList.json"));
} catch (Exception e) {
if(e.getClass() == IOException.class) {
if (e.getClass() == IOException.class) {
Grasscutter.getLogger().info("Unable to read file 'GameAnnouncementList.json'. \n" + e);
}
}
@@ -76,9 +76,9 @@ public final class AnnouncementsHandler implements Router {
.replace("{{SYSTEM_TIME}}", String.valueOf(System.currentTimeMillis()));
response.send("{\"retcode\":0,\"message\":\"OK\",\"data\": " + data + "}");
}
private static void getPageResources(Request request, Response response) {
try(InputStream filestream = DataLoader.load(request.path())) {
try (InputStream filestream = DataLoader.load(request.path())) {
String possibleFilename = Utils.toFilePath(DATA(request.path()));
MediaType fromExtension = MediaType.getByExtension(possibleFilename.substring(possibleFilename.lastIndexOf(".") + 1));

View File

@@ -4,7 +4,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.gacha.GachaBanner;
import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.gacha.GachaSystem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.http.Router;
import emu.grasscutter.tools.Tools;
@@ -22,7 +22,7 @@ import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import static emu.grasscutter.Configuration.DATA;
import static emu.grasscutter.config.Configuration.DATA;
import static emu.grasscutter.utils.Language.translate;
/**
@@ -30,14 +30,14 @@ import static emu.grasscutter.utils.Language.translate;
*/
public final class GachaHandler implements Router {
public static final String gachaMappings = DATA(Utils.toFilePath("gacha/mappings.js"));
@Override public void applyRoutes(Express express, Javalin handle) {
express.get("/gacha", GachaHandler::gachaRecords);
express.get("/gacha/details", GachaHandler::gachaDetails);
express.useStaticFallback("/gacha/mappings", gachaMappings, Location.EXTERNAL);
}
private static void gachaRecords(Request request, Response response) {
File recordsTemplate = new File(Utils.toFilePath(DATA("gacha/records.html")));
if (!recordsTemplate.exists()) {
@@ -48,7 +48,7 @@ public final class GachaHandler implements Router {
String sessionKey = request.query("s");
Account account = DatabaseHelper.getAccountBySessionKey(sessionKey);
if(account == null) {
if (account == null) {
response.status(403).send("Requested account was not found");
return;
}
@@ -59,9 +59,9 @@ public final class GachaHandler implements Router {
}
int page = 0, gachaType = 0;
if(request.query("p") != null)
if (request.query("p") != null)
page = Integer.parseInt(request.query("p"));
if(request.query("gachaType") != null)
if (request.query("gachaType") != null)
gachaType = Integer.parseInt(request.query("gachaType"));
String records = DatabaseHelper.getGachaRecords(player.getUid(), page, gachaType).toString();
@@ -76,7 +76,7 @@ public final class GachaHandler implements Router {
.replace("{{LANGUAGE}}", Utils.getLanguageCode(account.getLocale()));
response.send(template);
}
private static void gachaDetails(Request request, Response response) {
File detailsTemplate = new File(Utils.toFilePath(DATA("gacha/details.html")));
if (!detailsTemplate.exists()) {
@@ -87,7 +87,7 @@ public final class GachaHandler implements Router {
String sessionKey = request.query("s");
Account account = DatabaseHelper.getAccountBySessionKey(sessionKey);
if(account == null) {
if (account == null) {
response.status(403).send("Requested account was not found");
return;
}
@@ -108,7 +108,7 @@ public final class GachaHandler implements Router {
// Get the banner info for the banner we want.
int scheduleId = Integer.parseInt(request.query("scheduleId"));
GachaManager manager = Grasscutter.getGameServer().getGachaManager();
GachaSystem manager = Grasscutter.getGameServer().getGachaSystem();
GachaBanner banner = manager.getGachaBanners().get(scheduleId);
// Add 5-star items.

View File

@@ -1,5 +1,7 @@
package emu.grasscutter.server.http.handlers;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.http.objects.HttpJsonResponse;
@@ -10,8 +12,6 @@ import express.http.Request;
import express.http.Response;
import io.javalin.Javalin;
import static emu.grasscutter.Configuration.ACCOUNT;
/**
* Handles all generic, hard-coded responses.
*/
@@ -25,7 +25,7 @@ public final class GenericHandler implements Router {
// api-account-os.hoyoverse.com
express.post("/account/risky/api/check", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"none\",\"action\":\"ACTION_NONE\",\"geetest\":null}}"));
// sdk-os-static.hoyoverse.com
express.get("/combo/box/api/config/sdk/combo", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}"));
// hk4e-sdk-os-static.hoyoverse.com
@@ -35,7 +35,7 @@ public final class GenericHandler implements Router {
// Test api?
// abtest-api-data-sg.hoyoverse.com
express.post("/data_abtest_api/config/experiment/list", new HttpJsonResponse("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}"));
// log-upload-os.mihoyo.com
express.all("/log/sdk/upload", new HttpJsonResponse("{\"code\":0}"));
express.all("/sdk/upload", new HttpJsonResponse("{\"code\":0}"));
@@ -45,10 +45,10 @@ public final class GenericHandler implements Router {
// webstatic-sea.hoyoverse.com
express.get("/admin/mi18n/plat_oversea/*", new WebStaticVersionResponse());
express.get("/status/server", GenericHandler::serverStatus);
}
private static void serverStatus(Request request, Response response) {
int playerCount = Grasscutter.getGameServer().getPlayers().size();
int maxPlayer = ACCOUNT.maxPlayer;

View File

@@ -10,34 +10,34 @@ import express.http.HttpContextHandler;
import express.http.Request;
import express.http.Response;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
import static emu.grasscutter.Configuration.*;
public final class HttpJsonResponse implements HttpContextHandler {
private final String response;
private final String[] missingRoutes = { // TODO: When http requests for theses routes are found please remove it from this list and update the route request type in the DispatchServer
"/common/hk4e_global/announcement/api/getAlertPic",
"/common/hk4e_global/announcement/api/getAlertAnn",
"/common/hk4e_global/announcement/api/getAnnList",
"/common/hk4e_global/announcement/api/getAnnContent",
"/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier",
"/log/sdk/upload",
"/sdk/upload",
"/perf/config/verify",
"/log",
"/crash/dataUpload"
};
public HttpJsonResponse(String response) {
this.response = response;
}
private final String response;
private final String[] missingRoutes = { // TODO: When http requests for theses routes are found please remove it from this list and update the route request type in the DispatchServer
"/common/hk4e_global/announcement/api/getAlertPic",
"/common/hk4e_global/announcement/api/getAlertAnn",
"/common/hk4e_global/announcement/api/getAnnList",
"/common/hk4e_global/announcement/api/getAnnContent",
"/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier",
"/log/sdk/upload",
"/sdk/upload",
"/perf/config/verify",
"/log",
"/crash/dataUpload"
};
@Override
public void handle(Request req, Response res) throws IOException {
// Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled
if(SERVER.debugLevel == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> Objects.equals(x, req.baseUrl()))) {
Grasscutter.getLogger().info(translate("messages.dispatch.request", req.ip(), req.method(), req.baseUrl()) + (SERVER.debugLevel == ServerDebugMode.MISSING ? "(MISSING)" : ""));
}
res.send(response);
}
public HttpJsonResponse(String response) {
this.response = response;
}
@Override
public void handle(Request req, Response res) throws IOException {
// Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled
if (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> Objects.equals(x, req.baseUrl()))) {
Grasscutter.getLogger().info(translate("messages.dispatch.request", req.ip(), req.method(), req.baseUrl()) + (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING ? "(MISSING)" : ""));
}
res.send(response);
}
}

View File

@@ -0,0 +1,6 @@
package emu.grasscutter.server.http.objects;
public class QueryCurRegionRspJson {
public String content;
public String sign;
}

View File

@@ -10,11 +10,12 @@ import express.http.Request;
import express.http.Response;
import io.javalin.core.util.FileUtil;
import static emu.grasscutter.config.Configuration.DATA;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import java.io.IOException;
import java.io.InputStream;
import static emu.grasscutter.Configuration.DATA;
public class WebStaticVersionResponse implements HttpContextHandler {
@Override
@@ -26,13 +27,13 @@ public class WebStaticVersionResponse implements HttpContextHandler {
}
private static void getPageResources(String path, Response response) {
try(InputStream filestream = FileUtils.readResourceAsStream(path)) {
try (InputStream filestream = FileUtils.readResourceAsStream(path)) {
MediaType fromExtension = MediaType.getByExtension(path.substring(path.lastIndexOf(".") + 1));
response.type((fromExtension != null) ? fromExtension.getMIME() : "application/octet-stream");
response.send(filestream.readAllBytes());
} catch (Exception e) {
if(Grasscutter.getConfig().server.debugLevel.equals(Grasscutter.ServerDebugMode.MISSING)) {
if (DISPATCH_INFO.logRequests == Grasscutter.ServerDebugMode.MISSING) {
Grasscutter.getLogger().warn("Webstatic File Missing: " + path);
}
response.status(404);

View File

@@ -34,13 +34,13 @@ public class HandlerAvatarExpeditionGetRewardReq extends PacketHandler {
List<GameItem> items = new LinkedList<>();
if (session.getServer().getExpeditionManager().getExpeditionRewardDataList().containsKey(expInfo.getExpId())) {
for (ExpeditionRewardDataList RewardDataList : session.getServer().getExpeditionManager().getExpeditionRewardDataList().get(expInfo.getExpId())) {
if(RewardDataList.getHourTime() == expInfo.getHourTime()){
if(!RewardDataList.getExpeditionRewardData().isEmpty()){
if (session.getServer().getExpeditionSystem().getExpeditionRewardDataList().containsKey(expInfo.getExpId())) {
for (ExpeditionRewardDataList RewardDataList : session.getServer().getExpeditionSystem().getExpeditionRewardDataList().get(expInfo.getExpId())) {
if (RewardDataList.getHourTime() == expInfo.getHourTime()) {
if (!RewardDataList.getExpeditionRewardData().isEmpty()) {
for (ExpeditionRewardData RewardData :RewardDataList.getExpeditionRewardData()) {
int num = RewardData.getMinCount();
if(RewardData.getMinCount() != RewardData.getMaxCount()){
if (RewardData.getMinCount() != RewardData.getMaxCount()) {
num = Utils.randomRange(RewardData.getMinCount(), RewardData.getMaxCount());
}
items.add(new GameItem(RewardData.getItemId(), num));

View File

@@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.AvatarPromoteReq)
public class HandlerAvatarPromoteReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarPromoteReq req = AvatarPromoteReq.parseFrom(payload);
// Ascend avatar
session.getServer().getInventoryManager().promoteAvatar(session.getPlayer(), req.getGuid());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarPromoteReq req = AvatarPromoteReq.parseFrom(payload);
// Ascend avatar
session.getServer().getInventorySystem().promoteAvatar(session.getPlayer(), req.getGuid());
}
}

View File

@@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.AvatarSkillUpgradeReq)
public class HandlerAvatarSkillUpgradeReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarSkillUpgradeReq req = AvatarSkillUpgradeReq.parseFrom(payload);
// Level up avatar talent
session.getServer().getInventoryManager().upgradeAvatarSkill(session.getPlayer(), req.getAvatarGuid(), req.getAvatarSkillId());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarSkillUpgradeReq req = AvatarSkillUpgradeReq.parseFrom(payload);
// Level up avatar talent
session.getServer().getInventorySystem().upgradeAvatarSkill(session.getPlayer(), req.getAvatarGuid(), req.getAvatarSkillId());
}
}

View File

@@ -8,18 +8,18 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.AvatarUpgradeReq)
public class HandlerAvatarUpgradeReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarUpgradeReq req = AvatarUpgradeReq.parseFrom(payload);
// Level up avatar
session.getServer().getInventoryManager().upgradeAvatar(
session.getPlayer(),
req.getAvatarGuid(),
req.getItemId(),
req.getCount()
);
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarUpgradeReq req = AvatarUpgradeReq.parseFrom(payload);
// Level up avatar
session.getServer().getInventorySystem().upgradeAvatar(
session.getPlayer(),
req.getAvatarGuid(),
req.getItemId(),
req.getCount()
);
}
}

View File

@@ -6,7 +6,7 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.shop.ShopInfo;
import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.shop.ShopManager;
import emu.grasscutter.game.shop.ShopSystem;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
@@ -29,7 +29,7 @@ public class HandlerBuyGoodsReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
BuyGoodsReqOuterClass.BuyGoodsReq buyGoodsReq = BuyGoodsReqOuterClass.BuyGoodsReq.parseFrom(payload);
List<ShopInfo> configShop = session.getServer().getShopManager().getShopData().get(buyGoodsReq.getShopType());
List<ShopInfo> configShop = session.getServer().getShopSystem().getShopData().get(buyGoodsReq.getShopType());
if (configShop == null)
return;
@@ -46,7 +46,7 @@ public class HandlerBuyGoodsReq extends PacketHandler {
int bought = 0;
if (shopLimit != null) {
if (currentTs > shopLimit.getNextRefreshTime()) {
shopLimit.setNextRefreshTime(ShopManager.getShopNextRefreshTime(sg));
shopLimit.setNextRefreshTime(ShopSystem.getShopNextRefreshTime(sg));
} else {
bought = shopLimit.getHasBoughtInPeriod();
}
@@ -65,7 +65,7 @@ public class HandlerBuyGoodsReq extends PacketHandler {
return;
}
session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBuyCount(), ShopManager.getShopNextRefreshTime(sg));
session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBuyCount(), ShopSystem.getShopNextRefreshTime(sg));
GameItem item = new GameItem(GameData.getItemDataMap().get(sg.getGoodsItem().getId()));
item.setCount(buyGoodsReq.getBuyCount() * sg.getGoodsItem().getCount());
session.getPlayer().getInventory().addItem(item, ActionReason.Shop, true); // fix: not notify when got virtual item from shop

View File

@@ -12,23 +12,23 @@ import emu.grasscutter.server.packet.send.PacketCalcWeaponUpgradeReturnItemsRsp;
@Opcodes(PacketOpcodes.CalcWeaponUpgradeReturnItemsReq)
public class HandlerCalcWeaponUpgradeReturnItemsReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
CalcWeaponUpgradeReturnItemsReq req = CalcWeaponUpgradeReturnItemsReq.parseFrom(payload);
List<ItemParam> returnOres = session.getServer().getInventoryManager().calcWeaponUpgradeReturnItems(
session.getPlayer(),
req.getTargetWeaponGuid(),
req.getFoodWeaponGuidListList(),
req.getItemParamListList()
);
if (returnOres != null) {
session.send(new PacketCalcWeaponUpgradeReturnItemsRsp(req.getTargetWeaponGuid(), returnOres));
} else {
session.send(new PacketCalcWeaponUpgradeReturnItemsRsp());
}
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
CalcWeaponUpgradeReturnItemsReq req = CalcWeaponUpgradeReturnItemsReq.parseFrom(payload);
List<ItemParam> returnOres = session.getServer().getInventorySystem().calcWeaponUpgradeReturnItems(
session.getPlayer(),
req.getTargetWeaponGuid(),
req.getFoodWeaponGuidListList(),
req.getItemParamListList()
);
if (returnOres != null) {
session.send(new PacketCalcWeaponUpgradeReturnItemsRsp(req.getTargetWeaponGuid(), returnOres));
} else {
session.send(new PacketCalcWeaponUpgradeReturnItemsRsp());
}
}
}

View File

@@ -11,6 +11,7 @@ import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
import emu.grasscutter.net.proto.EvtAnimatorParameterInfoOuterClass.EvtAnimatorParameterInfo;
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
@@ -33,6 +34,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload);
for (CombatInvokeEntry entry : notif.getInvokeListList()) {
// Handle combat invoke
switch (entry.getArgumentType()) {
case COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT:
EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData());
@@ -82,8 +84,21 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
handleFallOnGround(session, entity, motionState);
}
}
// MOTION_STATE_NOTIFY = Dont send to other players
if (motionState == MotionState.MOTION_STATE_NOTIFY) {
continue;
}
}
break;
case COMBAT_TYPE_ARGUMENT_ANIMATOR_PARAMETER_CHANGED:
EvtAnimatorParameterInfo paramInfo = EvtAnimatorParameterInfo.parseFrom(entry.getCombatData());
if (paramInfo.getIsServerCache()) {
paramInfo = paramInfo.toBuilder().setIsServerCache(false).build();
entry = entry.toBuilder().setCombatData(paramInfo.toByteString()).build();
}
break;
default:
break;
}

View File

@@ -21,10 +21,10 @@ public class HandlerCombineReq extends PacketHandler {
CombineReqOuterClass.CombineReq req = CombineReqOuterClass.CombineReq.parseFrom(payload);
var result = session.getServer().getCombineManger()
var result = session.getServer().getCombineSystem()
.combineItem(session.getPlayer(), req.getCombineId(), req.getCombineCount());
if(result == null){
if (result == null) {
return;
}
@@ -36,7 +36,7 @@ public class HandlerCombineReq extends PacketHandler {
toItemParamList(result.getBack())));
}
private List<ItemParamOuterClass.ItemParam> toItemParamList(List<ItemParamData> list){
private List<ItemParamOuterClass.ItemParam> toItemParamList(List<ItemParamData> list) {
return list.stream()
.map(item -> ItemParamOuterClass.ItemParam.newBuilder()
.setItemId(item.getId())

View File

@@ -8,11 +8,11 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.DestroyMaterialReq)
public class HandlerDestroyMaterialReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DestroyMaterialReq req = DestroyMaterialReq.parseFrom(payload);
// Delete items
session.getServer().getInventoryManager().destroyMaterial(session.getPlayer(), req.getMaterialListList());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DestroyMaterialReq req = DestroyMaterialReq.parseFrom(payload);
// Delete items
session.getServer().getInventorySystem().destroyMaterial(session.getPlayer(), req.getMaterialListList());
}
}

View File

@@ -8,10 +8,10 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.DoGachaReq)
public class HandlerDoGachaReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DoGachaReq req = DoGachaReq.parseFrom(payload);
session.getServer().getGachaManager().doPulls(session.getPlayer(), req.getGachaScheduleId(), req.getGachaTimes());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DoGachaReq req = DoGachaReq.parseFrom(payload);
session.getServer().getGachaSystem().doPulls(session.getPlayer(), req.getGachaScheduleId(), req.getGachaTimes());
}
}

View File

@@ -8,12 +8,12 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.DungeonEntryInfoReq)
public class HandlerDungeonEntryInfoReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload);
session.getServer().getDungeonManager().getEntryInfo(session.getPlayer(), req.getPointId());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload);
session.getServer().getDungeonSystem().getEntryInfo(session.getPlayer(), req.getPointId());
}
}

View File

@@ -11,6 +11,6 @@ import emu.grasscutter.server.game.GameSession;
public class HandlerEnterTransPointRegionNotify extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{
session.getPlayer().getSotSManager().handleEnterTransPointRegionNotify();
session.getPlayer().getSotsManager().handleEnterTransPointRegionNotify();
}
}

View File

@@ -12,6 +12,6 @@ import emu.grasscutter.server.game.GameSession;
public class HandlerExitTransPointRegionNotify extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{
session.getPlayer().getSotSManager().handleExitTransPointRegionNotify();
session.getPlayer().getSotsManager().handleExitTransPointRegionNotify();
}
}

View File

@@ -16,7 +16,7 @@ public class HandlerGachaWishReq extends PacketHandler {
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
GachaWishReq req = GachaWishReq.parseFrom(payload);
GachaBanner banner = session.getServer().getGachaManager().getGachaBanners().get(req.getGachaScheduleId());
GachaBanner banner = session.getServer().getGachaSystem().getGachaBanners().get(req.getGachaScheduleId());
PlayerGachaBannerInfo gachaInfo = session.getPlayer().getGachaInfo().getBannerInfo(banner);
gachaInfo.setFailedChosenItemPulls(0);

View File

@@ -16,6 +16,6 @@ public class HandlerGetAllMailReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
GetAllMailReqOuterClass.GetAllMailReq req = GetAllMailReqOuterClass.GetAllMailReq.parseFrom(payload);
session.send(new PacketGetAllMailRsp(session.getPlayer(), req.getANKKGPJCINB()));
session.send(new PacketGetAllMailRsp(session.getPlayer(), req.getUnk2700OPEHLDAGICF()));
}
}

View File

@@ -11,10 +11,10 @@ import emu.grasscutter.server.packet.send.PacketGetGachaInfoRsp;
@Opcodes(PacketOpcodes.GetGachaInfoReq)
public class HandlerGetGachaInfoReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaManager(), session.getPlayer()));
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaSystem(), session.getPlayer()));
}
}

View File

@@ -9,16 +9,16 @@ import emu.grasscutter.server.packet.send.PacketGetInvestigationMonsterRsp;
@Opcodes(PacketOpcodes.GetInvestigationMonsterReq)
public class HandlerGetInvestigationMonsterReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = GetInvestigationMonsterReqOuterClass.GetInvestigationMonsterReq.parseFrom(payload);
session.send(new PacketGetInvestigationMonsterRsp(
session.getPlayer(),
session.getServer().getWorldDataManager(),
req.getCityIdListList()));
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = GetInvestigationMonsterReqOuterClass.GetInvestigationMonsterReq.parseFrom(payload);
}
session.send(new PacketGetInvestigationMonsterRsp(
session.getPlayer(),
session.getServer().getWorldDataSystem(),
req.getCityIdListList()));
}
}

View File

@@ -1,7 +1,5 @@
package emu.grasscutter.server.packet.recv;
import static emu.grasscutter.Configuration.ACCOUNT;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
@@ -14,84 +12,130 @@ import emu.grasscutter.server.event.game.PlayerCreationEvent;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.game.GameSession.SessionState;
import emu.grasscutter.server.packet.send.PacketGetPlayerTokenRsp;
import emu.grasscutter.utils.ByteHelper;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.Utils;
import javax.crypto.Cipher;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import java.nio.ByteBuffer;
import java.security.Signature;
@Opcodes(PacketOpcodes.GetPlayerTokenReq)
public class HandlerGetPlayerTokenReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
GetPlayerTokenReq req = GetPlayerTokenReq.parseFrom(payload);
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
GetPlayerTokenReq req = GetPlayerTokenReq.parseFrom(payload);
// Authenticate
Account account = DatabaseHelper.getAccountById(req.getAccountUid());
if (account == null || !account.getToken().equals(req.getAccountToken())) {
return;
}
// Authenticate
Account account = DatabaseHelper.getAccountById(req.getAccountUid());
if (account == null || !account.getToken().equals(req.getAccountToken())) {
return;
}
// Set account
session.setAccount(account);
// Set account
session.setAccount(account);
// Check if player object exists in server
// NOTE: CHECKING MUST SITUATED HERE (BEFORE getPlayerByUid)! because to save firstly ,to load secondly !!!
// TODO - optimize
boolean kicked = false;
Player exists = Grasscutter.getGameServer().getPlayerByAccountId(account.getId());
if (exists != null) {
GameSession existsSession = exists.getSession();
if (existsSession != session) {// No self-kicking
exists.onLogout();//must save immediately , or the below will load old data
existsSession.close();
Grasscutter.getLogger().warn("Player {} was kicked due to duplicated login", account.getUsername());
kicked = true;
}
}
// Check if player object exists in server
// NOTE: CHECKING MUST SITUATED HERE (BEFORE getPlayerByUid)! because to save firstly ,to load secondly !!!
// TODO - optimize
boolean kicked = false;
Player exists = Grasscutter.getGameServer().getPlayerByAccountId(account.getId());
if (exists != null) {
GameSession existsSession = exists.getSession();
if (existsSession != session) {// No self-kicking
exists.onLogout();//must save immediately , or the below will load old data
existsSession.close();
Grasscutter.getLogger().warn("Player {} was kicked due to duplicated login", account.getUsername());
kicked = true;
}
}
//NOTE: If there are 5 online players, max count of player is 5,
// a new client want to login by kicking one of them ,
// I think it should be allowed
if(!kicked) {
// Max players limit
if (ACCOUNT.maxPlayer > -1 && Grasscutter.getGameServer().getPlayers().size() >= ACCOUNT.maxPlayer) {
session.close();
return;
}
}
//NOTE: If there are 5 online players, max count of player is 5,
// a new client want to login by kicking one of them ,
// I think it should be allowed
if (!kicked) {
// Max players limit
if (ACCOUNT.maxPlayer > -1 && Grasscutter.getGameServer().getPlayers().size() >= ACCOUNT.maxPlayer) {
session.close();
return;
}
}
// Call creation event.
PlayerCreationEvent event = new PlayerCreationEvent(session, Player.class); event.call();
// Get player.
Player player = DatabaseHelper.getPlayerByAccount(account, event.getPlayerClass());
// Get player.
Player player = DatabaseHelper.getPlayerByAccount(account, event.getPlayerClass());
if (player == null) {
int nextPlayerUid = DatabaseHelper.getNextPlayerId(session.getAccount().getReservedPlayerUid());
if (player == null) {
int nextPlayerUid = DatabaseHelper.getNextPlayerId(session.getAccount().getReservedPlayerUid());
// Create player instance from event.
player = event.getPlayerClass().getDeclaredConstructor(GameSession.class).newInstance(session);
// Create player instance from event.
player = event.getPlayerClass().getDeclaredConstructor(GameSession.class).newInstance(session);
// Save to db
DatabaseHelper.generatePlayerUid(player, nextPlayerUid);
}
// Save to db
DatabaseHelper.generatePlayerUid(player, nextPlayerUid);
}
// Set player object for session
session.setPlayer(player);
// Set player object for session
session.setPlayer(player);
// Checks if the player is banned
if (session.getAccount().isBanned()) {
session.send(new PacketGetPlayerTokenRsp(session, 21, "FORBID_CHEATING_PLUGINS", session.getAccount().getBanEndTime()));
session.close();
return;
}
// Checks if the player is banned
if (session.getAccount().isBanned()) {
session.send(new PacketGetPlayerTokenRsp(session, 21, "FORBID_CHEATING_PLUGINS", session.getAccount().getBanEndTime()));
session.close();
return;
}
// Load player from database
player.loadFromDatabase();
// Load player from database
player.loadFromDatabase();
// Set session state
session.setUseSecretKey(true);
session.setState(SessionState.WAITING_FOR_LOGIN);
// Set session state
session.setUseSecretKey(true);
session.setState(SessionState.WAITING_FOR_LOGIN);
// Send packet
session.send(new PacketGetPlayerTokenRsp(session));
}
// Only >= 2.7.50 has this
if (req.getKeyId() > 0) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, Crypto.CUR_SIGNING_KEY);
var client_seed_encrypted = Utils.base64Decode(req.getClientSeed());
var client_seed = ByteBuffer.wrap(cipher.doFinal(client_seed_encrypted))
.getLong();
byte[] seed_bytes = ByteBuffer.wrap(new byte[8])
.putLong(Crypto.ENCRYPT_SEED ^ client_seed)
.array();
//Kind of a hack, but whatever
cipher.init(Cipher.ENCRYPT_MODE, req.getKeyId() == 3 ? Crypto.CUR_OS_ENCRYPT_KEY : Crypto.CUR_CN_ENCRYPT_KEY);
var seed_encrypted = cipher.doFinal(seed_bytes);
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(Crypto.CUR_SIGNING_KEY);
privateSignature.update(seed_bytes);
session.send(new PacketGetPlayerTokenRsp(session, Utils.base64Encode(seed_encrypted), Utils.base64Encode(privateSignature.sign())));
} catch (Exception ignore) {
// Only UA Patch users will have exception
byte[] clientBytes = Utils.base64Decode(req.getClientSeed());
byte[] seed = ByteHelper.longToBytes(Crypto.ENCRYPT_SEED);
Crypto.xor(clientBytes, seed);
String base64str = Utils.base64Encode(clientBytes);
session.send(new PacketGetPlayerTokenRsp(session, base64str, "bm90aGluZyBoZXJl"));
}
}
else {
// Send packet
session.send(new PacketGetPlayerTokenRsp(session));
}
}
}

View File

@@ -6,15 +6,15 @@ import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketHomeUnknown2Rsp;
@Opcodes(PacketOpcodes.HomeUnknown2Req)
@Opcodes(PacketOpcodes.Unk2700_ACILPONNGGK_ClientReq)
public class HandlerHomeUnknown2Req extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
/*
* This packet is about the edit mode
*/
session.send(new PacketHomeUnknown2Rsp());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
/*
* This packet is about the edit mode
*/
session.send(new PacketHomeUnknown2Rsp());
}
}

View File

@@ -9,13 +9,13 @@ import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpRsp;
@Opcodes(PacketOpcodes.PlayerApplyEnterMpReq)
public class HandlerPlayerApplyEnterMpReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
PlayerApplyEnterMpReq req = PlayerApplyEnterMpReq.parseFrom(payload);
session.getServer().getMultiplayerManager().applyEnterMp(session.getPlayer(), req.getTargetUid());
session.send(new PacketPlayerApplyEnterMpRsp(req.getTargetUid()));
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
PlayerApplyEnterMpReq req = PlayerApplyEnterMpReq.parseFrom(payload);
session.getServer().getMultiplayerSystem().applyEnterMp(session.getPlayer(), req.getTargetUid());
session.send(new PacketPlayerApplyEnterMpRsp(req.getTargetUid()));
}
}

View File

@@ -9,13 +9,13 @@ import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpResultRsp;
@Opcodes(PacketOpcodes.PlayerApplyEnterMpResultReq)
public class HandlerPlayerApplyEnterMpResultReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
PlayerApplyEnterMpResultReq req = PlayerApplyEnterMpResultReq.parseFrom(payload);
session.getServer().getMultiplayerManager().applyEnterMpReply(session.getPlayer(), req.getApplyUid(), req.getIsAgreed());
session.send(new PacketPlayerApplyEnterMpResultRsp(req.getApplyUid(), req.getIsAgreed()));
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
PlayerApplyEnterMpResultReq req = PlayerApplyEnterMpResultReq.parseFrom(payload);
session.getServer().getMultiplayerSystem().applyEnterMpReply(session.getPlayer(), req.getApplyUid(), req.getIsAgreed());
session.send(new PacketPlayerApplyEnterMpResultRsp(req.getApplyUid(), req.getIsAgreed()));
}
}

View File

@@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.PlayerEnterDungeonReq)
public class HandlerPlayerEnterDungeonReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
// Auto template
PlayerEnterDungeonReq req = PlayerEnterDungeonReq.parseFrom(payload);
session.getServer().getDungeonManager().enterDungeon(session.getPlayer(), req.getPointId(), req.getDungeonId());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
// Auto template
PlayerEnterDungeonReq req = PlayerEnterDungeonReq.parseFrom(payload);
session.getServer().getDungeonSystem().enterDungeon(session.getPlayer(), req.getPointId(), req.getDungeonId());
}
}

View File

@@ -9,17 +9,17 @@ import emu.grasscutter.server.packet.send.PacketPlayerGetForceQuitBanInfoRsp;
@Opcodes(PacketOpcodes.PlayerGetForceQuitBanInfoReq)
public class HandlerPlayerGetForceQuitBanInfoReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
if (session.getServer().getMultiplayerManager().leaveCoop(session.getPlayer())) {
// Success
session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SUCC_VALUE));
} else {
// Fail
session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SVR_ERROR_VALUE));
}
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
if (session.getServer().getMultiplayerSystem().leaveCoop(session.getPlayer())) {
// Success
session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SUCC_VALUE));
} else {
// Fail
session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SVR_ERROR_VALUE));
}
}
}

View File

@@ -1,5 +1,7 @@
package emu.grasscutter.server.packet.recv;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
@@ -13,44 +15,42 @@ import emu.grasscutter.server.game.GameSession.SessionState;
import emu.grasscutter.server.packet.send.PacketPlayerLoginRsp;
import emu.grasscutter.server.packet.send.PacketTakeAchievementRewardReq;
import static emu.grasscutter.Configuration.ACCOUNT;
@Opcodes(PacketOpcodes.PlayerLoginReq) // Sends initial data packets
public class HandlerPlayerLoginReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
// Check
if (session.getAccount() == null) {
session.close();
return;
}
// Parse request
PlayerLoginReq req = PlayerLoginReq.parseFrom(payload);
// Authenticate session
if (!req.getToken().equals(session.getAccount().getToken())) {
session.close();
return;
}
// Load character from db
Player player = session.getPlayer();
// Show opening cutscene if player has no avatars
if (player.getAvatars().getAvatarCount() == 0) {
// Pick character
session.setState(SessionState.PICKING_CHARACTER);
session.send(new BasePacket(PacketOpcodes.DoSetPlayerBornDataNotify));
} else {
// Login done
session.getPlayer().onLogin();
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
// Check
if (session.getAccount() == null) {
session.close();
return;
}
// Final packet to tell client logging in is done
session.send(new PacketPlayerLoginRsp(session));
session.send(new PacketTakeAchievementRewardReq(session));
}
// Parse request
PlayerLoginReq req = PlayerLoginReq.parseFrom(payload);
// Authenticate session
if (!req.getToken().equals(session.getAccount().getToken())) {
session.close();
return;
}
// Load character from db
Player player = session.getPlayer();
// Show opening cutscene if player has no avatars
if (player.getAvatars().getAvatarCount() == 0) {
// Pick character
session.setState(SessionState.PICKING_CHARACTER);
session.send(new BasePacket(PacketOpcodes.DoSetPlayerBornDataNotify));
} else {
// Login done
session.getPlayer().onLogin();
}
// Final packet to tell client logging in is done
session.send(new PacketPlayerLoginRsp(session));
session.send(new PacketTakeAchievementRewardReq(session));
}
}

View File

@@ -7,10 +7,10 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.PlayerQuitDungeonReq)
public class HandlerPlayerQuitDungeonReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.getPlayer().getServer().getDungeonManager().exitDungeon(session.getPlayer());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.getPlayer().getServer().getDungeonSystem().exitDungeon(session.getPlayer());
}
}

View File

@@ -9,12 +9,14 @@ import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp;
@Opcodes(PacketOpcodes.PullPrivateChatReq)
public class HandlerPullPrivateChatReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
PullPrivateChatReq req = PullPrivateChatReq.parseFrom(payload);
session.send(new PacketPullPrivateChatRsp());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
PullPrivateChatReq req = PullPrivateChatReq.parseFrom(payload);
session.getServer().getChatManager().handlePullPrivateChatReq(session.getPlayer(), req.getTargetUid());
// session.send(new PacketPullPrivateChatRsp(req.getTargetUid()));
}
}

View File

@@ -8,8 +8,8 @@ import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp;
@Opcodes(PacketOpcodes.PullRecentChatReq)
public class HandlerPullRecentChatReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketPullRecentChatRsp(session.getPlayer()));
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.getServer().getChatManager().handlePullRecentChatReq(session.getPlayer());
}
}

View File

@@ -4,20 +4,24 @@ import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.QueryPathReqOuterClass;
import emu.grasscutter.net.proto.QueryPathReqOuterClass.QueryPathReq;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketQueryPathRsp;
@Opcodes(PacketOpcodes.QueryPathReq)
public class HandlerQueryPathReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = QueryPathReqOuterClass.QueryPathReq.parseFrom(payload);
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = QueryPathReq.parseFrom(payload);
/**
* It is not the actual work
*/
session.send(new PacketQueryPathRsp(req));
}
if (req.getDestinationPosList().size() > 0) {
session.send(new PacketQueryPathRsp(req));
}
}
}

View File

@@ -8,9 +8,9 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.ReliquaryDecomposeReq)
public class HandlerReliquaryDecomposeReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ReliquaryDecomposeReq req = ReliquaryDecomposeReq.parseFrom(payload);
session.getServer().getCombineManger().decomposeReliquaries(session.getPlayer(), req.getConfigId(), req.getTargetCount(), req.getGuidListList());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ReliquaryDecomposeReq req = ReliquaryDecomposeReq.parseFrom(payload);
session.getServer().getCombineSystem().decomposeReliquaries(session.getPlayer(), req.getConfigId(), req.getTargetCount(), req.getGuidListList());
}
}

View File

@@ -8,12 +8,12 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.ReliquaryUpgradeReq)
public class HandlerReliquaryUpgradeReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ReliquaryUpgradeReq req = ReliquaryUpgradeReq.parseFrom(payload);
session.getServer().getInventoryManager().upgradeRelic(session.getPlayer(), req.getTargetReliquaryGuid(), req.getFoodReliquaryGuidListList(), req.getItemParamListList());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ReliquaryUpgradeReq req = ReliquaryUpgradeReq.parseFrom(payload);
session.getServer().getInventorySystem().upgradeRelic(session.getPlayer(), req.getTargetReliquaryGuid(), req.getFoodReliquaryGuidListList(), req.getItemParamListList());
}
}

View File

@@ -9,18 +9,18 @@ import emu.grasscutter.server.packet.send.PacketSceneKickPlayerRsp;
@Opcodes(PacketOpcodes.SceneKickPlayerReq)
public class HandlerSceneKickPlayerReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SceneKickPlayerReq req = SceneKickPlayerReq.parseFrom(payload);
if (session.getServer().getMultiplayerManager().kickPlayer(session.getPlayer(), req.getTargetUid())) {
// Success
session.send(new PacketSceneKickPlayerRsp(req.getTargetUid()));
} else {
// Fail
session.send(new PacketSceneKickPlayerRsp());
}
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SceneKickPlayerReq req = SceneKickPlayerReq.parseFrom(payload);
if (session.getServer().getMultiplayerSystem().kickPlayer(session.getPlayer(), req.getTargetUid())) {
// Success
session.send(new PacketSceneKickPlayerRsp(req.getTargetUid()));
} else {
// Fail
session.send(new PacketSceneKickPlayerRsp());
}
}
}

View File

@@ -8,12 +8,12 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.SetEquipLockStateReq)
public class HandlerSetEquipLockStateReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SetEquipLockStateReq req = SetEquipLockStateReq.parseFrom(payload);
session.getServer().getInventoryManager().lockEquip(session.getPlayer(), req.getTargetEquipGuid(), req.getIsLocked());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SetEquipLockStateReq req = SetEquipLockStateReq.parseFrom(payload);
session.getServer().getInventorySystem().lockEquip(session.getPlayer(), req.getTargetEquipGuid(), req.getIsLocked());
}
}

View File

@@ -0,0 +1,26 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.props.OpenState;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SetOpenStateReqOuterClass.SetOpenStateReq;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketSetOpenStateRsp;
@Opcodes(PacketOpcodes.SetOpenStateReq)
public class HandlerSetOpenStateReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = SetOpenStateReq.parseFrom(payload);
int openState = req.getKey();
int value = req.getValue();
session.getPlayer().getOpenStateManager().setOpenState(OpenState.getTypeByValue(openState), value);
//Client Automatically Updates its OpenStateMap, no need to send OpenStateUpdateNotify
session.send(new PacketSetOpenStateRsp(openState,value));
}
}

View File

@@ -17,68 +17,68 @@ import emu.grasscutter.server.event.game.PlayerCreationEvent;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.game.GameSession.SessionState;
import java.util.Arrays;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.Configuration.*;
import java.util.Arrays;
@Opcodes(PacketOpcodes.SetPlayerBornDataReq)
public class HandlerSetPlayerBornDataReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SetPlayerBornDataReq req = SetPlayerBornDataReq.parseFrom(payload);
// Sanity checks
int avatarId = req.getAvatarId();
int startingSkillDepot;
if (avatarId == GameConstants.MAIN_CHARACTER_MALE) {
startingSkillDepot = 504;
} else if (avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
startingSkillDepot = 704;
} else {
return;
}
// Make sure resources folder is set
if (!GameData.getAvatarDataMap().containsKey(avatarId)) {
Grasscutter.getLogger().error("No avatar data found! Please check your ExcelBinOutput folder.");
session.close();
return;
}
// Get player object
Player player = session.getPlayer();
player.setNickname(req.getNickName());
// Create avatar
if (player.getAvatars().getAvatarCount() == 0) {
Avatar mainCharacter = new Avatar(avatarId);
mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot));
// Manually handle adding to team
player.addAvatar(mainCharacter, false);
player.setMainCharacterId(avatarId);
player.setHeadImage(avatarId);
player.getTeamManager().getCurrentSinglePlayerTeamInfo().getAvatars().add(mainCharacter.getAvatarId());
player.save(); // TODO save player team in different object
} else {
return;
}
// Login done
session.getPlayer().onLogin();
// Born resp packet
session.send(new BasePacket(PacketOpcodes.SetPlayerBornDataRsp));
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SetPlayerBornDataReq req = SetPlayerBornDataReq.parseFrom(payload);
// Default mail
var welcomeMail = GAME_INFO.joinOptions.welcomeMail;
MailBuilder mailBuilder = new MailBuilder(player.getUid(), new Mail());
mailBuilder.mail.mailContent.title = welcomeMail.title;
mailBuilder.mail.mailContent.sender = welcomeMail.sender;
// Please credit Grasscutter if changing something here. We don't condone commercial use of the project.
mailBuilder.mail.mailContent.content = welcomeMail.content + "\n<type=\"browser\" text=\"GitHub\" href=\"https://github.com/Melledy/Grasscutter\"/>";
mailBuilder.mail.itemList.addAll(Arrays.asList(welcomeMail.items));
mailBuilder.mail.importance = 1;
player.sendMail(mailBuilder.mail);
}
// Sanity checks
int avatarId = req.getAvatarId();
int startingSkillDepot;
if (avatarId == GameConstants.MAIN_CHARACTER_MALE) {
startingSkillDepot = 504;
} else if (avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
startingSkillDepot = 704;
} else {
return;
}
// Make sure resources folder is set
if (!GameData.getAvatarDataMap().containsKey(avatarId)) {
Grasscutter.getLogger().error("No avatar data found! Please check your ExcelBinOutput folder.");
session.close();
return;
}
// Get player object
Player player = session.getPlayer();
player.setNickname(req.getNickName());
// Create avatar
if (player.getAvatars().getAvatarCount() == 0) {
Avatar mainCharacter = new Avatar(avatarId);
mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot));
// Manually handle adding to team
player.addAvatar(mainCharacter, false);
player.setMainCharacterId(avatarId);
player.setHeadImage(avatarId);
player.getTeamManager().getCurrentSinglePlayerTeamInfo().getAvatars().add(mainCharacter.getAvatarId());
player.save(); // TODO save player team in different object
} else {
return;
}
// Login done
session.getPlayer().onLogin();
// Born resp packet
session.send(new BasePacket(PacketOpcodes.SetPlayerBornDataRsp));
// Default mail
var welcomeMail = GAME_INFO.joinOptions.welcomeMail;
MailBuilder mailBuilder = new MailBuilder(player.getUid(), new Mail());
mailBuilder.mail.mailContent.title = welcomeMail.title;
mailBuilder.mail.mailContent.sender = welcomeMail.sender;
// Please credit Grasscutter if changing something here. We don't condone commercial use of the project.
mailBuilder.mail.mailContent.content = welcomeMail.content + "\n<type=\"browser\" text=\"GitHub\" href=\"https://github.com/Melledy/Grasscutter\"/>";
mailBuilder.mail.itemList.addAll(Arrays.asList(welcomeMail.items));
mailBuilder.mail.importance = 1;
player.sendMail(mailBuilder.mail);
}
}

View File

@@ -8,13 +8,13 @@ import emu.grasscutter.server.packet.send.PacketTowerAllDataRsp;
@Opcodes(PacketOpcodes.TowerAllDataReq)
public class HandlerTowerAllDataReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketTowerAllDataRsp(
session.getServer().getTowerScheduleManager(),
session.getPlayer().getTowerManager()
));
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketTowerAllDataRsp(
session.getServer().getTowerSystem(),
session.getPlayer().getTowerManager()
));
}
}

View File

@@ -1,6 +1,8 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter;
import static emu.grasscutter.config.Configuration.GAME_INFO;
import static emu.grasscutter.config.Configuration.SERVER;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.UnionCmdNotifyOuterClass.UnionCmdNotify;
@@ -9,32 +11,30 @@ import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.Grasscutter.ServerDebugMode;
import static emu.grasscutter.Configuration.SERVER;
@Opcodes(PacketOpcodes.UnionCmdNotify)
public class HandlerUnionCmdNotify extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
UnionCmdNotify req = UnionCmdNotify.parseFrom(payload);
for (UnionCmd cmd : req.getCmdListList()) {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
UnionCmdNotify req = UnionCmdNotify.parseFrom(payload);
for (UnionCmd cmd : req.getCmdListList()) {
int cmdOpcode = cmd.getMessageId();
byte[] cmdPayload = cmd.getBody().toByteArray();
if(Grasscutter.config.server.debugLevel == ServerDebugMode.WHITELIST && SERVER.DebugWhitelist.contains(cmd.getMessageId())) {
if (GAME_INFO.logPackets == ServerDebugMode.WHITELIST && SERVER.debugWhitelist.contains(cmd.getMessageId())) {
session.logPacket("RECV in Union", cmdOpcode, cmdPayload);
} else if (Grasscutter.config.server.debugLevel == ServerDebugMode.BLACKLIST && !SERVER.DebugBlacklist.contains(cmd.getMessageId())) {
} else if (GAME_INFO.logPackets == ServerDebugMode.BLACKLIST && !SERVER.debugBlacklist.contains(cmd.getMessageId())) {
session.logPacket("RECV in Union", cmdOpcode, cmdPayload);
}
//debugLevel ALL ignores UnionCmdNotify, so we will also ignore the contained opcodes
session.getServer().getPacketHandler().handle(session, cmd.getMessageId(), EMPTY_BYTE_ARRAY, cmd.getBody().toByteArray());
}
}
// Update
session.getPlayer().getCombatInvokeHandler().update(session.getPlayer());
session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer());
// Update
session.getPlayer().getCombatInvokeHandler().update(session.getPlayer());
session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer());
// Handle attack results last
while (!session.getPlayer().getAttackResults().isEmpty()) {
session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll());
}
}
while (!session.getPlayer().getAttackResults().isEmpty()) {
session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll());
}
}
}

View File

@@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.UnlockAvatarTalentReq)
public class HandlerUnlockAvatarTalentReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
UnlockAvatarTalentReq req = UnlockAvatarTalentReq.parseFrom(payload);
// Unlock avatar const
session.getServer().getInventoryManager().unlockAvatarConstellation(session.getPlayer(), req.getAvatarGuid());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
UnlockAvatarTalentReq req = UnlockAvatarTalentReq.parseFrom(payload);
// Unlock avatar const
session.getServer().getInventorySystem().unlockAvatarConstellation(session.getPlayer(), req.getAvatarGuid());
}
}

View File

@@ -10,17 +10,17 @@ import emu.grasscutter.server.packet.send.PacketUseItemRsp;
@Opcodes(PacketOpcodes.UseItemReq)
public class HandlerUseItemReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
UseItemReq req = UseItemReq.parseFrom(payload);
GameItem useItem = session.getServer().getInventoryManager().useItem(session.getPlayer(), req.getTargetGuid(), req.getGuid(), req.getCount(), req.getOptionIdx());
if (useItem != null) {
session.send(new PacketUseItemRsp(req.getTargetGuid(), useItem));
} else {
session.send(new PacketUseItemRsp());
}
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
UseItemReq req = UseItemReq.parseFrom(payload);
GameItem useItem = session.getServer().getInventorySystem().useItem(session.getPlayer(), req.getTargetGuid(), req.getGuid(), req.getCount(), req.getOptionIdx());
if (useItem != null) {
session.send(new PacketUseItemRsp(req.getTargetGuid(), useItem));
} else {
session.send(new PacketUseItemRsp());
}
}
}

View File

@@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.WeaponAwakenReq)
public class HandlerWeaponAwakenReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
WeaponAwakenReq req = WeaponAwakenReq.parseFrom(payload);
// Weapon refinement
session.getServer().getInventoryManager().refineWeapon(session.getPlayer(), req.getTargetWeaponGuid(), req.getItemGuid());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
WeaponAwakenReq req = WeaponAwakenReq.parseFrom(payload);
// Weapon refinement
session.getServer().getInventorySystem().refineWeapon(session.getPlayer(), req.getTargetWeaponGuid(), req.getItemGuid());
}
}

View File

@@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.WeaponPromoteReq)
public class HandlerWeaponPromoteReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
WeaponPromoteReq req = WeaponPromoteReq.parseFrom(payload);
// Ascend weapon
session.getServer().getInventoryManager().promoteWeapon(session.getPlayer(), req.getTargetWeaponGuid());
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
WeaponPromoteReq req = WeaponPromoteReq.parseFrom(payload);
// Ascend weapon
session.getServer().getInventorySystem().promoteWeapon(session.getPlayer(), req.getTargetWeaponGuid());
}
}

View File

@@ -8,18 +8,18 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.WeaponUpgradeReq)
public class HandlerWeaponUpgradeReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
WeaponUpgradeReq req = WeaponUpgradeReq.parseFrom(payload);
// Level up weapon
session.getServer().getInventoryManager().upgradeWeapon(
session.getPlayer(),
req.getTargetWeaponGuid(),
req.getFoodWeaponGuidListList(),
req.getItemParamListList()
);
}
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
WeaponUpgradeReq req = WeaponUpgradeReq.parseFrom(payload);
// Level up weapon
session.getServer().getInventorySystem().upgradeWeapon(
session.getPlayer(),
req.getTargetWeaponGuid(),
req.getFoodWeaponGuidListList(),
req.getItemParamListList()
);
}
}

View File

@@ -9,7 +9,6 @@ import emu.grasscutter.net.proto.WidgetSlotDataOuterClass;
import emu.grasscutter.net.proto.WidgetSlotTagOuterClass;
import java.util.List;
import java.util.Map;
public class PacketAllWidgetDataNotify extends BasePacket {
@@ -19,35 +18,35 @@ public class PacketAllWidgetDataNotify extends BasePacket {
// TODO: Implement this
AllWidgetDataNotify.Builder proto = AllWidgetDataNotify.newBuilder()
// If you want to implement this, feel free to do so. :)
.setLunchBoxData(
LunchBoxDataOuterClass.LunchBoxData.newBuilder().build()
)
// Maybe it's a little difficult, or it makes you upset :(
.addAllOneoffGatherPointDetectorDataList(List.of())
// So, goodbye, and hopefully sometime in the future o(* ̄▽ ̄*)ブ
.addAllCoolDownGroupDataList(List.of())
// I'll see your PR with a title that says (・∀・(・∀・(・∀・*)
.addAllAnchorPointList(List.of())
// "Complete implementation of widget functionality" b ̄▽ ̄d 
.addAllClientCollectorDataList(List.of())
// Good luck, my boy.
.addAllNormalCoolDownDataList(List.of());
// If you want to implement this, feel free to do so. :)
.setLunchBoxData(
LunchBoxDataOuterClass.LunchBoxData.newBuilder().build()
)
// Maybe it's a little difficult, or it makes you upset :(
.addAllOneofGatherPointDetectorDataList(List.of())
// So, goodbye, and hopefully sometime in the future o(* ̄▽ ̄*)ブ
.addAllCoolDownGroupDataList(List.of())
// I'll see your PR with a title that says (・∀・(・∀・(・∀・*)
.addAllAnchorPointList(List.of())
// "Complete implementation of widget functionality" b ̄▽ ̄d 
.addAllClientCollectorDataList(List.of())
// Good luck, my boy.
.addAllNormalCoolDownDataList(List.of());
if (player.getWidgetId() == null) {
proto.addAllSlotList(List.of());
} else {
proto.addSlotList(
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
.setIsActive(true)
.setMaterialId(player.getWidgetId())
.build()
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
.setIsActive(true)
.setMaterialId(player.getWidgetId())
.build()
);
proto.addSlotList(
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
.setTag(WidgetSlotTagOuterClass.WidgetSlotTag.WIDGET_SLOT_TAG_ATTACH_AVATAR)
.build()
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
.setTag(WidgetSlotTagOuterClass.WidgetSlotTag.WIDGET_SLOT_TAG_ATTACH_AVATAR)
.build()
);
}

View File

@@ -13,11 +13,11 @@ import emu.grasscutter.net.proto.AvatarTeamOuterClass.AvatarTeam;
public class PacketAvatarDataNotify extends BasePacket {
public PacketAvatarDataNotify(Player player) {
super(PacketOpcodes.AvatarDataNotify, 2);
super(PacketOpcodes.AvatarDataNotify, true);
AvatarDataNotify.Builder proto = AvatarDataNotify.newBuilder()
.setCurAvatarTeamId(player.getTeamManager().getCurrentTeamId())
.setChooseAvatarGuid(player.getTeamManager().getCurrentCharacterGuid())
//.setChooseAvatarGuid(player.getTeamManager().getCurrentCharacterGuid())
.addAllOwnedFlycloakList(player.getFlyCloakList())
.addAllOwnedCostumeList(player.getCostumeList());
@@ -38,6 +38,12 @@ public class PacketAvatarDataNotify extends BasePacket {
proto.putAvatarTeamMap(entry.getKey(), avatarTeam.build());
}
// Set main character
Avatar mainCharacter = player.getAvatars().getAvatarById(player.getMainCharacterId());
if (mainCharacter != null) {
proto.setChooseAvatarGuid(mainCharacter.getGuid());
}
this.setData(proto.build());
}

View File

@@ -24,17 +24,17 @@ public class PacketGetAllMailRsp extends BasePacket {
GetAllMailRsp.Builder proto = GetAllMailRsp.newBuilder();
if (isGiftMail) {
proto.setANKKGPJCINB(true);
proto.setUnk2700OPEHLDAGICF(true);
} else {
proto.setANKKGPJCINB(false);
proto.setUnk2700OPEHLDAGICF(false);
if (player.getAllMail().size() != 0) { // Make sure the player has mail
List<MailData> mailDataList = new ArrayList<MailData>();
for (Mail message : player.getAllMail()) {
if(message.stateValue == 1) { // Make sure it isn't a gift
if (message.stateValue == 1) { // Make sure it isn't a gift
if (message.expireTime > (int) Instant.now().getEpochSecond()) { // Make sure the message isn't expired (The game won't show expired mail, but I don't want to send unnecessary information).
if(mailDataList.size() <= 1000) { // Make sure that there isn't over 1000 messages in the mailbox. (idk what will happen if there is but the game probably won't like it.)
if (mailDataList.size() <= 1000) { // Make sure that there isn't over 1000 messages in the mailbox. (idk what will happen if there is but the game probably won't like it.)
MailTextContent.Builder mailTextContent = MailTextContent.newBuilder();
mailTextContent.setTitle(message.mailContent.title);
mailTextContent.setContent(message.mailContent.content);
@@ -61,7 +61,7 @@ public class PacketGetAllMailRsp extends BasePacket {
mailData.setImportance(message.importance);
mailData.setIsRead(message.isRead);
mailData.setIsAttachmentGot(message.isAttachmentGot);
mailData.setBHCAHLJIKFFValue(1);
mailData.setUnk2700NDPPGJKJOMHValue(1);
mailDataList.add(mailData.build());
}

View File

@@ -1,16 +1,16 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.gacha.GachaSystem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
public class PacketGetGachaInfoRsp extends BasePacket {
public PacketGetGachaInfoRsp(GachaManager manager, Player player) {
super(PacketOpcodes.GetGachaInfoRsp);
this.setData(manager.toProto(player));
}
public PacketGetGachaInfoRsp(GachaSystem manager, Player player) {
super(PacketOpcodes.GetGachaInfoRsp);
this.setData(manager.toProto(player));
}
}

View File

@@ -1,7 +1,7 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.WorldDataManager;
import emu.grasscutter.game.world.WorldDataSystem;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.GetActivityInfoRspOuterClass;
@@ -11,15 +11,15 @@ import java.util.List;
public class PacketGetInvestigationMonsterRsp extends BasePacket {
public PacketGetInvestigationMonsterRsp(Player player, WorldDataManager worldDataManager, List<Integer> cityIdListList) {
public PacketGetInvestigationMonsterRsp(Player player, WorldDataSystem worldDataManager, List<Integer> cityIdListList) {
super(PacketOpcodes.GetInvestigationMonsterRsp);
super(PacketOpcodes.GetInvestigationMonsterRsp);
var resp = GetInvestigationMonsterRspOuterClass.GetInvestigationMonsterRsp.newBuilder();
var resp = GetInvestigationMonsterRspOuterClass.GetInvestigationMonsterRsp.newBuilder();
cityIdListList.forEach(id -> resp.addAllMonsterList(worldDataManager.getInvestigationMonstersByCityId(player, id)));
cityIdListList.forEach(id -> resp.addAllMonsterList(worldDataManager.getInvestigationMonstersByCityId(player, id)));
this.setData(resp.build());
}
this.setData(resp.build());
}
}

View File

@@ -1,5 +1,7 @@
package emu.grasscutter.server.packet.send;
import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.GameConstants;
import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.player.Player;
@@ -11,35 +13,33 @@ import emu.grasscutter.net.proto.GetPlayerFriendListRspOuterClass.GetPlayerFrien
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
import emu.grasscutter.net.proto.PlatformTypeOuterClass;
import static emu.grasscutter.Configuration.*;
public class PacketGetPlayerFriendListRsp extends BasePacket {
public PacketGetPlayerFriendListRsp(Player player) {
super(PacketOpcodes.GetPlayerFriendListRsp);
var serverAccount = GAME_INFO.serverAccount;
FriendBrief serverFriend = FriendBrief.newBuilder()
.setUid(GameConstants.SERVER_CONSOLE_UID)
.setNickname(serverAccount.nickName)
.setLevel(serverAccount.adventureRank)
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(serverAccount.avatarId))
.setWorldLevel(serverAccount.worldLevel)
.setSignature(serverAccount.signature)
.setLastActiveTime((int) (System.currentTimeMillis() / 1000f))
.setNameCardId(serverAccount.nameCardId)
.setOnlineState(FriendOnlineState.FRIEND_ONLINE_STATE_ONLINE)
.setParam(1)
.setIsGameSource(true)
.setPlatformType(PlatformTypeOuterClass.PlatformType.PLATFORM_TYPE_PC)
.build();
GetPlayerFriendListRsp.Builder proto = GetPlayerFriendListRsp.newBuilder().addFriendList(serverFriend);
for (Friendship friendship : player.getFriendsList().getFriends().values()) {
proto.addFriendList(friendship.toProto());
}
this.setData(proto);
}
public PacketGetPlayerFriendListRsp(Player player) {
super(PacketOpcodes.GetPlayerFriendListRsp);
var serverAccount = GAME_INFO.serverAccount;
FriendBrief serverFriend = FriendBrief.newBuilder()
.setUid(GameConstants.SERVER_CONSOLE_UID)
.setNickname(serverAccount.nickName)
.setLevel(serverAccount.adventureRank)
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(serverAccount.avatarId))
.setWorldLevel(serverAccount.worldLevel)
.setSignature(serverAccount.signature)
.setLastActiveTime((int) (System.currentTimeMillis() / 1000f))
.setNameCardId(serverAccount.nameCardId)
.setOnlineState(FriendOnlineState.FRIEND_ONLINE_STATE_ONLINE)
.setParam(1)
.setIsGameSource(true)
.setPlatformType(PlatformTypeOuterClass.PlatformType.PLATFORM_TYPE_PC)
.build();
GetPlayerFriendListRsp.Builder proto = GetPlayerFriendListRsp.newBuilder().addFriendList(serverFriend);
for (Friendship friendship : player.getFriendsList().getFriends().values()) {
proto.addFriendList(friendship.toProto());
}
this.setData(proto);
}
}

View File

@@ -10,45 +10,70 @@ import emu.grasscutter.utils.Crypto;
public class PacketGetPlayerTokenRsp extends BasePacket {
public PacketGetPlayerTokenRsp(GameSession session) {
super(PacketOpcodes.GetPlayerTokenRsp, true);
this.setUseDispatchKey(true);
public PacketGetPlayerTokenRsp(GameSession session) {
super(PacketOpcodes.GetPlayerTokenRsp, true);
GetPlayerTokenRsp p = GetPlayerTokenRsp.newBuilder()
.setUid(session.getPlayer().getUid())
.setToken(session.getAccount().getToken())
.setAccountType(1)
.setIsProficientPlayer(session.getPlayer().getAvatars().getAvatarCount() > 0) // Not sure where this goes
.setSecretKeySeed(Crypto.ENCRYPT_SEED)
.setSecurityCmdBuffer(ByteString.copyFrom(Crypto.ENCRYPT_SEED_BUFFER))
.setPlatformType(3)
.setChannelId(1)
.setCountryCode("US")
.setClientVersionRandomKey("c25-314dd05b0b5f")
.setRegPlatform(3)
.setClientIpStr(session.getAddress().getAddress().getHostAddress())
.build();
this.setData(p.toByteArray());
}
public PacketGetPlayerTokenRsp(GameSession session, int retcode, String msg, int blackEndTime) {
super(PacketOpcodes.GetPlayerTokenRsp, true);
this.setUseDispatchKey(true);
GetPlayerTokenRsp p = GetPlayerTokenRsp.newBuilder()
.setUid(session.getPlayer().getUid())
.setIsProficientPlayer(session.getPlayer().getAvatars().getAvatarCount() > 0)
.setRetcode(retcode)
.setMsg(msg)
.setBlackUidEndTime(blackEndTime)
.setRegPlatform(3)
.setCountryCode("US")
.setClientIpStr(session.getAddress().getAddress().getHostAddress())
.build();
this.setData(p.toByteArray());
}
this.setUseDispatchKey(true);
GetPlayerTokenRsp p = GetPlayerTokenRsp.newBuilder()
.setUid(session.getPlayer().getUid())
.setToken(session.getAccount().getToken())
.setAccountType(1)
.setIsProficientPlayer(session.getPlayer().getAvatars().getAvatarCount() > 0) // Not sure where this goes
.setSecretKeySeed(Crypto.ENCRYPT_SEED)
.setSecurityCmdBuffer(ByteString.copyFrom(Crypto.ENCRYPT_SEED_BUFFER))
.setPlatformType(3)
.setChannelId(1)
.setCountryCode("US")
.setClientVersionRandomKey("c25-314dd05b0b5f")
.setRegPlatform(3)
.setClientIpStr(session.getAddress().getAddress().getHostAddress())
.build();
this.setData(p.toByteArray());
}
public PacketGetPlayerTokenRsp(GameSession session, int retcode, String msg, int blackEndTime) {
super(PacketOpcodes.GetPlayerTokenRsp, true);
this.setUseDispatchKey(true);
GetPlayerTokenRsp p = GetPlayerTokenRsp.newBuilder()
.setUid(session.getPlayer().getUid())
.setIsProficientPlayer(session.getPlayer().getAvatars().getAvatarCount() > 0)
.setRetcode(retcode)
.setMsg(msg)
.setBlackUidEndTime(blackEndTime)
.setRegPlatform(3)
.setCountryCode("US")
.setClientIpStr(session.getAddress().getAddress().getHostAddress())
.build();
this.setData(p.toByteArray());
}
public PacketGetPlayerTokenRsp(GameSession session, String encryptedSeed, String encryptedSeedSign) {
super(PacketOpcodes.GetPlayerTokenRsp, true);
this.setUseDispatchKey(true);
GetPlayerTokenRsp p = GetPlayerTokenRsp.newBuilder()
.setUid(session.getPlayer().getUid())
.setToken(session.getAccount().getToken())
.setAccountType(1)
.setIsProficientPlayer(session.getPlayer().getAvatars().getAvatarCount() > 0) // Not sure where this goes
.setSecretKeySeed(Crypto.ENCRYPT_SEED)
.setSecurityCmdBuffer(ByteString.copyFrom(Crypto.ENCRYPT_SEED_BUFFER))
.setPlatformType(3)
.setChannelId(1)
.setCountryCode("US")
.setClientVersionRandomKey("c25-314dd05b0b5f")
.setRegPlatform(3)
.setClientIpStr(session.getAddress().getAddress().getHostAddress())
.setEncryptedSeed(encryptedSeed)
.setSeedSignature(encryptedSeedSign)
.build();
this.setData(p.toByteArray());
}
}

View File

@@ -9,20 +9,20 @@ import emu.grasscutter.net.proto.CityInfoOuterClass.CityInfo;
import emu.grasscutter.net.proto.GetSceneAreaRspOuterClass.GetSceneAreaRsp;
public class PacketGetSceneAreaRsp extends BasePacket {
public PacketGetSceneAreaRsp(int sceneId) {
super(PacketOpcodes.GetSceneAreaRsp);
this.buildHeader(0);
GetSceneAreaRsp p = GetSceneAreaRsp.newBuilder()
.setSceneId(sceneId)
.addAllAreaIdList(Arrays.stream(new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19,100,101,102,103,200,210,300}).boxed().collect(Collectors.toList()))
.addCityInfoList(CityInfo.newBuilder().setCityId(1).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build())
.build();
this.setData(p);
}
public PacketGetSceneAreaRsp(int sceneId) {
super(PacketOpcodes.GetSceneAreaRsp);
this.buildHeader(0);
GetSceneAreaRsp p = GetSceneAreaRsp.newBuilder()
.setSceneId(sceneId)
.addAllAreaIdList(Arrays.stream(new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19,100,101,102,103,200,210,300,400,401,402,403}).boxed().collect(Collectors.toList()))
.addCityInfoList(CityInfo.newBuilder().setCityId(1).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build())
.build();
this.setData(p);
}
}

View File

@@ -4,7 +4,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.shop.ShopInfo;
import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.shop.ShopManager;
import emu.grasscutter.game.shop.ShopSystem;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.GetShopRspOuterClass;
@@ -18,61 +18,61 @@ import java.util.List;
import java.util.stream.Collectors;
public class PacketGetShopRsp extends BasePacket {
public PacketGetShopRsp(Player inv, int shopType) {
super(PacketOpcodes.GetShopRsp);
public PacketGetShopRsp(Player inv, int shopType) {
super(PacketOpcodes.GetShopRsp);
// TODO: CityReputationLevel
Shop.Builder shop = Shop.newBuilder()
.setShopType(shopType)
.setCityId(1) //mock
.setCityReputationLevel(10); //mock
// TODO: CityReputationLevel
Shop.Builder shop = Shop.newBuilder()
.setShopType(shopType)
.setCityId(1) //mock
.setCityReputationLevel(10); //mock
ShopManager manager = Grasscutter.getGameServer().getShopManager();
if (manager.getShopData().get(shopType) != null) {
List<ShopInfo> list = manager.getShopData().get(shopType);
List<ShopGoods> goodsList = new ArrayList<>();
for (ShopInfo info : list) {
ShopGoods.Builder goods = ShopGoods.newBuilder()
.setGoodsId(info.getGoodsId())
.setGoodsItem(ItemParamOuterClass.ItemParam.newBuilder().setItemId(info.getGoodsItem().getId()).setCount(info.getGoodsItem().getCount()).build())
.setScoin(info.getScoin())
.setHcoin(info.getHcoin())
.setBuyLimit(info.getBuyLimit())
.setBeginTime(info.getBeginTime())
.setEndTime(info.getEndTime())
.setMinLevel(info.getMinLevel())
.setMaxLevel(info.getMaxLevel())
.setMcoin(info.getMcoin())
.setDisableType(info.getDisableType())
.setSecondarySheetId(info.getSecondarySheetId());
if (info.getCostItemList() != null) {
goods.addAllCostItemList(info.getCostItemList().stream().map(x -> ItemParamOuterClass.ItemParam.newBuilder().setItemId(x.getId()).setCount(x.getCount()).build()).collect(Collectors.toList()));
}
if (info.getPreGoodsIdList() != null) {
goods.addAllPreGoodsIdList(info.getPreGoodsIdList());
}
ShopSystem manager = Grasscutter.getGameServer().getShopSystem();
if (manager.getShopData().get(shopType) != null) {
List<ShopInfo> list = manager.getShopData().get(shopType);
List<ShopGoods> goodsList = new ArrayList<>();
for (ShopInfo info : list) {
ShopGoods.Builder goods = ShopGoods.newBuilder()
.setGoodsId(info.getGoodsId())
.setGoodsItem(ItemParamOuterClass.ItemParam.newBuilder().setItemId(info.getGoodsItem().getId()).setCount(info.getGoodsItem().getCount()).build())
.setScoin(info.getScoin())
.setHcoin(info.getHcoin())
.setBuyLimit(info.getBuyLimit())
.setBeginTime(info.getBeginTime())
.setEndTime(info.getEndTime())
.setMinLevel(info.getMinLevel())
.setMaxLevel(info.getMaxLevel())
.setMcoin(info.getMcoin())
.setDisableType(info.getDisableType())
.setSecondarySheetId(info.getSecondarySheetId());
if (info.getCostItemList() != null) {
goods.addAllCostItemList(info.getCostItemList().stream().map(x -> ItemParamOuterClass.ItemParam.newBuilder().setItemId(x.getId()).setCount(x.getCount()).build()).collect(Collectors.toList()));
}
if (info.getPreGoodsIdList() != null) {
goods.addAllPreGoodsIdList(info.getPreGoodsIdList());
}
int currentTs = Utils.getCurrentSeconds();
ShopLimit currentShopLimit = inv.getGoodsLimit(info.getGoodsId());
int nextRefreshTime = ShopManager.getShopNextRefreshTime(info);
if (currentShopLimit != null) {
if (currentShopLimit.getNextRefreshTime() < currentTs) { // second game day
currentShopLimit.setHasBoughtInPeriod(0);
currentShopLimit.setNextRefreshTime(nextRefreshTime);
}
goods.setBoughtNum(currentShopLimit.getHasBoughtInPeriod());
goods.setNextRefreshTime(currentShopLimit.getNextRefreshTime());
} else {
inv.addShopLimit(goods.getGoodsId(), 0, nextRefreshTime); // save generated refresh time
goods.setNextRefreshTime(nextRefreshTime);
}
int currentTs = Utils.getCurrentSeconds();
ShopLimit currentShopLimit = inv.getGoodsLimit(info.getGoodsId());
int nextRefreshTime = ShopSystem.getShopNextRefreshTime(info);
if (currentShopLimit != null) {
if (currentShopLimit.getNextRefreshTime() < currentTs) { // second game day
currentShopLimit.setHasBoughtInPeriod(0);
currentShopLimit.setNextRefreshTime(nextRefreshTime);
}
goods.setBoughtNum(currentShopLimit.getHasBoughtInPeriod());
goods.setNextRefreshTime(currentShopLimit.getNextRefreshTime());
} else {
inv.addShopLimit(goods.getGoodsId(), 0, nextRefreshTime); // save generated refresh time
goods.setNextRefreshTime(nextRefreshTime);
}
goodsList.add(goods.build());
}
shop.addAllGoodsList(goodsList);
}
goodsList.add(goods.build());
}
shop.addAllGoodsList(goodsList);
}
inv.save();
this.setData(GetShopRspOuterClass.GetShopRsp.newBuilder().setShop(shop).build());
}
inv.save();
this.setData(GetShopRspOuterClass.GetShopRsp.newBuilder().setShop(shop).build());
}
}

View File

@@ -10,28 +10,28 @@ import emu.grasscutter.net.proto.VectorOuterClass;
public class PacketHomeBasicInfoNotify extends BasePacket {
public PacketHomeBasicInfoNotify(Player player, boolean editMode) {
super(PacketOpcodes.HomeBasicInfoNotify);
public PacketHomeBasicInfoNotify(Player player, boolean editMode) {
super(PacketOpcodes.HomeBasicInfoNotify);
if(player.getCurrentRealmId() == null){
return;
}
if (player.getCurrentRealmId() <= 0) {
return;
}
var proto = HomeBasicInfoNotifyOuterClass.HomeBasicInfoNotify.newBuilder();
var proto = HomeBasicInfoNotifyOuterClass.HomeBasicInfoNotify.newBuilder();
var sceneId = player.getCurrentRealmId() + 2000;
var homeScene = player.getHome().getHomeSceneItem(sceneId);
var sceneId = player.getCurrentRealmId() + 2000;
var homeScene = player.getHome().getHomeSceneItem(sceneId);
proto.setBasicInfo(HomeBasicInfoOuterClass.HomeBasicInfo.newBuilder()
.setCurModuleId(player.getCurrentRealmId())
.setCurRoomSceneId(homeScene.getRoomSceneId())
.setIsInEditMode(editMode)
.setHomeOwnerUid(player.getUid())
.setLevel(player.getHome().getLevel())
.setOwnerNickName(player.getNickname())
// TODO limit shop
.build());
proto.setBasicInfo(HomeBasicInfoOuterClass.HomeBasicInfo.newBuilder()
.setCurModuleId(player.getCurrentRealmId())
.setCurRoomSceneId(homeScene.getRoomSceneId())
.setIsInEditMode(editMode)
.setHomeOwnerUid(player.getUid())
.setLevel(player.getHome().getLevel())
.setOwnerNickName(player.getNickname())
// TODO limit shop
.build());
this.setData(proto);
}
this.setData(proto);
}
}

View File

@@ -6,13 +6,13 @@ import emu.grasscutter.net.proto.HomeUnknown1NotifyOuterClass;
public class PacketHomeUnknown1Notify extends BasePacket {
public PacketHomeUnknown1Notify(boolean isEnterEditMode) {
super(PacketOpcodes.HomeUnknown1Notify);
public PacketHomeUnknown1Notify(boolean isEnterEditMode) {
super(PacketOpcodes.Unk2700_JDMPECKFGIG_ServerNotify);
var proto = HomeUnknown1NotifyOuterClass.HomeUnknown1Notify.newBuilder();
var proto = HomeUnknown1NotifyOuterClass.HomeUnknown1Notify.newBuilder();
proto.setIsEnterEditMode(isEnterEditMode);
proto.setIsEnterEditMode(isEnterEditMode);
this.setData(proto);
}
this.setData(proto);
}
}

View File

@@ -5,8 +5,8 @@ import emu.grasscutter.net.packet.PacketOpcodes;
public class PacketHomeUnknown2Rsp extends BasePacket {
public PacketHomeUnknown2Rsp() {
super(PacketOpcodes.HomeUnknown2Rsp);
public PacketHomeUnknown2Rsp() {
super(PacketOpcodes.Unk2700_KIIOGMKFNNP_ServerRsp);
}
}
}

View File

@@ -14,7 +14,7 @@ import java.util.List;
public class PacketMailChangeNotify extends BasePacket {
public PacketMailChangeNotify(Player player, Mail message) {
this (player, new ArrayList<Mail>(){{add(message);}});
this (player, new ArrayList<Mail>() {{add(message);}});
}
public PacketMailChangeNotify(Player player, List<Mail> mailList) {
@@ -54,16 +54,16 @@ public class PacketMailChangeNotify extends BasePacket {
mailData.setImportance(message.importance);
mailData.setIsRead(message.isRead);
mailData.setIsAttachmentGot(message.isAttachmentGot);
mailData.setBHCAHLJIKFFValue(message.stateValue);
mailData.setUnk2700NDPPGJKJOMHValue(message.stateValue);
proto.addMailList(mailData.build());
}
}
if(delMailIdList != null) {
if (delMailIdList != null) {
proto.addAllDelMailIdList(delMailIdList);
}
this.setData(proto.build());
}
}
}

View File

@@ -10,7 +10,7 @@ import java.util.*;
public class PacketMarkMapRsp extends BasePacket {
public PacketMarkMapRsp(HashMap<String, MapMark> mapMarks) {
public PacketMarkMapRsp(Map<String, MapMark> mapMarks) {
super(PacketOpcodes.MarkMapRsp);
MarkMapRspOuterClass.MarkMapRsp.Builder proto = MarkMapRspOuterClass.MarkMapRsp.newBuilder();
@@ -40,4 +40,4 @@ public class PacketMarkMapRsp extends BasePacket {
MarkMapRspOuterClass.MarkMapRsp data = proto.build();
this.setData(data);
}
}
}

View File

@@ -0,0 +1,32 @@
package emu.grasscutter.server.packet.send;
import java.util.Map;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.OpenStateChangeNotifyOuterClass.OpenStateChangeNotify;
//Sets openState to value
public class PacketOpenStateChangeNotify extends BasePacket {
public PacketOpenStateChangeNotify(int openState, int value) {
super(PacketOpcodes.OpenStateChangeNotify);
OpenStateChangeNotify proto = OpenStateChangeNotify.newBuilder()
.putOpenStateMap(openState, value)
.build();
this.setData(proto);
}
public PacketOpenStateChangeNotify(Map<Integer, Integer> map) {
super(PacketOpcodes.OpenStateChangeNotify);
OpenStateChangeNotify proto = OpenStateChangeNotify.newBuilder()
.putAllOpenStateMap(map)
.build();
this.setData(proto);
}
}

View File

@@ -1,23 +1,32 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.PlayerOpenStateManager;
import emu.grasscutter.game.props.OpenState;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.OpenStateUpdateNotifyOuterClass.OpenStateUpdateNotify;
/*
Must be sent on login for openStates to work
Tells the client to update its openStateMap for the keys sent. value is irrelevant
*/
public class PacketOpenStateUpdateNotify extends BasePacket {
public PacketOpenStateUpdateNotify() {
super(PacketOpcodes.OpenStateUpdateNotify);
OpenStateUpdateNotify.Builder proto = OpenStateUpdateNotify.newBuilder();
for (OpenState type : OpenState.values()) {
if (type.getValue() > 0) {
proto.putOpenStateMap(type.getValue(), 1);
}
}
this.setData(proto);
}
public PacketOpenStateUpdateNotify(PlayerOpenStateManager manager) {
super(PacketOpcodes.OpenStateUpdateNotify);
OpenStateUpdateNotify.Builder proto = OpenStateUpdateNotify.newBuilder();
for (OpenState state : OpenState.values()) {
// If the player has an open state stored in their map, then it would always override any default value
if (manager.getOpenStateMap().containsKey(state.getValue())) {
proto.putOpenStateMap(state.getValue(), manager.getOpenState(state));
} else if (PlayerOpenStateManager.DEV_OPEN_STATES.contains(state)) {
// Add default value here. TODO properly put default values somewhere
proto.putOpenStateMap(state.getValue(), 1);
}
}
this.setData(proto);
}
}

View File

@@ -11,61 +11,60 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
public class PacketPlayerEnterSceneNotify extends BasePacket {
// Login
public PacketPlayerEnterSceneNotify(Player player) {
super(PacketOpcodes.PlayerEnterSceneNotify);
player.setSceneLoadState(SceneLoadState.LOADING);
player.setEnterSceneToken(Utils.randomRange(1000, 99999));
PlayerEnterSceneNotify proto = PlayerEnterSceneNotify.newBuilder()
.setSceneId(player.getSceneId())
.setPos(player.getPos().toProto())
.setSceneBeginTime(System.currentTimeMillis())
.setType(EnterType.ENTER_TYPE_SELF)
.setTargetUid(player.getUid())
.setEnterSceneToken(player.getEnterSceneToken())
.setWorldLevel(player.getWorldLevel())
.setEnterReason(EnterReason.Login.getValue())
.setIsFirstLoginEnterScene(player.isFirstLoginEnterScene())
.setWorldType(1)
.setSceneTransaction("3-" + player.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402)
.build();
this.setData(proto);
}
public PacketPlayerEnterSceneNotify(Player player, EnterType type, EnterReason reason, int newScene, Position newPos) {
this(player, player, type, reason, newScene, newPos);
}
// Teleport or go somewhere
public PacketPlayerEnterSceneNotify(Player player, Player target, EnterType type, EnterReason reason, int newScene, Position newPos) {
super(PacketOpcodes.PlayerEnterSceneNotify);
player.setEnterSceneToken(Utils.randomRange(1000, 99999));
// Login
public PacketPlayerEnterSceneNotify(Player player) {
super(PacketOpcodes.PlayerEnterSceneNotify);
PlayerEnterSceneNotify proto = PlayerEnterSceneNotify.newBuilder()
.setPrevSceneId(player.getSceneId())
.setPrevPos(player.getPos().toProto())
.setSceneId(newScene)
.setPos(newPos.toProto())
.setSceneBeginTime(System.currentTimeMillis())
.setType(type)
.setTargetUid(target.getUid())
.setEnterSceneToken(player.getEnterSceneToken())
.setWorldLevel(target.getWorld().getWorldLevel())
.setEnterReason(reason.getValue())
.addSceneTagIdList(102)
.addSceneTagIdList(107)
.addSceneTagIdList(109)
.addSceneTagIdList(113)
.addSceneTagIdList(117)
.setWorldType(1)
.setSceneTransaction(newScene + "-" + target.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402)
.build();
this.setData(proto);
}
player.setSceneLoadState(SceneLoadState.LOADING);
player.setEnterSceneToken(Utils.randomRange(1000, 99999));
PlayerEnterSceneNotify proto = PlayerEnterSceneNotify.newBuilder()
.setSceneId(player.getSceneId())
.setPos(player.getPosition().toProto())
.setSceneBeginTime(System.currentTimeMillis())
.setType(EnterType.ENTER_TYPE_SELF)
.setTargetUid(player.getUid())
.setEnterSceneToken(player.getEnterSceneToken())
.setWorldLevel(player.getWorldLevel())
.setEnterReason(EnterReason.Login.getValue())
.setIsFirstLoginEnterScene(player.isFirstLoginEnterScene())
.setWorldType(1)
.setSceneTransaction("3-" + player.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402)
.build();
this.setData(proto);
}
public PacketPlayerEnterSceneNotify(Player player, EnterType type, EnterReason reason, int newScene, Position newPos) {
this(player, player, type, reason, newScene, newPos);
}
// Teleport or go somewhere
public PacketPlayerEnterSceneNotify(Player player, Player target, EnterType type, EnterReason reason, int newScene, Position newPos) {
super(PacketOpcodes.PlayerEnterSceneNotify);
player.setSceneLoadState(SceneLoadState.LOADING);
player.setEnterSceneToken(Utils.randomRange(1000, 99999));
PlayerEnterSceneNotify.Builder proto = PlayerEnterSceneNotify.newBuilder()
.setPrevSceneId(player.getSceneId())
.setPrevPos(player.getPosition().toProto())
.setSceneId(newScene)
.setPos(newPos.toProto())
.setSceneBeginTime(System.currentTimeMillis())
.setType(type)
.setTargetUid(target.getUid())
.setEnterSceneToken(player.getEnterSceneToken())
.setWorldLevel(target.getWorld().getWorldLevel())
.setEnterReason(reason.getValue())
.setWorldType(1)
.setSceneTransaction(newScene + "-" + target.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402);
for (int i = 0; i < 3000; i++) {
proto.addSceneTagIdList(i);
}
this.setData(proto.build());
}
}

View File

@@ -13,61 +13,61 @@ import emu.grasscutter.server.http.dispatch.RegionHandler;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils;
import static emu.grasscutter.config.Configuration.*;
import java.io.File;
import java.util.Base64;
import java.util.Objects;
import static emu.grasscutter.Configuration.*;
public class PacketPlayerLoginRsp extends BasePacket {
private static QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionCache;
private static QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionCache;
public PacketPlayerLoginRsp(GameSession session) {
super(PacketOpcodes.PlayerLoginRsp, 1);
this.setUseDispatchKey(true);
public PacketPlayerLoginRsp(GameSession session) {
super(PacketOpcodes.PlayerLoginRsp, 1);
RegionInfo info;
this.setUseDispatchKey(true);
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
if (regionCache == null) {
try {
// todo: we might want to push custom config to client
RegionInfo serverRegion = RegionInfo.newBuilder()
.setGateserverIp(lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress))
.setGateserverPort(lr(GAME_INFO.accessPort, GAME_INFO.bindPort))
.setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.build();
RegionInfo info;
regionCache = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build();
} catch (Exception e) {
Grasscutter.getLogger().error("Error while initializing region cache!", e);
}
}
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
if (regionCache == null) {
try {
// todo: we might want to push custom config to client
RegionInfo serverRegion = RegionInfo.newBuilder()
.setGateserverIp(lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress))
.setGateserverPort(lr(GAME_INFO.accessPort, GAME_INFO.bindPort))
.setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.build();
info = regionCache.getRegionInfo();
} else {
info = Objects.requireNonNull(RegionHandler.getCurrentRegion()).getRegionInfo();
}
regionCache = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build();
} catch (Exception e) {
Grasscutter.getLogger().error("Error while initializing region cache!", e);
}
}
PlayerLoginRsp p = PlayerLoginRsp.newBuilder()
.setIsUseAbilityHash(true) // true
.setAbilityHashCode(1844674) // 1844674
.setGameBiz("hk4e_global")
.setClientDataVersion(info.getClientDataVersion())
.setClientSilenceDataVersion(info.getClientSilenceDataVersion())
.setClientMd5(info.getClientDataMd5())
.setClientSilenceMd5(info.getClientSilenceDataMd5())
.setResVersionConfig(info.getResVersionConfig())
.setClientVersionSuffix(info.getClientVersionSuffix())
.setClientSilenceVersionSuffix(info.getClientSilenceVersionSuffix())
.setIsScOpen(false)
//.setScInfo(ByteString.copyFrom(new byte[] {}))
.setRegisterCps("mihoyo")
.setCountryCode("US")
.build();
this.setData(p.toByteArray());
}
info = regionCache.getRegionInfo();
} else {
info = Objects.requireNonNull(RegionHandler.getCurrentRegion()).getRegionInfo();
}
PlayerLoginRsp p = PlayerLoginRsp.newBuilder()
.setIsUseAbilityHash(true) // true
.setAbilityHashCode(1844674) // 1844674
.setGameBiz("hk4e_global")
.setClientDataVersion(info.getClientDataVersion())
.setClientSilenceDataVersion(info.getClientSilenceDataVersion())
.setClientMd5(info.getClientDataMd5())
.setClientSilenceMd5(info.getClientSilenceDataMd5())
.setResVersionConfig(info.getResVersionConfig())
.setClientVersionSuffix(info.getClientVersionSuffix())
.setClientSilenceVersionSuffix(info.getClientSilenceVersionSuffix())
.setIsScOpen(false)
//.setScInfo(ByteString.copyFrom(new byte[] {}))
.setRegisterCps("mihoyo")
.setCountryCode("US")
.build();
this.setData(p.toByteArray());
}
}

View File

@@ -1,5 +1,7 @@
package emu.grasscutter.server.packet.send;
import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
@@ -8,24 +10,22 @@ import emu.grasscutter.net.proto.ItemOuterClass.Item;
import emu.grasscutter.net.proto.PlayerStoreNotifyOuterClass.PlayerStoreNotify;
import emu.grasscutter.net.proto.StoreTypeOuterClass.StoreType;
import static emu.grasscutter.Configuration.*;
public class PacketPlayerStoreNotify extends BasePacket {
public PacketPlayerStoreNotify(Player player) {
super(PacketOpcodes.PlayerStoreNotify);
this.buildHeader(2);
PlayerStoreNotify.Builder p = PlayerStoreNotify.newBuilder()
.setStoreType(StoreType.STORE_TYPE_PACK)
.setWeightLimit(GAME_OPTIONS.inventoryLimits.all);
for (GameItem item : player.getInventory()) {
Item itemProto = item.toProto();
p.addItemList(itemProto);
}
this.setData(p.build());
}
public PacketPlayerStoreNotify(Player player) {
super(PacketOpcodes.PlayerStoreNotify);
this.buildHeader(2);
PlayerStoreNotify.Builder p = PlayerStoreNotify.newBuilder()
.setStoreType(StoreType.STORE_TYPE_PACK)
.setWeightLimit(GAME_OPTIONS.inventoryLimits.all);
for (GameItem item : player.getInventory()) {
Item itemProto = item.toProto();
p.addItemList(itemProto);
}
this.setData(p.build());
}
}

View File

@@ -9,55 +9,64 @@ import emu.grasscutter.net.proto.SceneUnlockInfoOuterClass.SceneUnlockInfo;
import static emu.grasscutter.net.proto.PlayerWorldSceneInfoOuterClass.*;
public class PacketPlayerWorldSceneInfoListNotify extends BasePacket {
public PacketPlayerWorldSceneInfoListNotify() {
super(PacketOpcodes.PlayerWorldSceneInfoListNotify); // Rename opcode later
PlayerWorldSceneInfoListNotify proto = PlayerWorldSceneInfoListNotify.newBuilder()
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(1)
.setIsLocked(false)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(3)
.setIsLocked(false)
.addSceneTagIdList(102)
.addSceneTagIdList(113)
.addSceneTagIdList(117)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(4)
.setIsLocked(false)
.addSceneTagIdList(106)
.addSceneTagIdList(109)
.addSceneTagIdList(117)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(5)
.setIsLocked(false)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(6)
.setIsLocked(false)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(7)
.setIsLocked(false)
.build()
)
.build();
this.setData(proto);
}
public PacketPlayerWorldSceneInfoListNotify() {
super(PacketOpcodes.PlayerWorldSceneInfoListNotify); // Rename opcode later
PlayerWorldSceneInfoListNotify.Builder proto = PlayerWorldSceneInfoListNotify.newBuilder()
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(1)
.setIsLocked(false)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(3)
.setIsLocked(false)
.addSceneTagIdList(102)
.addSceneTagIdList(113)
.addSceneTagIdList(117)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(4)
.setIsLocked(false)
.addSceneTagIdList(106)
.addSceneTagIdList(109)
.addSceneTagIdList(117)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(5)
.setIsLocked(false)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(6)
.setIsLocked(false)
.build()
)
.addInfoList(
PlayerWorldSceneInfo.newBuilder()
.setSceneId(7)
.setIsLocked(false)
.build()
);
var gaa = PlayerWorldSceneInfo.newBuilder()
.setSceneId(9)
.setIsLocked(false);
for (int i = 0; i < 3000; i++) {
gaa.addSceneTagIdList(i);
}
proto.addInfoList(gaa);
this.setData(proto);
}
}

View File

@@ -6,37 +6,45 @@ import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
import emu.grasscutter.net.proto.PrivateChatNotifyOuterClass.PrivateChatNotify;
public class PacketPrivateChatNotify extends BasePacket {
public PacketPrivateChatNotify(int senderId, int recvId, String message) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info = ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setText(message)
.build();
PrivateChatNotify proto = PrivateChatNotify.newBuilder()
.setChatInfo(info)
.build();
this.setData(proto);
}
public PacketPrivateChatNotify(int senderId, int recvId, int emote) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info = ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setIcon(emote)
.build();
PrivateChatNotify proto = PrivateChatNotify.newBuilder()
.setChatInfo(info)
.build();
this.setData(proto);
}
private ChatInfo info;
public PacketPrivateChatNotify(int senderId, int recvId, String message) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info = ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setText(message)
.build();
this.info = info;
PrivateChatNotify proto = PrivateChatNotify.newBuilder()
.setChatInfo(info)
.build();
this.setData(proto);
}
public PacketPrivateChatNotify(int senderId, int recvId, int emote) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info = ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setIcon(emote)
.build();
this.info = info;
PrivateChatNotify proto = PrivateChatNotify.newBuilder()
.setChatInfo(info)
.build();
this.setData(proto);
}
public ChatInfo getChatInfo() {
return this.info;
}
}

View File

@@ -1,16 +1,29 @@
package emu.grasscutter.server.packet.send;
import java.util.List;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
import emu.grasscutter.net.proto.PullPrivateChatRspOuterClass.PullPrivateChatRsp;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
public class PacketPullPrivateChatRsp extends BasePacket {
public PacketPullPrivateChatRsp() {
super(PacketOpcodes.PullPrivateChatRsp);
PullPrivateChatRsp proto = PullPrivateChatRsp.newBuilder().build();
this.setData(proto);
}
public PacketPullPrivateChatRsp(List<ChatInfo> history) {
super(PacketOpcodes.PullPrivateChatRsp);
PullPrivateChatRsp.Builder builder = PullPrivateChatRsp.newBuilder();
if (history == null) {
builder.setRetcode(Retcode.RET_FAIL_VALUE);
}
else {
for (var info : history) {
builder.addChatInfo(info);
}
}
this.setData(builder.build());
}
}

View File

@@ -8,36 +8,17 @@ import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
import emu.grasscutter.net.proto.PullRecentChatRspOuterClass.PullRecentChatRsp;
import emu.grasscutter.utils.Utils;
import static emu.grasscutter.Configuration.*;
import static emu.grasscutter.config.Configuration.*;
import java.util.List;
public class PacketPullRecentChatRsp extends BasePacket {
public PacketPullRecentChatRsp(Player player) {
super(PacketOpcodes.PullRecentChatRsp);
var joinOptions = GAME_INFO.joinOptions;
PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder();
if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) {
ChatInfo welcomeEmote = ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(GameConstants.SERVER_CONSOLE_UID)
.setToUid(player.getUid())
.setIcon(joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)])
.build();
proto.addChatInfo(welcomeEmote);
}
if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) {
ChatInfo welcomeMessage = ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(GameConstants.SERVER_CONSOLE_UID)
.setToUid(player.getUid())
.setText(joinOptions.welcomeMessage)
.build();
proto.addChatInfo(welcomeMessage);
}
public PacketPullRecentChatRsp(List<ChatInfo> messages) {
super(PacketOpcodes.PullRecentChatRsp);
this.setData(proto);
}
PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder()
.addAllChatInfo(messages);
this.setData(proto);
}
}

View File

@@ -14,10 +14,10 @@ public class PacketQueryCodexMonsterBeKilledNumRsp extends BasePacket {
QueryCodexMonsterBeKilledNumRsp.Builder proto = QueryCodexMonsterBeKilledNumRsp.newBuilder();
codexList.forEach(animal -> {
if(player.getCodex().getUnlockedAnimal().containsKey(animal)){
if (player.getCodex().getUnlockedAnimal().containsKey(animal)) {
proto.addCodexIdList(animal)
.addBeKilledNumList(player.getCodex().getUnlockedAnimal().get(animal))
.addCHPBKCLKPCJ(0);
.addBeKilledNumList(player.getCodex().getUnlockedAnimal().get(animal))
.addUnk2700MKOBMGGPNMI(0);
}
});

View File

@@ -0,0 +1,44 @@
package emu.grasscutter.server.packet.send;
import java.util.Collection;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.PlayerBuffManager.PlayerBuff;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ServerBuffChangeNotifyOuterClass.ServerBuffChangeNotify;
import emu.grasscutter.net.proto.ServerBuffChangeNotifyOuterClass.ServerBuffChangeNotify.ServerBuffChangeType;
public class PacketServerBuffChangeNotify extends BasePacket {
public PacketServerBuffChangeNotify(Player player, ServerBuffChangeType changeType, PlayerBuff buff) {
super(PacketOpcodes.ServerBuffChangeNotify);
var proto = ServerBuffChangeNotify.newBuilder();
for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) {
proto.addAvatarGuidList(entity.getAvatar().getGuid());
}
proto.setServerBuffChangeType(changeType);
proto.addServerBuffList(buff.toProto());
this.setData(proto);
}
public PacketServerBuffChangeNotify(Player player, ServerBuffChangeType changeType, Collection<PlayerBuff> buffs) {
super(PacketOpcodes.ServerBuffChangeNotify);
var proto = ServerBuffChangeNotify.newBuilder();
for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) {
proto.addAvatarGuidList(entity.getAvatar().getGuid());
}
proto.setServerBuffChangeType(changeType);
proto.addAllServerBuffList(buffs.stream().map(PlayerBuff::toProto).toList());
this.setData(proto);
}
}

View File

@@ -0,0 +1,18 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SetOpenStateRspOuterClass.SetOpenStateRsp;
public class PacketSetOpenStateRsp extends BasePacket {
public PacketSetOpenStateRsp(int openState, int value) {
super(PacketOpcodes.SetOpenStateRsp);
SetOpenStateRsp proto = SetOpenStateRsp.newBuilder()
.setKey(openState).setValue(value).build();
this.setData(proto);
}
}

View File

@@ -1,26 +1,26 @@
package emu.grasscutter.server.packet.send;
import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.StoreTypeOuterClass.StoreType;
import emu.grasscutter.net.proto.StoreWeightLimitNotifyOuterClass.StoreWeightLimitNotify;
import static emu.grasscutter.Configuration.*;
public class PacketStoreWeightLimitNotify extends BasePacket {
public PacketStoreWeightLimitNotify() {
super(PacketOpcodes.StoreWeightLimitNotify);
StoreWeightLimitNotify p = StoreWeightLimitNotify.newBuilder()
.setStoreType(StoreType.STORE_TYPE_PACK)
.setWeightLimit(INVENTORY_LIMITS.all)
.setWeaponCountLimit(INVENTORY_LIMITS.weapons)
.setReliquaryCountLimit(INVENTORY_LIMITS.relics)
.setMaterialCountLimit(INVENTORY_LIMITS.materials)
.setFurnitureCountLimit(INVENTORY_LIMITS.furniture)
.build();
this.setData(p);
}
public PacketStoreWeightLimitNotify() {
super(PacketOpcodes.StoreWeightLimitNotify);
StoreWeightLimitNotify p = StoreWeightLimitNotify.newBuilder()
.setStoreType(StoreType.STORE_TYPE_PACK)
.setWeightLimit(INVENTORY_LIMITS.all)
.setWeaponCountLimit(INVENTORY_LIMITS.weapons)
.setReliquaryCountLimit(INVENTORY_LIMITS.relics)
.setMaterialCountLimit(INVENTORY_LIMITS.materials)
.setFurnitureCountLimit(INVENTORY_LIMITS.furniture)
.build();
this.setData(p);
}
}

View File

@@ -1,7 +1,7 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.tower.TowerManager;
import emu.grasscutter.game.tower.TowerScheduleManager;
import emu.grasscutter.game.tower.TowerSystem;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.TowerAllDataRspOuterClass.TowerAllDataRsp;
@@ -16,49 +16,49 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class PacketTowerAllDataRsp extends BasePacket {
public PacketTowerAllDataRsp(TowerScheduleManager towerScheduleManager, TowerManager towerManager) {
super(PacketOpcodes.TowerAllDataRsp);
var recordList = towerManager.getRecordMap().values().stream()
.map(rec -> TowerFloorRecord.newBuilder()
.setFloorId(rec.getFloorId())
.setFloorStarRewardProgress(rec.getFloorStarRewardProgress())
.putAllPassedLevelMap(rec.getPassedLevelMap())
.addAllPassedLevelRecordList(buildFromPassedLevelMap(rec.getPassedLevelMap()))
.build()
)
.toList();
public PacketTowerAllDataRsp(TowerSystem towerScheduleManager, TowerManager towerManager) {
super(PacketOpcodes.TowerAllDataRsp);
var openTimeMap = towerScheduleManager.getScheduleFloors().stream()
.collect(Collectors.toMap(x -> x,
y -> DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
.getScheduleStartTime()))
);
var recordList = towerManager.getRecordMap().values().stream()
.map(rec -> TowerFloorRecord.newBuilder()
.setFloorId(rec.getFloorId())
.setFloorStarRewardProgress(rec.getFloorStarRewardProgress())
.putAllPassedLevelMap(rec.getPassedLevelMap())
.addAllPassedLevelRecordList(buildFromPassedLevelMap(rec.getPassedLevelMap()))
.build()
)
.toList();
TowerAllDataRsp proto = TowerAllDataRsp.newBuilder()
.setTowerScheduleId(towerScheduleManager.getCurrentTowerScheduleData().getScheduleId())
.addAllTowerFloorRecordList(recordList)
.setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true))
.setScheduleStartTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
.getScheduleStartTime()))
.setNextScheduleChangeTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
.getNextScheduleChangeTime()))
.putAllFloorOpenTimeMap(openTimeMap)
.setIsFinishedEntranceFloor(towerManager.canEnterScheduleFloor())
.build();
this.setData(proto);
}
var openTimeMap = towerScheduleManager.getScheduleFloors().stream()
.collect(Collectors.toMap(x -> x,
y -> DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
.getScheduleStartTime()))
);
private List<TowerLevelRecordOuterClass.TowerLevelRecord> buildFromPassedLevelMap(Map<Integer, Integer> map){
return map.entrySet().stream()
.map(item -> TowerLevelRecordOuterClass.TowerLevelRecord.newBuilder()
.setLevelId(item.getKey())
.addAllSatisfiedCondList(IntStream.range(1, item.getValue() + 1).boxed().toList())
.build())
.toList();
TowerAllDataRsp proto = TowerAllDataRsp.newBuilder()
.setTowerScheduleId(towerScheduleManager.getCurrentTowerScheduleData().getScheduleId())
.addAllTowerFloorRecordList(recordList)
.setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true))
.setScheduleStartTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
.getScheduleStartTime()))
.setNextScheduleChangeTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
.getNextScheduleChangeTime()))
.putAllFloorOpenTimeMap(openTimeMap)
.setIsFinishedEntranceFloor(towerManager.canEnterScheduleFloor())
.build();
}
this.setData(proto);
}
private List<TowerLevelRecordOuterClass.TowerLevelRecord> buildFromPassedLevelMap(Map<Integer, Integer> map) {
return map.entrySet().stream()
.map(item -> TowerLevelRecordOuterClass.TowerLevelRecord.newBuilder()
.setLevelId(item.getKey())
.addAllSatisfiedCondList(IntStream.range(1, item.getValue() + 1).boxed().toList())
.build())
.toList();
}
}