Commit 01b190bc authored by Magix's avatar Magix Committed by GitHub
Browse files

UPGRADE TO 1.1.0 POG

Merge `development` into `stable`
parents 6b81b888 1beddf16
package emu.grasscutter.server.event.player;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.types.GameEvent;
import emu.grasscutter.server.event.types.PlayerEvent;
public final class PlayerQuitEvent extends PlayerEvent {
public PlayerQuitEvent(Player player) {
super(player);
}
}
package emu.grasscutter.server.event.player;
import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.Cancellable;
import emu.grasscutter.server.event.types.PlayerEvent;
public final class PlayerReceiveMailEvent extends PlayerEvent implements Cancellable {
private Mail message;
public PlayerReceiveMailEvent(Player player, Mail message) {
super(player);
this.message = message;
}
public void setMessage(Mail message) {
this.message = message;
}
public Mail getMessage() {
return this.message;
}
}
package emu.grasscutter.server.event.types;
import emu.grasscutter.server.event.Event;
/**
* An event that is related to the game.
*/
public abstract class GameEvent extends Event {
}
\ No newline at end of file
package emu.grasscutter.server.event.types;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.Event;
/**
* An event that is related to player interactions.
*/
public abstract class PlayerEvent extends Event {
protected final Player player;
public PlayerEvent(Player player) {
this.player = player;
}
public Player getPlayer() {
return this.player;
}
}
package emu.grasscutter.server.event.types;
import emu.grasscutter.server.event.Event;
/**
* An event that is related to the internals of the server.
*/
public abstract class ServerEvent extends Event {
protected final Type type;
public ServerEvent(Type type) {
this.type = type;
}
public Type getServerType() {
return this.type;
}
public enum Type {
DISPATCH,
GAME
}
}
package emu.grasscutter.server.game; package emu.grasscutter.server.game;
import java.net.InetSocketAddress; import emu.grasscutter.GameConstants;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import emu.grasscutter.GenshinConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandMap; import emu.grasscutter.command.CommandMap;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.combine.CombineManger;
import emu.grasscutter.game.drop.DropManager;
import emu.grasscutter.game.dungeons.DungeonManager; import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.expedition.ExpeditionManager;
import emu.grasscutter.game.gacha.GachaManager; import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.managers.ChatManager; import emu.grasscutter.game.managers.ChatManager;
import emu.grasscutter.game.managers.InventoryManager; import emu.grasscutter.game.managers.InventoryManager;
import emu.grasscutter.game.managers.MultiplayerManager; import emu.grasscutter.game.managers.MultiplayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.game.shop.ShopManager;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.netty.MihoyoKcpServer; import emu.grasscutter.netty.KcpServer;
import org.greenrobot.eventbus.EventBus; import emu.grasscutter.server.event.types.ServerEvent;
import emu.grasscutter.server.event.game.ServerTickEvent;
import emu.grasscutter.server.event.internal.ServerStartEvent;
import emu.grasscutter.server.event.internal.ServerStopEvent;
import emu.grasscutter.task.TaskMap;
import java.net.InetSocketAddress;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public final class GameServer extends MihoyoKcpServer { import static emu.grasscutter.utils.Language.translate;
public final class GameServer extends KcpServer {
private final InetSocketAddress address; private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler; private final GameServerPacketHandler packetHandler;
private final Map<Integer, GenshinPlayer> players; private final Map<Integer, Player> players;
private final Set<World> worlds;
private final ChatManager chatManager; private final ChatManager chatManager;
private final InventoryManager inventoryManager; private final InventoryManager inventoryManager;
...@@ -33,23 +48,28 @@ public final class GameServer extends MihoyoKcpServer { ...@@ -33,23 +48,28 @@ public final class GameServer extends MihoyoKcpServer {
private final ShopManager shopManager; private final ShopManager shopManager;
private final MultiplayerManager multiplayerManager; private final MultiplayerManager multiplayerManager;
private final DungeonManager dungeonManager; private final DungeonManager dungeonManager;
private final ExpeditionManager expeditionManager;
private final CommandMap commandMap; private final CommandMap commandMap;
private final TaskMap taskMap;
private final DropManager dropManager;
private final CombineManger combineManger;
public EventBus OnGameServerStartFinish; public GameServer() {
public EventBus OnGameServerTick; this(new InetSocketAddress(
public EventBus OnGameServerStop; Grasscutter.getConfig().getGameServerOptions().Ip,
Grasscutter.getConfig().getGameServerOptions().Port
));
}
public GameServer(InetSocketAddress address) { public GameServer(InetSocketAddress address) {
super(address); super(address);
OnGameServerStartFinish = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build();
OnGameServerTick = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build();
OnGameServerStop = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build();
this.setServerInitializer(new GameServerInitializer(this)); this.setServerInitializer(new GameServerInitializer(this));
this.address = address; this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class); this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.players = new ConcurrentHashMap<>(); this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>());
this.chatManager = new ChatManager(this); this.chatManager = new ChatManager(this);
this.inventoryManager = new InventoryManager(this); this.inventoryManager = new InventoryManager(this);
...@@ -58,19 +78,10 @@ public final class GameServer extends MihoyoKcpServer { ...@@ -58,19 +78,10 @@ public final class GameServer extends MihoyoKcpServer {
this.multiplayerManager = new MultiplayerManager(this); this.multiplayerManager = new MultiplayerManager(this);
this.dungeonManager = new DungeonManager(this); this.dungeonManager = new DungeonManager(this);
this.commandMap = new CommandMap(true); this.commandMap = new CommandMap(true);
this.taskMap = new TaskMap(true);
// Schedule game loop. this.dropManager = new DropManager(this);
Timer gameLoop = new Timer(); this.expeditionManager = new ExpeditionManager(this);
gameLoop.scheduleAtFixedRate(new TimerTask() { this.combineManger = new CombineManger(this);
@Override
public void run() {
try {
onTick();
} catch (Exception e) {
Grasscutter.getLogger().error("An error occurred during game update.", e);
}
}
}, new Date(), 1000L);
// Hook into shutdown event. // Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown)); Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
...@@ -80,10 +91,14 @@ public final class GameServer extends MihoyoKcpServer { ...@@ -80,10 +91,14 @@ public final class GameServer extends MihoyoKcpServer {
return packetHandler; return packetHandler;
} }
public Map<Integer, GenshinPlayer> getPlayers() { public Map<Integer, Player> getPlayers() {
return players; return players;
} }
public Set<World> getWorlds() {
return worlds;
}
public ChatManager getChatManager() { public ChatManager getChatManager() {
return chatManager; return chatManager;
} }
...@@ -103,31 +118,47 @@ public final class GameServer extends MihoyoKcpServer { ...@@ -103,31 +118,47 @@ public final class GameServer extends MihoyoKcpServer {
public MultiplayerManager getMultiplayerManager() { public MultiplayerManager getMultiplayerManager() {
return multiplayerManager; return multiplayerManager;
} }
public DropManager getDropManager() {
return dropManager;
}
public DungeonManager getDungeonManager() { public DungeonManager getDungeonManager() {
return dungeonManager; return dungeonManager;
} }
public ExpeditionManager getExpeditionManager() {
return expeditionManager;
}
public CommandMap getCommandMap() { public CommandMap getCommandMap() {
return this.commandMap; return this.commandMap;
} }
public CombineManger getCombineManger(){
return this.combineManger;
}
public TaskMap getTaskMap() {
return this.taskMap;
}
public void registerPlayer(GenshinPlayer player) { public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player); getPlayers().put(player.getUid(), player);
} }
public GenshinPlayer getPlayerByUid(int id) { public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false); return this.getPlayerByUid(id, false);
} }
public GenshinPlayer getPlayerByUid(int id, boolean allowOfflinePlayers) { public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check // Console check
if (id == GenshinConstants.SERVER_CONSOLE_UID) { if (id == GameConstants.SERVER_CONSOLE_UID) {
return null; return null;
} }
// Get from online players // Get from online players
GenshinPlayer player = this.getPlayers().get(id); Player player = this.getPlayers().get(id);
if (!allowOfflinePlayers) { if (!allowOfflinePlayers) {
return player; return player;
...@@ -143,7 +174,7 @@ public final class GameServer extends MihoyoKcpServer { ...@@ -143,7 +174,7 @@ public final class GameServer extends MihoyoKcpServer {
public SocialDetail.Builder getSocialDetailByUid(int id) { public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players // Get from online players
GenshinPlayer player = this.getPlayerByUid(id, true); Player player = this.getPlayerByUid(id, true);
if (player == null) { if (player == null) {
return null; return null;
...@@ -153,7 +184,7 @@ public final class GameServer extends MihoyoKcpServer { ...@@ -153,7 +184,7 @@ public final class GameServer extends MihoyoKcpServer {
} }
public Account getAccountByName(String username) { public Account getAccountByName(String username) {
Optional<GenshinPlayer> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst(); Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
if (playerOpt.isPresent()) { if (playerOpt.isPresent()) {
return playerOpt.get().getAccount(); return playerOpt.get().getAccount();
} }
...@@ -161,28 +192,66 @@ public final class GameServer extends MihoyoKcpServer { ...@@ -161,28 +192,66 @@ public final class GameServer extends MihoyoKcpServer {
} }
public void onTick() throws Exception { public void onTick() throws Exception {
for (GenshinPlayer player : this.getPlayers().values()) { Iterator<World> it = this.getWorlds().iterator();
while (it.hasNext()) {
World world = it.next();
if (world.getPlayerCount() == 0) {
it.remove();
}
world.onTick();
}
for (Player player : this.getPlayers().values()) {
player.onTick(); player.onTick();
} }
ServerTickEvent event = new ServerTickEvent(); event.call();
}
public void registerWorld(World world) {
this.getWorlds().add(world);
}
public void deregisterWorld(World world) {
// TODO Auto-generated method stub
}
OnGameServerTick.post(new GameServerTickEvent()); @Override
public synchronized 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);
super.start();
} }
@Override @Override
public void onStartFinish() { public void onStartFinish() {
Grasscutter.getLogger().info("Game Server started on port " + address.getPort()); Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
OnGameServerStartFinish.post(new GameServerStartFinishEvent()); ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
} }
public void onServerShutdown() { public void onServerShutdown() {
OnGameServerStop.post(new GameServerStopEvent()); ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
// Kick and save all players // Kick and save all players
List<GenshinPlayer> list = new ArrayList<>(this.getPlayers().size()); List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values()); list.addAll(this.getPlayers().values());
for (GenshinPlayer player : list) { for (Player player : list) {
player.getSession().close(); player.getSession().close();
} }
} }
......
package emu.grasscutter.server.game; package emu.grasscutter.server.game;
import emu.grasscutter.netty.MihoyoKcpServerInitializer; import emu.grasscutter.netty.KcpServerInitializer;
import io.jpower.kcp.netty.UkcpChannel; import io.jpower.kcp.netty.UkcpChannel;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
public class GameServerInitializer extends MihoyoKcpServerInitializer { public class GameServerInitializer extends KcpServerInitializer {
private GameServer server; private GameServer server;
public GameServerInitializer(GameServer server) { public GameServerInitializer(GameServer server) {
......
...@@ -2,9 +2,11 @@ package emu.grasscutter.server.game; ...@@ -2,9 +2,11 @@ package emu.grasscutter.server.game;
import java.util.Set; import java.util.Set;
import emu.grasscutter.server.event.game.ReceivePacketEvent;
import org.reflections.Reflections; import org.reflections.Reflections;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
...@@ -12,6 +14,7 @@ import emu.grasscutter.server.game.GameSession.SessionState; ...@@ -12,6 +14,7 @@ import emu.grasscutter.server.game.GameSession.SessionState;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@SuppressWarnings("unchecked")
public class GameServerPacketHandler { public class GameServerPacketHandler {
private final Int2ObjectMap<PacketHandler> handlers; private final Int2ObjectMap<PacketHandler> handlers;
...@@ -20,27 +23,29 @@ public class GameServerPacketHandler { ...@@ -20,27 +23,29 @@ public class GameServerPacketHandler {
this.registerHandlers(handlerClass); this.registerHandlers(handlerClass);
} }
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
PacketHandler packetHandler = (PacketHandler) handlerClass.newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
public void registerHandlers(Class<? extends PacketHandler> handlerClass) { public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet"); Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass); Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
for (Object obj : handlerClasses) { for (Object obj : handlerClasses) {
Class<?> c = (Class<?>) obj; this.registerPacketHandler((Class<? extends PacketHandler>) obj);
try {
Opcodes opcode = c.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
continue;
}
PacketHandler packetHandler = (PacketHandler) c.newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
} }
// Debug // Debug
...@@ -48,9 +53,7 @@ public class GameServerPacketHandler { ...@@ -48,9 +53,7 @@ public class GameServerPacketHandler {
} }
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) { public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
PacketHandler handler = null; PacketHandler handler = this.handlers.get(opcode);
handler = this.handlers.get(opcode);
if (handler != null) { if (handler != null) {
try { try {
...@@ -77,8 +80,10 @@ public class GameServerPacketHandler { ...@@ -77,8 +80,10 @@ public class GameServerPacketHandler {
} }
} }
// Handle // Invoke event.
handler.handle(session, header, payload); 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) { } catch (Exception ex) {
// TODO Remove this when no more needed // TODO Remove this when no more needed
ex.printStackTrace(); ex.printStackTrace();
...@@ -87,8 +92,8 @@ public class GameServerPacketHandler { ...@@ -87,8 +92,8 @@ public class GameServerPacketHandler {
} }
// Log unhandled packets // Log unhandled packets
if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) { if (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING) {
//Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + PacketOpcodesUtil.getOpcodeName(opcode)); Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtil.getOpcodeName(opcode));
} }
} }
} }
...@@ -3,13 +3,18 @@ package emu.grasscutter.server.game; ...@@ -3,13 +3,18 @@ package emu.grasscutter.server.game;
import java.io.File; import java.io.File;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketOpcodesUtil; import emu.grasscutter.net.packet.PacketOpcodesUtil;
import emu.grasscutter.netty.MihoyoKcpChannel; import emu.grasscutter.netty.KcpChannel;
import emu.grasscutter.server.event.game.SendPacketEvent;
import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
...@@ -17,11 +22,13 @@ import io.netty.buffer.ByteBuf; ...@@ -17,11 +22,13 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
public class GameSession extends MihoyoKcpChannel { import static emu.grasscutter.utils.Language.translate;
public class GameSession extends KcpChannel {
private GameServer server; private GameServer server;
private Account account; private Account account;
private GenshinPlayer player; private Player player;
private boolean useSecretKey; private boolean useSecretKey;
private SessionState state; private SessionState state;
...@@ -63,11 +70,11 @@ public class GameSession extends MihoyoKcpChannel { ...@@ -63,11 +70,11 @@ public class GameSession extends MihoyoKcpChannel {
return this.getAccount().getId(); return this.getAccount().getId();
} }
public GenshinPlayer getPlayer() { public Player getPlayer() {
return player; return player;
} }
public synchronized void setPlayer(GenshinPlayer player) { public synchronized void setPlayer(Player player) {
this.player = player; this.player = player;
this.player.setSession(this); this.player.setSession(this);
this.player.setAccount(this.getAccount()); this.player.setAccount(this.getAccount());
...@@ -108,21 +115,21 @@ public class GameSession extends MihoyoKcpChannel { ...@@ -108,21 +115,21 @@ public class GameSession extends MihoyoKcpChannel {
@Override @Override
protected void onConnect() { protected void onConnect() {
Grasscutter.getLogger().info("Client connected from " + getAddress().getHostString().toLowerCase()); Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().getHostString().toLowerCase()));
} }
@Override @Override
protected synchronized void onDisconnect() { // Synchronize so we dont add character at the same time protected synchronized void onDisconnect() { // Synchronize so we don't add character at the same time.
Grasscutter.getLogger().info("Client disconnected from " + getAddress().getHostString().toLowerCase()); Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().getHostString().toLowerCase()));
// Set state so no more packets can be handled // Set state so no more packets can be handled
this.setState(SessionState.INACTIVE); this.setState(SessionState.INACTIVE);
// Save after disconnecting // Save after disconnecting
if (this.isLoggedIn()) { if (this.isLoggedIn()) {
// Save // Call logout event.
getPlayer().onLogout(); getPlayer().onLogout();
// Remove from gameserver // Remove from server.
getServer().getPlayers().remove(getPlayer().getUid()); getServer().getPlayers().remove(getPlayer().getUid());
} }
} }
...@@ -140,47 +147,54 @@ public class GameSession extends MihoyoKcpChannel { ...@@ -140,47 +147,54 @@ public class GameSession extends MihoyoKcpChannel {
byte[] packet = FileUtils.read(p); byte[] packet = FileUtils.read(p);
GenshinPacket genshinPacket = new GenshinPacket(opcode); BasePacket basePacket = new BasePacket(opcode);
genshinPacket.setData(packet); basePacket.setData(packet);
// Log
logPacket(genshinPacket.getOpcode());
send(genshinPacket); send(basePacket);
} }
public void send(GenshinPacket genshinPacket) { public void send(BasePacket packet) {
// Test // Test
if (genshinPacket.getOpcode() <= 0) { if (packet.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!"); Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return; return;
} }
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if(PacketOpcodes.BANNED_PACKETS.contains(packet.getOpcode())) {
return;
}
// Header // Header
if (genshinPacket.shouldBuildHeader()) { if (packet.shouldBuildHeader()) {
genshinPacket.buildHeader(this.getNextClientSequence()); packet.buildHeader(this.getNextClientSequence());
} }
// Build packet
byte[] data = genshinPacket.build();
// Log // Log
if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) { if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) {
logPacket(genshinPacket); logPacket(packet);
} }
// Send // Invoke event.
send(data); SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
} if(!event.isCanceled()) // If event is not cancelled, continue.
this.send(event.getPacket().build());
private void logPacket(int opcode) {
//Grasscutter.getLogger().info("SEND: " + PacketOpcodesUtil.getOpcodeName(opcode));
//System.out.println(Utils.bytesToHex(genshinPacket.getData()));
} }
private void logPacket(GenshinPacket genshinPacket) { private static final Set<Integer> loopPacket = Set.of(
Grasscutter.getLogger().info("SEND: " + PacketOpcodesUtil.getOpcodeName(genshinPacket.getOpcode()) + " (" + genshinPacket.getOpcode() + ")"); PacketOpcodes.PingReq,
System.out.println(Utils.bytesToHex(genshinPacket.getData())); PacketOpcodes.PingRsp,
PacketOpcodes.WorldPlayerRTTNotify,
PacketOpcodes.UnionCmdNotify,
PacketOpcodes.QueryPathReq
);
private void logPacket(BasePacket packet) {
if (!loopPacket.contains(packet.getOpcode())) {
Grasscutter.getLogger().info("SEND: " + PacketOpcodesUtil.getOpcodeName(packet.getOpcode()) + " (" + packet.getOpcode() + ")");
System.out.println(Utils.bytesToHex(packet.getData()));
}
} }
@Override @Override
...@@ -225,9 +239,11 @@ public class GameSession extends MihoyoKcpChannel { ...@@ -225,9 +239,11 @@ public class GameSession extends MihoyoKcpChannel {
} }
// Log packet // Log packet
if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) { if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) {
Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")"); if (!loopPacket.contains(opcode)) {
System.out.println(Utils.bytesToHex(payload)); Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")");
System.out.println(Utils.bytesToHex(payload));
}
} }
// Handle // Handle
......
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAvatarExpeditionAllDataRsp;
@Opcodes(PacketOpcodes.AvatarExpeditionAllDataReq)
public class HandlerAvatarExpeditionAllDataReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketAvatarExpeditionAllDataRsp(session.getPlayer()));
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarExpeditionCallBackReqOuterClass.AvatarExpeditionCallBackReq;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAvatarExpeditionCallBackRsp;
import emu.grasscutter.server.packet.send.PacketAvatarExpeditionStartRsp;
import emu.grasscutter.utils.Utils;
@Opcodes(PacketOpcodes.AvatarExpeditionCallBackReq)
public class HandlerAvatarExpeditionCallBackReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarExpeditionCallBackReq req = AvatarExpeditionCallBackReq.parseFrom(payload);
for (int i = 0; i < req.getAvatarGuidCount(); i++) {
session.getPlayer().removeExpeditionInfo(req.getAvatarGuid(i));
}
session.getPlayer().save();
session.send(new PacketAvatarExpeditionCallBackRsp(session.getPlayer()));
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.drop.DropData;
import emu.grasscutter.game.expedition.ExpeditionInfo;
import emu.grasscutter.game.expedition.ExpeditionRewardData;
import emu.grasscutter.game.expedition.ExpeditionRewardDataList;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarExpeditionGetRewardReqOuterClass.AvatarExpeditionGetRewardReq;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAvatarExpeditionCallBackRsp;
import emu.grasscutter.server.packet.send.PacketAvatarExpeditionGetRewardRsp;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
import emu.grasscutter.utils.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@Opcodes(PacketOpcodes.AvatarExpeditionGetRewardReq)
public class HandlerAvatarExpeditionGetRewardReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarExpeditionGetRewardReq req = AvatarExpeditionGetRewardReq.parseFrom(payload);
ExpeditionInfo expInfo = session.getPlayer().getExpeditionInfo(req.getAvatarGuid());
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()){
for (ExpeditionRewardData RewardData :RewardDataList.getExpeditionRewardData()) {
int num = RewardData.getMinCount();
if(RewardData.getMinCount() != RewardData.getMaxCount()){
num = Utils.randomRange(RewardData.getMinCount(), RewardData.getMaxCount());
}
items.add(new GameItem(RewardData.getItemId(), num));
}
}
}
}
}
session.getPlayer().getInventory().addItems(items);
session.getPlayer().sendPacket(new PacketItemAddHintNotify(items, ActionReason.ExpeditionReward));
session.getPlayer().removeExpeditionInfo(req.getAvatarGuid());
session.getPlayer().save();
session.send(new PacketAvatarExpeditionGetRewardRsp(session.getPlayer(), items));
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarExpeditionStartReqOuterClass.AvatarExpeditionStartReq;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAvatarExpeditionStartRsp;
import emu.grasscutter.utils.Utils;
@Opcodes(PacketOpcodes.AvatarExpeditionStartReq)
public class HandlerAvatarExpeditionStartReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarExpeditionStartReq req = AvatarExpeditionStartReq.parseFrom(payload);
int startTime = Utils.getCurrentSeconds();
session.getPlayer().addExpeditionInfo(req.getAvatarGuid(), req.getExpId(), req.getHourTime(), startTime);
session.getPlayer().save();
session.send(new PacketAvatarExpeditionStartRsp(session.getPlayer()));
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.RewardData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarFetterLevelRewardReqOuterClass.AvatarFetterLevelRewardReq;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAvatarDataNotify;
import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
import emu.grasscutter.server.packet.send.PacketAvatarFetterLevelRewardRsp;
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify;
import emu.grasscutter.net.packet.PacketHandler;
@Opcodes(PacketOpcodes.AvatarFetterLevelRewardReq)
public class HandlerAvatarFetterLevelRewardReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarFetterLevelRewardReq req = AvatarFetterLevelRewardReq.parseFrom(payload);
if (req.getFetterLevel() < 10) {
// You don't have a full level of fetter level, why do you want to get a divorce certificate?
session.send(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel()));
} else {
long avatarGuid = req.getAvatarGuid();
Avatar avatar = session
.getPlayer()
.getAvatars()
.getAvatarByGuid(avatarGuid);
int rewardId = avatar.getNameCardRewardId();
RewardData card = GameData.getRewardDataMap().get(rewardId);
int cardId = card.getRewardItemList().get(0).getItemId();
if (session.getPlayer().getNameCardList().contains(cardId)) {
// Already got divorce certificate.
session.getPlayer().sendPacket(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel(), rewardId));
return;
}
GameItem item = new GameItem(cardId);
session.getPlayer().getInventory().addItem(item, ActionReason.FetterLevelReward);
session.getPlayer().sendPacket(new PacketUnlockNameCardNotify(cardId));
session.send(new PacketAvatarFetterDataNotify(avatar));
session.send(new PacketAvatarDataNotify(avatar.getPlayer()));
session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId));
}
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
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.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.BuyGoodsReqOuterClass;
import emu.grasscutter.net.proto.ItemParamOuterClass;
import emu.grasscutter.net.proto.ShopGoodsOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketBuyGoodsRsp;
import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify;
import emu.grasscutter.utils.Utils;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@Opcodes(PacketOpcodes.BuyGoodsReq)
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());
if (configShop == null)
return;
// Don't trust your users' input
List<Integer> targetShopGoodsId = buyGoodsReq.getGoodsListList().stream().map(ShopGoodsOuterClass.ShopGoods::getGoodsId).toList();
for (int goodsId : targetShopGoodsId) {
Optional<ShopInfo> sg2 = configShop.stream().filter(x -> x.getGoodsId() == goodsId).findFirst();
if (sg2.isEmpty())
continue;
ShopInfo sg = sg2.get();
int currentTs = Utils.getCurrentSeconds();
ShopLimit shopLimit = session.getPlayer().getGoodsLimit(sg.getGoodsId());
int bought = 0;
if (shopLimit != null) {
if (currentTs > shopLimit.getNextRefreshTime()) {
shopLimit.setNextRefreshTime(ShopManager.getShopNextRefreshTime(sg));
} else {
bought = shopLimit.getHasBoughtInPeriod();
}
session.getPlayer().save();
}
if ((bought + buyGoodsReq.getBoughtNum() > sg.getBuyLimit()) && sg.getBuyLimit() != 0) {
return;
}
if (sg.getScoin() > 0 && session.getPlayer().getMora() < buyGoodsReq.getBoughtNum() * sg.getScoin()) {
return;
}
if (sg.getHcoin() > 0 && session.getPlayer().getPrimogems() < buyGoodsReq.getBoughtNum() * sg.getHcoin()) {
return;
}
if (sg.getMcoin() > 0 && session.getPlayer().getCrystals() < buyGoodsReq.getBoughtNum() * sg.getMcoin()) {
return;
}
HashMap<GameItem, Integer> itemsCache = new HashMap<>();
if (sg.getCostItemList() != null) {
for (ItemParamData p : sg.getCostItemList()) {
Optional<GameItem> invItem = session.getPlayer().getInventory().getItems().values().stream().filter(x -> x.getItemId() == p.getId()).findFirst();
if (invItem.isEmpty() || invItem.get().getCount() < p.getCount())
return;
itemsCache.put(invItem.get(), p.getCount() * buyGoodsReq.getBoughtNum());
}
}
session.getPlayer().setMora(session.getPlayer().getMora() - buyGoodsReq.getBoughtNum() * sg.getScoin());
session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() - buyGoodsReq.getBoughtNum() * sg.getHcoin());
session.getPlayer().setCrystals(session.getPlayer().getCrystals() - buyGoodsReq.getBoughtNum() * sg.getMcoin());
if (!itemsCache.isEmpty()) {
for (GameItem gi : itemsCache.keySet()) {
session.getPlayer().getInventory().removeItem(gi, itemsCache.get(gi));
}
itemsCache.clear();
}
session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBoughtNum(), ShopManager.getShopNextRefreshTime(sg));
GameItem item = new GameItem(GameData.getItemDataMap().get(sg.getGoodsItem().getId()));
item.setCount(buyGoodsReq.getBoughtNum() * sg.getGoodsItem().getCount());
session.getPlayer().getInventory().addItem(item, ActionReason.Shop, true); // fix: not notify when got virtual item from shop
session.send(new PacketBuyGoodsRsp(buyGoodsReq.getShopType(), session.getPlayer().getGoodsLimit(sg.getGoodsId()).getHasBoughtInPeriod(), buyGoodsReq.getGoodsListList().stream().filter(x -> x.getGoodsId() == goodsId).findFirst().get()));
}
session.getPlayer().save();
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ChangeMailStarNotifyOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketMailChangeNotify;
import java.util.ArrayList;
import java.util.List;
@Opcodes(PacketOpcodes.ChangeMailStarNotify)
public class HandlerChangeMailStarNotify extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ChangeMailStarNotifyOuterClass.ChangeMailStarNotify req = ChangeMailStarNotifyOuterClass.ChangeMailStarNotify.parseFrom(payload);
List<Mail> updatedMail = new ArrayList<>();
for (int mailId : req.getMailIdListList()) {
Mail message = session.getPlayer().getMail(mailId);
message.importance = req.getIsStar() == true ? 1 : 0;
session.getPlayer().replaceMailByIndex(mailId, message);
updatedMail.add(message);
}
session.send(new PacketMailChangeNotify(session.getPlayer(), updatedMail));
}
}
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.GenshinEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify; import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
...@@ -12,40 +12,40 @@ import emu.grasscutter.server.game.GameSession; ...@@ -12,40 +12,40 @@ import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.CombatInvocationsNotify) @Opcodes(PacketOpcodes.CombatInvocationsNotify)
public class HandlerCombatInvocationsNotify extends PacketHandler { public class HandlerCombatInvocationsNotify extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload); CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload);
for (CombatInvokeEntry entry : notif.getInvokeListList()) { for (CombatInvokeEntry entry : notif.getInvokeListList()) {
switch (entry.getArgumentType()) { switch (entry.getArgumentType()) {
case CombatEvtBeingHit: case COMBAT_EVT_BEING_HIT:
// Handle damage // Handle damage
EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData()); EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData());
session.getPlayer().getScene().handleAttack(hitInfo.getAttackResult()); session.getPlayer().getAttackResults().add(hitInfo.getAttackResult());
break; break;
case EntityMove: case ENTITY_MOVE:
// Handle movement // Handle movement
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData()); EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
GenshinEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId()); GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
if (entity != null) { if (entity != null) {
entity.getPosition().set(moveInfo.getMotionInfo().getPos()); session.getPlayer().getMovementManager().handle(session, moveInfo, entity);
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
entity.setMotionState(moveInfo.getMotionInfo().getState());
} }
break; break;
default: default:
break; break;
} }
session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry); session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry);
} }
if (notif.getInvokeListList().size() > 0) { if (notif.getInvokeListList().size() > 0) {
session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); session.getPlayer().getCombatInvokeHandler().update(session.getPlayer());
} }
// Handle attack results last
while (!session.getPlayer().getAttackResults().isEmpty()) {
session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll());
}
} }
} }
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.def.CombineData;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.CombineReqOuterClass;
import emu.grasscutter.net.proto.ItemParamOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketCombineRsp;
import java.util.List;
import java.util.stream.Collectors;
@Opcodes(PacketOpcodes.CombineReq)
public class HandlerCombineReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
CombineReqOuterClass.CombineReq req = CombineReqOuterClass.CombineReq.parseFrom(payload);
var result = session.getServer().getCombineManger()
.combineItem(session.getPlayer(), req.getCombineId(), req.getCombineCount());
if(result == null){
return;
}
session.send(new PacketCombineRsp(req,
toItemParamList(result.getMaterial()),
toItemParamList(result.getResult()),
toItemParamList(result.getExtra()),
toItemParamList(result.getBack()),
toItemParamList(result.getBack())));
}
private List<ItemParamOuterClass.ItemParam> toItemParamList(List<CombineData.CombineItemPair> list){
return list.stream()
.map(item -> ItemParamOuterClass.ItemParam.newBuilder()
.setItemId(item.getId())
.setCount(item.getCount())
.build())
.collect(Collectors.toList());
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DelMailReqOuterClass;
import emu.grasscutter.net.proto.DeleteFriendReqOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketDelMailRsp;
@Opcodes(PacketOpcodes.DelMailReq)
public class HandlerDelMailReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DelMailReqOuterClass.DelMailReq req = DelMailReqOuterClass.DelMailReq.parseFrom(payload);
session.getPlayer().getMailHandler().deleteMail(req.getMailIdListList());
}
}
...@@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.recv; ...@@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DungeonEntryInfoReqOuterClass.DungeonEntryInfoReq;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
...@@ -10,7 +11,9 @@ public class HandlerDungeonEntryInfoReq extends PacketHandler { ...@@ -10,7 +11,9 @@ public class HandlerDungeonEntryInfoReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload);
session.getServer().getDungeonManager().getEntryInfo(session.getPlayer(), req.getPointId());
} }
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment