mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-25 04:19:40 +00:00
Merge branch 'Grasscutters:development' into development
This commit is contained in:
commit
584aec58a5
@ -28,7 +28,7 @@ EN | [中文](README_zh-CN.md) | [FR](README_fr-FR.md)
|
|||||||
|
|
||||||
**Note:** If you just want to **run it**, then **jre** only is fine.
|
**Note:** If you just want to **run it**, then **jre** only is fine.
|
||||||
|
|
||||||
* MongoDB (recommended 4.0+)
|
* [MongoDB](https://www.mongodb.com/try/download/community) (recommended 4.0+)
|
||||||
|
|
||||||
* Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc.
|
* Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc.
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
**注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **jre** 便足够了
|
**注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **jre** 便足够了
|
||||||
|
|
||||||
* MongoDB (推荐 4.0+)
|
* [MongoDB](https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-5.0.9-signed.msi) (推荐 4.0+)
|
||||||
|
|
||||||
* Proxy daemon: mitmproxy (推荐使用mitmdump), Fiddler Classic, 等
|
* Proxy daemon: mitmproxy (推荐使用mitmdump), Fiddler Classic, 等
|
||||||
|
|
||||||
|
Binary file not shown.
BIN
lib/kcp.jar
Normal file
BIN
lib/kcp.jar
Normal file
Binary file not shown.
@ -83,33 +83,33 @@ public final class DatabaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Account getAccountByName(String username) {
|
public static Account getAccountByName(String username) {
|
||||||
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("username", username)).first();
|
return DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("username", username)).first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Account getAccountByToken(String token) {
|
public static Account getAccountByToken(String token) {
|
||||||
if(token == null) return null;
|
if(token == null) return null;
|
||||||
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("token", token)).first();
|
return DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("token", token)).first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Account getAccountBySessionKey(String sessionKey) {
|
public static Account getAccountBySessionKey(String sessionKey) {
|
||||||
if(sessionKey == null) return null;
|
if(sessionKey == null) return null;
|
||||||
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first();
|
return DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Account getAccountById(String uid) {
|
public static Account getAccountById(String uid) {
|
||||||
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first();
|
return DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Account getAccountByPlayerId(int playerId) {
|
public static Account getAccountByPlayerId(int playerId) {
|
||||||
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", playerId)).first();
|
return DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", playerId)).first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean checkIfAccountExists(String name) {
|
public static boolean checkIfAccountExists(String name) {
|
||||||
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("username", name)).count() > 0;
|
return DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("username", name)).count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean checkIfAccountExists(int reservedUid) {
|
public static boolean checkIfAccountExists(int reservedUid) {
|
||||||
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", reservedUid)).count() > 0;
|
return DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", reservedUid)).count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteAccount(Account target) {
|
public static void deleteAccount(Account target) {
|
||||||
@ -141,7 +141,7 @@ public final class DatabaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally, delete the account itself.
|
// Finally, delete the account itself.
|
||||||
DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete();
|
DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Player> getAllPlayers() {
|
public static List<Player> getAllPlayers() {
|
||||||
|
@ -16,6 +16,7 @@ import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlo
|
|||||||
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
|
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
|
||||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
||||||
|
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
|
||||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
||||||
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||||
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
|
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
|
||||||
@ -31,6 +32,7 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
|||||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import emu.grasscutter.utils.ProtoHelper;
|
import emu.grasscutter.utils.ProtoHelper;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
@ -108,13 +110,13 @@ public class EntityAvatar extends GameEntity {
|
|||||||
public void onDeath(int killerId) {
|
public void onDeath(int killerId) {
|
||||||
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
|
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
|
||||||
this.killedBy = killerId;
|
this.killedBy = killerId;
|
||||||
clearEnergy(PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER);
|
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDeath(PlayerDieType dieType, int killerId) {
|
public void onDeath(PlayerDieType dieType, int killerId) {
|
||||||
this.killedType = dieType;
|
this.killedType = dieType;
|
||||||
this.killedBy = killerId;
|
this.killedBy = killerId;
|
||||||
clearEnergy(PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER);
|
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -130,14 +132,25 @@ public class EntityAvatar extends GameEntity {
|
|||||||
return healed;
|
return healed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearEnergy(PropChangeReason reason) {
|
public void clearEnergy(ChangeEnergyReason reason) {
|
||||||
|
// Fight props.
|
||||||
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||||
this.avatar.setCurrentEnergy(curEnergyProp, 0);
|
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
|
||||||
|
|
||||||
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
|
|
||||||
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, 0f, reason));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Get max energy.
|
||||||
|
float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
|
||||||
|
|
||||||
|
// Set energy to zero.
|
||||||
|
this.avatar.setCurrentEnergy(curEnergyProp, 0);
|
||||||
|
|
||||||
|
// Send packets.
|
||||||
|
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
|
||||||
|
|
||||||
|
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
|
||||||
|
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addEnergy(float amount, PropChangeReason reason) {
|
public void addEnergy(float amount, PropChangeReason reason) {
|
||||||
this.addEnergy(amount, reason, false);
|
this.addEnergy(amount, reason, false);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import emu.grasscutter.net.proto.GachaItemOuterClass.GachaItem;
|
|||||||
import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem;
|
import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem;
|
||||||
import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp;
|
import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp;
|
||||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||||
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
import emu.grasscutter.server.game.GameServer;
|
import emu.grasscutter.server.game.GameServer;
|
||||||
import emu.grasscutter.server.game.GameServerTickEvent;
|
import emu.grasscutter.server.game.GameServerTickEvent;
|
||||||
import emu.grasscutter.server.packet.send.PacketDoGachaRsp;
|
import emu.grasscutter.server.packet.send.PacketDoGachaRsp;
|
||||||
@ -244,7 +245,7 @@ public class GachaManager {
|
|||||||
}
|
}
|
||||||
Inventory inventory = player.getInventory();
|
Inventory inventory = player.getInventory();
|
||||||
if (inventory.getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > inventory.getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) {
|
if (inventory.getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > inventory.getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) {
|
||||||
player.sendPacket(new PacketDoGachaRsp());
|
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_ITEM_EXCEED_LIMIT));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.Ability
|
|||||||
import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier;
|
import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier;
|
||||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||||
|
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
|
||||||
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
||||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
@ -334,7 +335,7 @@ public class EnergyManager {
|
|||||||
|
|
||||||
// If the cast skill was a burst, consume energy.
|
// If the cast skill was a burst, consume energy.
|
||||||
if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) {
|
if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) {
|
||||||
avatar.getAsEntity().clearEnergy(PropChangeReason.PROP_CHANGE_REASON_ABILITY);
|
avatar.getAsEntity().clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
package emu.grasscutter.netty;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import io.jpower.kcp.netty.UkcpChannel;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.ByteBufUtil;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
||||||
|
|
||||||
public abstract class KcpChannel extends ChannelInboundHandlerAdapter {
|
|
||||||
private UkcpChannel kcpChannel;
|
|
||||||
private ChannelHandlerContext ctx;
|
|
||||||
private boolean isActive;
|
|
||||||
|
|
||||||
public UkcpChannel getChannel() {
|
|
||||||
return kcpChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isActive() {
|
|
||||||
return this.isActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
|
||||||
this.kcpChannel = (UkcpChannel) ctx.channel();
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.isActive = true;
|
|
||||||
|
|
||||||
this.onConnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
||||||
this.isActive = false;
|
|
||||||
|
|
||||||
this.onDisconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
|
||||||
ByteBuf data = (ByteBuf) msg;
|
|
||||||
onMessage(ctx, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
|
||||||
ctx.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|
||||||
cause.printStackTrace();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void send(byte[] data) {
|
|
||||||
if (!isActive()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ByteBuf packet = Unpooled.wrappedBuffer(data);
|
|
||||||
kcpChannel.writeAndFlush(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
if (getChannel() != null) {
|
|
||||||
getChannel().close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
protected void logPacket(ByteBuffer buf) {
|
|
||||||
ByteBuf b = Unpooled.wrappedBuffer(buf.array());
|
|
||||||
logPacket(b);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected void logPacket(ByteBuf buf) {
|
|
||||||
Grasscutter.getLogger().info("Received: \n" + ByteBufUtil.prettyHexDump(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Events
|
|
||||||
|
|
||||||
protected abstract void onConnect();
|
|
||||||
|
|
||||||
protected abstract void onDisconnect();
|
|
||||||
|
|
||||||
public abstract void onMessage(ChannelHandlerContext ctx, ByteBuf data);
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
package emu.grasscutter.netty;
|
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.nio.channels.SelectableChannel;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelConfig;
|
|
||||||
import io.netty.channel.ChannelMetadata;
|
|
||||||
import io.netty.channel.ChannelOutboundBuffer;
|
|
||||||
import io.netty.channel.nio.AbstractNioMessageChannel;
|
|
||||||
|
|
||||||
public class KcpHandshaker extends AbstractNioMessageChannel {
|
|
||||||
|
|
||||||
protected KcpHandshaker(Channel parent, SelectableChannel ch, int readInterestOp) {
|
|
||||||
super(parent, ch, readInterestOp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChannelConfig config() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActive() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChannelMetadata metadata() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int doReadMessages(List<Object> buf) throws Exception {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doFinishConnect() throws Exception {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SocketAddress localAddress0() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SocketAddress remoteAddress0() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doDisconnect() throws Exception {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
package emu.grasscutter.netty;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import io.jpower.kcp.netty.ChannelOptionHelper;
|
|
||||||
import io.jpower.kcp.netty.UkcpChannelOption;
|
|
||||||
import io.jpower.kcp.netty.UkcpServerChannel;
|
|
||||||
import io.netty.bootstrap.UkcpServerBootstrap;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelInitializer;
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public class KcpServer extends Thread {
|
|
||||||
private EventLoopGroup group;
|
|
||||||
private UkcpServerBootstrap bootstrap;
|
|
||||||
|
|
||||||
private ChannelInitializer serverInitializer;
|
|
||||||
private InetSocketAddress address;
|
|
||||||
|
|
||||||
public KcpServer(InetSocketAddress address) {
|
|
||||||
this.address = address;
|
|
||||||
this.setName("Netty Server Thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
public InetSocketAddress getAddress() {
|
|
||||||
return this.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChannelInitializer getServerInitializer() {
|
|
||||||
return serverInitializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setServerInitializer(ChannelInitializer serverInitializer) {
|
|
||||||
this.serverInitializer = serverInitializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (getServerInitializer() == null) {
|
|
||||||
this.setServerInitializer(new KcpServerInitializer());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
group = new NioEventLoopGroup();
|
|
||||||
bootstrap = new UkcpServerBootstrap();
|
|
||||||
bootstrap.group(group)
|
|
||||||
.channel(UkcpServerChannel.class)
|
|
||||||
.childHandler(this.getServerInitializer());
|
|
||||||
ChannelOptionHelper
|
|
||||||
.nodelay(bootstrap, true, 20, 2, true)
|
|
||||||
.childOption(UkcpChannelOption.UKCP_MTU, 1200);
|
|
||||||
|
|
||||||
// Start handler
|
|
||||||
this.onStart();
|
|
||||||
|
|
||||||
// Start the server.
|
|
||||||
ChannelFuture f = bootstrap.bind(getAddress()).sync();
|
|
||||||
|
|
||||||
// Start finish handler
|
|
||||||
this.onStartFinish();
|
|
||||||
|
|
||||||
// Wait until the server socket is closed.
|
|
||||||
f.channel().closeFuture().sync();
|
|
||||||
} catch (Exception exception) {
|
|
||||||
Grasscutter.getLogger().error("Unable to start game server.", exception);
|
|
||||||
} finally {
|
|
||||||
// Close
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStart() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStartFinish() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finish() {
|
|
||||||
try {
|
|
||||||
group.shutdownGracefully();
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
Grasscutter.getLogger().info("Game Server closed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
package emu.grasscutter.netty;
|
|
||||||
|
|
||||||
import io.jpower.kcp.netty.UkcpChannel;
|
|
||||||
import io.netty.channel.ChannelInitializer;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class KcpServerInitializer extends ChannelInitializer<UkcpChannel> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initChannel(UkcpChannel ch) throws Exception {
|
|
||||||
ChannelPipeline pipeline = ch.pipeline();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -21,13 +21,13 @@ import emu.grasscutter.game.tower.TowerScheduleManager;
|
|||||||
import emu.grasscutter.game.world.World;
|
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.KcpServer;
|
|
||||||
import emu.grasscutter.server.event.types.ServerEvent;
|
import emu.grasscutter.server.event.types.ServerEvent;
|
||||||
import emu.grasscutter.server.event.game.ServerTickEvent;
|
import emu.grasscutter.server.event.game.ServerTickEvent;
|
||||||
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
||||||
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
||||||
import emu.grasscutter.task.TaskMap;
|
import emu.grasscutter.task.TaskMap;
|
||||||
import emu.grasscutter.BuildConfig;
|
import kcp.highway.ChannelConfig;
|
||||||
|
import kcp.highway.KcpServer;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
@ -60,7 +60,7 @@ public final class GameServer extends KcpServer {
|
|||||||
private final TowerScheduleManager towerScheduleManager;
|
private final TowerScheduleManager towerScheduleManager;
|
||||||
|
|
||||||
private static InetSocketAddress getAdapterInetSocketAddress(){
|
private static InetSocketAddress getAdapterInetSocketAddress(){
|
||||||
InetSocketAddress inetSocketAddress = null;
|
InetSocketAddress inetSocketAddress;
|
||||||
if(GAME_INFO.bindAddress.equals("")){
|
if(GAME_INFO.bindAddress.equals("")){
|
||||||
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
|
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
|
||||||
}else{
|
}else{
|
||||||
@ -75,9 +75,17 @@ public final class GameServer extends KcpServer {
|
|||||||
this(getAdapterInetSocketAddress());
|
this(getAdapterInetSocketAddress());
|
||||||
}
|
}
|
||||||
public GameServer(InetSocketAddress address) {
|
public GameServer(InetSocketAddress address) {
|
||||||
super(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);
|
||||||
|
|
||||||
|
this.init(GameSessionManager.getListener(),channelConfig,address);
|
||||||
|
|
||||||
this.setServerInitializer(new GameServerInitializer(this));
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
||||||
this.questHandler = new ServerQuestHandler();
|
this.questHandler = new ServerQuestHandler();
|
||||||
@ -96,6 +104,7 @@ public final class GameServer extends KcpServer {
|
|||||||
this.expeditionManager = new ExpeditionManager(this);
|
this.expeditionManager = new ExpeditionManager(this);
|
||||||
this.combineManger = new CombineManger(this);
|
this.combineManger = new CombineManger(this);
|
||||||
this.towerScheduleManager = new TowerScheduleManager(this);
|
this.towerScheduleManager = new TowerScheduleManager(this);
|
||||||
|
|
||||||
// Hook into shutdown event.
|
// Hook into shutdown event.
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
||||||
}
|
}
|
||||||
@ -164,6 +173,7 @@ public final class GameServer extends KcpServer {
|
|||||||
return towerScheduleManager;
|
return towerScheduleManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TaskMap getTaskMap() {
|
public TaskMap getTaskMap() {
|
||||||
return this.taskMap;
|
return this.taskMap;
|
||||||
}
|
}
|
||||||
@ -220,23 +230,23 @@ public final class GameServer extends KcpServer {
|
|||||||
}
|
}
|
||||||
return DatabaseHelper.getAccountByName(username);
|
return DatabaseHelper.getAccountByName(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTick() throws Exception {
|
public synchronized void onTick(){
|
||||||
Iterator<World> it = this.getWorlds().iterator();
|
Iterator<World> it = this.getWorlds().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
World world = it.next();
|
World world = it.next();
|
||||||
|
|
||||||
if (world.getPlayerCount() == 0) {
|
if (world.getPlayerCount() == 0) {
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onTick();
|
world.onTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Player player : this.getPlayers().values()) {
|
for (Player player : this.getPlayers().values()) {
|
||||||
player.onTick();
|
player.onTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerTickEvent event = new ServerTickEvent(); event.call();
|
ServerTickEvent event = new ServerTickEvent(); event.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,8 +259,7 @@ public final class GameServer extends KcpServer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void start() {
|
||||||
public synchronized void start() {
|
|
||||||
// Schedule game loop.
|
// Schedule game loop.
|
||||||
Timer gameLoop = new Timer();
|
Timer gameLoop = new Timer();
|
||||||
gameLoop.scheduleAtFixedRate(new TimerTask() {
|
gameLoop.scheduleAtFixedRate(new TimerTask() {
|
||||||
@ -263,24 +272,19 @@ public final class GameServer extends KcpServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, new Date(), 1000L);
|
}, new Date(), 1000L);
|
||||||
|
|
||||||
super.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartFinish() {
|
|
||||||
Grasscutter.getLogger().info(translate("messages.status.free_software"));
|
Grasscutter.getLogger().info(translate("messages.status.free_software"));
|
||||||
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
|
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
|
||||||
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
|
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
|
||||||
|
event.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServerShutdown() {
|
public void onServerShutdown() {
|
||||||
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
|
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
|
||||||
|
|
||||||
// Kick and save all players
|
// Kick and save all players
|
||||||
List<Player> 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 (Player player : list) {
|
for (Player player : list) {
|
||||||
player.getSession().close();
|
player.getSession().close();
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package emu.grasscutter.server.game;
|
|
||||||
|
|
||||||
import emu.grasscutter.netty.KcpServerInitializer;
|
|
||||||
import io.jpower.kcp.netty.UkcpChannel;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
|
|
||||||
public class GameServerInitializer extends KcpServerInitializer {
|
|
||||||
private GameServer server;
|
|
||||||
|
|
||||||
public GameServerInitializer(GameServer server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initChannel(UkcpChannel ch) throws Exception {
|
|
||||||
ChannelPipeline pipeline=null;
|
|
||||||
if(ch!=null){
|
|
||||||
pipeline = ch.pipeline();
|
|
||||||
}
|
|
||||||
new GameSession(server,pipeline);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,9 +2,6 @@ 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.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
@ -14,23 +11,19 @@ import emu.grasscutter.game.player.Player;
|
|||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodesUtil;
|
import emu.grasscutter.net.packet.PacketOpcodesUtil;
|
||||||
import emu.grasscutter.netty.KcpChannel;
|
|
||||||
import emu.grasscutter.server.event.game.SendPacketEvent;
|
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;
|
||||||
import io.jpower.kcp.netty.UkcpChannel;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
|
||||||
import static emu.grasscutter.Configuration.*;
|
import static emu.grasscutter.Configuration.*;
|
||||||
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
public class GameSession extends KcpChannel {
|
public class GameSession implements GameSessionManager.KcpChannel {
|
||||||
private final GameServer server;
|
private final GameServer server;
|
||||||
|
private GameSessionManager.KcpTunnel tunnel;
|
||||||
|
|
||||||
private Account account;
|
private Account account;
|
||||||
private Player player;
|
private Player player;
|
||||||
|
|
||||||
@ -41,29 +34,10 @@ public class GameSession extends KcpChannel {
|
|||||||
private long lastPingTime;
|
private long lastPingTime;
|
||||||
private int lastClientSeq = 10;
|
private int lastClientSeq = 10;
|
||||||
|
|
||||||
private final ChannelPipeline pipeline;
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
setState(SessionState.INACTIVE);
|
|
||||||
//send disconnection pack in case of reconnection
|
|
||||||
try {
|
|
||||||
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
|
|
||||||
}catch (Throwable ignore){
|
|
||||||
|
|
||||||
}
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
public GameSession(GameServer server) {
|
public GameSession(GameServer server) {
|
||||||
this(server,null);
|
|
||||||
}
|
|
||||||
public GameSession(GameServer server, ChannelPipeline pipeline) {
|
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.state = SessionState.WAITING_FOR_TOKEN;
|
this.state = SessionState.WAITING_FOR_TOKEN;
|
||||||
this.lastPingTime = System.currentTimeMillis();
|
this.lastPingTime = System.currentTimeMillis();
|
||||||
this.pipeline = pipeline;
|
|
||||||
if(pipeline!=null) {
|
|
||||||
pipeline.addLast(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameServer getServer() {
|
public GameServer getServer() {
|
||||||
@ -71,10 +45,11 @@ public class GameSession extends KcpChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public InetSocketAddress getAddress() {
|
public InetSocketAddress getAddress() {
|
||||||
if (this.getChannel() == null) {
|
try{
|
||||||
|
return tunnel.getAddress();
|
||||||
|
}catch (Throwable ignore){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.getChannel().remoteAddress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean useSecretKey() {
|
public boolean useSecretKey() {
|
||||||
@ -135,37 +110,7 @@ public class GameSession extends KcpChannel {
|
|||||||
public int getNextClientSequence() {
|
public int getNextClientSequence() {
|
||||||
return ++lastClientSeq;
|
return ++lastClientSeq;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onConnect() {
|
|
||||||
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().getHostString().toLowerCase()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected synchronized void onDisconnect() { // Synchronize so we don't add character at the same time.
|
|
||||||
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().getHostString().toLowerCase()));
|
|
||||||
|
|
||||||
// Set state so no more packets can be handled
|
|
||||||
this.setState(SessionState.INACTIVE);
|
|
||||||
|
|
||||||
// Save after disconnecting
|
|
||||||
if (this.isLoggedIn()) {
|
|
||||||
Player player = getPlayer();
|
|
||||||
// Call logout event.
|
|
||||||
player.onLogout();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
pipeline.remove(this);
|
|
||||||
} catch (Throwable ignore) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void logPacket(ByteBuffer buf) {
|
|
||||||
ByteBuf b = Unpooled.wrappedBuffer(buf.array());
|
|
||||||
logPacket(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replayPacket(int opcode, String name) {
|
public void replayPacket(int opcode, String name) {
|
||||||
String filePath = PACKET(name);
|
String filePath = PACKET(name);
|
||||||
File p = new File(filePath);
|
File p = new File(filePath);
|
||||||
@ -200,13 +145,16 @@ public class GameSession extends KcpChannel {
|
|||||||
|
|
||||||
// Log
|
// Log
|
||||||
if (SERVER.debugLevel == ServerDebugMode.ALL) {
|
if (SERVER.debugLevel == ServerDebugMode.ALL) {
|
||||||
logPacket(packet);
|
if (!loopPacket.contains(packet.getOpcode())) {
|
||||||
|
Grasscutter.getLogger().info("SEND: " + PacketOpcodesUtil.getOpcodeName(packet.getOpcode()) + " (" + packet.getOpcode() + ")");
|
||||||
|
System.out.println(Utils.bytesToHex(packet.getData()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke event.
|
// Invoke event.
|
||||||
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
|
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
|
||||||
if(!event.isCanceled()) // If event is not cancelled, continue.
|
if(!event.isCanceled()) { // If event is not cancelled, continue.
|
||||||
this.send(event.getPacket().build());
|
tunnel.writeData(event.getPacket().build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Set<Integer> loopPacket = Set.of(
|
private static final Set<Integer> loopPacket = Set.of(
|
||||||
@ -217,78 +165,104 @@ public class GameSession extends KcpChannel {
|
|||||||
PacketOpcodes.QueryPathReq
|
PacketOpcodes.QueryPathReq
|
||||||
);
|
);
|
||||||
|
|
||||||
private void logPacket(BasePacket packet) {
|
@Override
|
||||||
if (!loopPacket.contains(packet.getOpcode())) {
|
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
|
||||||
Grasscutter.getLogger().info("SEND: " + PacketOpcodesUtil.getOpcodeName(packet.getOpcode()) + " (" + packet.getOpcode() + ")");
|
this.tunnel = tunnel;
|
||||||
System.out.println(Utils.bytesToHex(packet.getData()));
|
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(ChannelHandlerContext ctx, ByteBuf data) {
|
public void handleReceive(byte[] bytes) {
|
||||||
// Decrypt and turn back into a packet
|
// Decrypt and turn back into a packet
|
||||||
byte[] byteData = Utils.byteBufToArray(data);
|
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
|
||||||
Crypto.xor(byteData, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
|
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
|
||||||
ByteBuf packet = Unpooled.wrappedBuffer(byteData);
|
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
//logPacket(packet);
|
//logPacket(packet);
|
||||||
|
|
||||||
// Handle
|
// Handle
|
||||||
try {
|
try {
|
||||||
|
boolean allDebug = SERVER.debugLevel == ServerDebugMode.ALL;
|
||||||
while (packet.readableBytes() > 0) {
|
while (packet.readableBytes() > 0) {
|
||||||
// Length
|
// Length
|
||||||
if (packet.readableBytes() < 12) {
|
if (packet.readableBytes() < 12) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packet sanity check
|
// Packet sanity check
|
||||||
int const1 = packet.readShort();
|
int const1 = packet.readShort();
|
||||||
if (const1 != 17767) {
|
if (const1 != 17767) {
|
||||||
|
if(allDebug){
|
||||||
|
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
|
||||||
|
}
|
||||||
return; // Bad packet
|
return; // Bad packet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
int opcode = packet.readShort();
|
int opcode = packet.readShort();
|
||||||
int headerLength = packet.readShort();
|
int headerLength = packet.readShort();
|
||||||
int payloadLength = packet.readInt();
|
int payloadLength = packet.readInt();
|
||||||
|
|
||||||
byte[] header = new byte[headerLength];
|
byte[] header = new byte[headerLength];
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
|
|
||||||
packet.readBytes(header);
|
packet.readBytes(header);
|
||||||
packet.readBytes(payload);
|
packet.readBytes(payload);
|
||||||
|
|
||||||
// Sanity check #2
|
// Sanity check #2
|
||||||
int const2 = packet.readShort();
|
int const2 = packet.readShort();
|
||||||
if (const2 != -30293) {
|
if (const2 != -30293) {
|
||||||
|
if(allDebug){
|
||||||
|
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
|
||||||
|
}
|
||||||
return; // Bad packet
|
return; // Bad packet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log packet
|
// Log packet
|
||||||
if (SERVER.debugLevel == ServerDebugMode.ALL) {
|
if (allDebug) {
|
||||||
if (!loopPacket.contains(opcode)) {
|
if (!loopPacket.contains(opcode)) {
|
||||||
Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")");
|
Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")");
|
||||||
System.out.println(Utils.bytesToHex(payload));
|
System.out.println(Utils.bytesToHex(payload));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle
|
// Handle
|
||||||
getServer().getPacketHandler().handle(this, opcode, header, payload);
|
getServer().getPacketHandler().handle(this, opcode, header, payload);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
data.release();
|
//byteBuf.release(); //Needn't
|
||||||
packet.release();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
tunnel.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return getState() == SessionState.ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
public enum SessionState {
|
public enum SessionState {
|
||||||
INACTIVE,
|
INACTIVE,
|
||||||
WAITING_FOR_TOKEN,
|
WAITING_FOR_TOKEN,
|
||||||
WAITING_FOR_LOGIN,
|
WAITING_FOR_LOGIN,
|
||||||
PICKING_CHARACTER,
|
PICKING_CHARACTER,
|
||||||
ACTIVE;
|
ACTIVE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
package emu.grasscutter.server.game;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.utils.Crypto;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.DefaultEventLoop;
|
||||||
|
import kcp.highway.KcpListener;
|
||||||
|
import kcp.highway.Ukcp;
|
||||||
|
|
||||||
|
public class GameSessionManager {
|
||||||
|
private static final DefaultEventLoop logicThread = new DefaultEventLoop();
|
||||||
|
private static final ConcurrentHashMap<Ukcp,GameSession> sessions = new ConcurrentHashMap<>();
|
||||||
|
private static final KcpListener listener = new KcpListener(){
|
||||||
|
@Override
|
||||||
|
public void onConnected(Ukcp ukcp) {
|
||||||
|
int times = 0;
|
||||||
|
GameServer server = Grasscutter.getGameServer();
|
||||||
|
while (server==null){//Waiting server to establish
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
ukcp.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(times++>5){
|
||||||
|
Grasscutter.getLogger().error("Service is not available!");
|
||||||
|
ukcp.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server = Grasscutter.getGameServer();
|
||||||
|
}
|
||||||
|
GameSession conversation = new GameSession(server);
|
||||||
|
conversation.onConnected(new KcpTunnel(){
|
||||||
|
@Override
|
||||||
|
public InetSocketAddress getAddress() {
|
||||||
|
return ukcp.user().getRemoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeData(byte[] bytes) {
|
||||||
|
ByteBuf buf = Unpooled.wrappedBuffer(bytes);
|
||||||
|
ukcp.write(buf);
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
ukcp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSrtt() {
|
||||||
|
return ukcp.srtt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sessions.put(ukcp,conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleReceive(ByteBuf buf, Ukcp kcp) {
|
||||||
|
byte[] byteData = Utils.byteBufToArray(buf);
|
||||||
|
logicThread.execute(() -> {
|
||||||
|
try {
|
||||||
|
GameSession conversation = sessions.get(kcp);
|
||||||
|
if(conversation!=null) {
|
||||||
|
conversation.handleReceive(byteData);
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleException(Throwable ex, Ukcp ukcp) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleClose(Ukcp ukcp) {
|
||||||
|
GameSession conversation = sessions.get(ukcp);
|
||||||
|
if(conversation!=null) {
|
||||||
|
conversation.handleClose();
|
||||||
|
sessions.remove(ukcp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static KcpListener getListener() {
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KcpTunnel{
|
||||||
|
InetSocketAddress getAddress();
|
||||||
|
void writeData(byte[] bytes);
|
||||||
|
void close();
|
||||||
|
int getSrtt();
|
||||||
|
}
|
||||||
|
interface KcpChannel{
|
||||||
|
void onConnected(KcpTunnel tunnel);
|
||||||
|
void handleClose();
|
||||||
|
void handleReceive(byte[] bytes);
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import emu.grasscutter.net.packet.BasePacket;
|
|||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.DoGachaRspOuterClass.DoGachaRsp;
|
import emu.grasscutter.net.proto.DoGachaRspOuterClass.DoGachaRsp;
|
||||||
import emu.grasscutter.net.proto.GachaItemOuterClass.GachaItem;
|
import emu.grasscutter.net.proto.GachaItemOuterClass.GachaItem;
|
||||||
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||||
|
|
||||||
public class PacketDoGachaRsp extends BasePacket {
|
public class PacketDoGachaRsp extends BasePacket {
|
||||||
@ -42,4 +43,14 @@ public class PacketDoGachaRsp extends BasePacket {
|
|||||||
|
|
||||||
this.setData(p);
|
this.setData(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketDoGachaRsp(Retcode retcode) {
|
||||||
|
super(PacketOpcodes.DoGachaRsp);
|
||||||
|
|
||||||
|
DoGachaRsp p = DoGachaRsp.newBuilder()
|
||||||
|
.setRetcode(retcode.getNumber())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import emu.grasscutter.game.entity.GameEntity;
|
|||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
|
||||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
||||||
import emu.grasscutter.net.proto.EntityFightPropChangeReasonNotifyOuterClass.EntityFightPropChangeReasonNotify;
|
import emu.grasscutter.net.proto.EntityFightPropChangeReasonNotifyOuterClass.EntityFightPropChangeReasonNotify;
|
||||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||||
@ -55,4 +56,17 @@ public class PacketEntityFightPropChangeReasonNotify extends BasePacket {
|
|||||||
|
|
||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, ChangeEnergyReason reason) {
|
||||||
|
super(PacketOpcodes.EntityFightPropChangeReasonNotify);
|
||||||
|
|
||||||
|
EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder()
|
||||||
|
.setEntityId(entity.getId())
|
||||||
|
.setPropType(prop.getId())
|
||||||
|
.setPropDelta(value)
|
||||||
|
.setChangeEnergyReson(reason)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user