Grasscutter/src/main/java/emu/grasscutter/server/game/GameSession.java
WetABQ addfb5eb5d [BREAKING CHANGE] proto auto compiled by gradle (#226)
* [BREAK] proto auto compiled by gradle

* [BREAK] move proto to submodule

* update gitmodules

* [BREAK] move proto to submodule

* move proto to submodule

* fix merge conflict

* fix github action after merging

* fix merge conflicts and del submodule

* upload the proto
2022-04-26 14:44:30 -07:00

266 lines
6.7 KiB
Java

package emu.grasscutter.server.game;
import java.io.File;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketOpcodesUtil;
import emu.grasscutter.netty.MihoyoKcpChannel;
import emu.grasscutter.server.event.game.SendPacketEvent;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
public class GameSession extends MihoyoKcpChannel {
private GameServer server;
private Account account;
private GenshinPlayer player;
private boolean useSecretKey;
private SessionState state;
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 GameServer getServer() {
return server;
}
public InetSocketAddress getAddress() {
if (this.getChannel() == null) {
return null;
}
return this.getChannel().remoteAddress();
}
public boolean useSecretKey() {
return useSecretKey;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountId() {
return this.getAccount().getId();
}
public GenshinPlayer getPlayer() {
return player;
}
public synchronized void setPlayer(GenshinPlayer player) {
this.player = player;
this.player.setSession(this);
this.player.setAccount(this.getAccount());
}
public SessionState getState() {
return state;
}
public void setState(SessionState state) {
this.state = state;
}
public boolean isLoggedIn() {
return this.getPlayer() != null;
}
public void setUseSecretKey(boolean useSecretKey) {
this.useSecretKey = useSecretKey;
}
public int getClientTime() {
return this.clientTime;
}
public long getLastPingTime() {
return lastPingTime;
}
public void updateLastPingTime(int clientTime) {
this.clientTime = clientTime;
this.lastPingTime = System.currentTimeMillis();
}
public int getNextClientSequence() {
return ++lastClientSeq;
}
@Override
protected void onConnect() {
Grasscutter.getLogger().info("Client connected from " + getAddress().getHostString().toLowerCase());
}
@Override
protected synchronized void onDisconnect() { // Synchronize so we dont add character at the same time
Grasscutter.getLogger().info("Client disconnected from " + getAddress().getHostString().toLowerCase());
// Set state so no more packets can be handled
this.setState(SessionState.INACTIVE);
// Save after disconnecting
if (this.isLoggedIn()) {
// Save
getPlayer().onLogout();
// Remove from gameserver
getServer().getPlayers().remove(getPlayer().getUid());
}
}
protected void logPacket(ByteBuffer buf) {
ByteBuf b = Unpooled.wrappedBuffer(buf.array());
logPacket(b);
}
public void replayPacket(int opcode, String name) {
String filePath = Grasscutter.getConfig().PACKETS_FOLDER + name;
File p = new File(filePath);
if (!p.exists()) return;
byte[] packet = FileUtils.read(p);
GenshinPacket genshinPacket = new GenshinPacket(opcode);
genshinPacket.setData(packet);
// Log
logPacket(genshinPacket.getOpcode());
send(genshinPacket);
}
public void send(GenshinPacket genshinPacket) {
// Test
if (genshinPacket.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return;
}
// Header
if (genshinPacket.shouldBuildHeader()) {
genshinPacket.buildHeader(this.getNextClientSequence());
}
// Log
if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) {
logPacket(genshinPacket);
}
// Invoke event.
SendPacketEvent event = new SendPacketEvent(this, genshinPacket); 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 static final Set<Integer> loopPacket = Set.of(
PacketOpcodes.PingReq,
PacketOpcodes.PingRsp,
PacketOpcodes.WorldPlayerRTTNotify,
PacketOpcodes.UnionCmdNotify,
PacketOpcodes.QueryPathReq
);
private void logPacket(GenshinPacket genshinPacket) {
if (!loopPacket.contains(genshinPacket.getOpcode())) {
Grasscutter.getLogger().info("SEND: " + PacketOpcodesUtil.getOpcodeName(genshinPacket.getOpcode()) + " (" + genshinPacket.getOpcode() + ")");
System.out.println(Utils.bytesToHex(genshinPacket.getData()));
}
}
@Override
public void onMessage(ChannelHandlerContext ctx, ByteBuf data) {
// Decrypt and turn back into a packet
byte[] byteData = Utils.byteBufToArray(data);
Crypto.xor(byteData, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
ByteBuf packet = Unpooled.wrappedBuffer(byteData);
// Log
//logPacket(packet);
// Handle
try {
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 12) {
return;
}
// Packet sanity check
int const1 = packet.readShort();
if (const1 != 17767) {
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) {
return; // Bad packet
}
// Log packet
if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) {
if (!loopPacket.contains(opcode)) {
Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")");
System.out.println(Utils.bytesToHex(payload));
}
}
// Handle
getServer().getPacketHandler().handle(this, opcode, header, payload);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
packet.release();
}
}
public enum SessionState {
INACTIVE,
WAITING_FOR_TOKEN,
WAITING_FOR_LOGIN,
PICKING_CHARACTER,
ACTIVE;
}
}