mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-17 09:25:06 +01:00
Initial commit
This commit is contained in:
250
src/main/java/emu/grasscutter/server/game/GameSession.java
Normal file
250
src/main/java/emu/grasscutter/server/game/GameSession.java
Normal file
@@ -0,0 +1,250 @@
|
||||
package emu.grasscutter.server.game;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
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.PacketOpcodesUtil;
|
||||
import emu.grasscutter.netty.MihoyoKcpChannel;
|
||||
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().getId());
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// Build packet
|
||||
byte[] data = genshinPacket.build();
|
||||
|
||||
// Log
|
||||
if (Grasscutter.getConfig().LOG_PACKETS) {
|
||||
logPacket(genshinPacket);
|
||||
}
|
||||
|
||||
// Send
|
||||
send(data);
|
||||
}
|
||||
|
||||
private void logPacket(int opcode) {
|
||||
//Grasscutter.getLogger().info("SEND: " + PacketOpcodesUtil.getOpcodeName(opcode));
|
||||
//System.out.println(Utils.bytesToHex(genshinPacket.getData()));
|
||||
}
|
||||
|
||||
private void logPacket(GenshinPacket genshinPacket) {
|
||||
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().LOG_PACKETS) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user