iohao / ioGame

无锁异步化、事件驱动架构设计的 java netty 网络编程框架; 轻量级,无需依赖任何第三方中间件或数据库就能支持集群、分布式; 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; 通过 ioGame 你可以很容易的搭建出一个集群无中心节点、集群自动化、分布式的网络服务器;FXGL、Unity、UE、Cocos Creator、Godot、Netty、Protobuf、webSocket、tcp、socket;java Netty 游戏服务器框架;
http://game.iohao.com
GNU Affero General Public License v3.0
886 stars 192 forks source link

protobuf 序列化反序列化前后端不一致问题 #186

Closed zglbig closed 1 year ago

zglbig commented 1 year ago

我用的是unity3d客户端,现在碰到个问题就是如果一个协议对象里面的字段值都是初始值的话那么客户端发送到服务端入参的时候这个参数对象是 null 值 比如我有一个api

public class Vector3{
    int x;
    int y;
    int z;
}

如果这个协议对象的 x,y,z 的值都等于 0 的话发送请求到服务端 那么这个对象是 null 值 而不是一个 x,y,z 都等于 0 的对象

     @ActionMethod(1)
     public Vector3 position(FlowContext ctx,Vector3 v3){
        //这个接收到 v3 参数输 null
     }
iohao commented 1 year ago

当使用原生的 protobuf 且都为默认值时,不会传递数据。或者说 ExternalMessage.data = Vector3 与 = null 序列化的字节是一样的。

框架默认使用的是 jprotobuf,当 data 没有数据时,解析出来的是 null;所以在游戏逻辑服中接收的参数为 null。

解决这个问题有两种方法。

方法一,配置参数 Supplier

优点:性能高 缺点:需要手动配置

推荐使用场景,处理类似业务不多的情况可以考虑

MethodParsers.me().mappingParamSupplier(Vector3.class, Vector3::new);

方法二,重写默认解析类 DefaultMethodParser

优点:简单省事 缺点:相比方法一,性能略低

推荐使用场景,喜欢省事的哥们

public class MyDefaultMethodParser implements MethodParser {
   ... ... 省略部分代码

    @Override
    public Object parseParam(byte[] data, ActionCommand.ParamInfo paramInfo) {
       ... ... 省略部分代码

        if (Objects.isNull(data)) {
            try {
                return actualTypeArgumentClazz.getDeclaredConstructor().newInstance();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }

        return DataCodecKit.decode(data, actualTypeArgumentClazz);
    }
}

设置

MethodParsers.me().setMethodParser(new MyDefaultMethodParser());
iohao commented 1 year ago

多说一个方式,通过编解码器的方式

当为 null 时,传递一个空数组

public class MyProtoDataCodec implements DataCodec {
    byte[] EMPTY_BYTES = new byte[0];

    @Override
    public byte[] encode(Object data) {
        return ProtoKit.toBytes(data);
    }

    @Override
    public <T> T decode(byte[] data, Class<?> dataClass) {

        if (Objects.isNull(data)) {
            data = EMPTY_BYTES;
        }

        return (T) ProtoKit.parseProtoByte(data, dataClass);
    }
}

设置

IoGameGlobalSetting.setDataCodec(new MyProtoDataCodec());
iohao commented 1 year ago

参考示例 https://github.com/iohao/ioGame-example/blob/main/example-springboot/spring-game-logic-parent/src/main/java/com/iohao/game/spring/logic/core/MyBarSkeletonConfig.java#L61

iohao commented 1 year ago

尝试使用 17.1.55 版本,已经在 ProtoDataCodec.java 做了增强处理。

zglbig commented 1 year ago

好的大佬 目前使用了 MyProtoDataCodec 方式 因为 17.1.55 貌似拉取不下来

iohao commented 1 year ago

https://github.com/game-town/ioGame/releases/tag/17.1.55