iohao / ioGame

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

模拟玩家请求时 - 从游戏对外服中获取在线玩家相关数据 #265

Closed iohao closed 7 months ago

iohao commented 7 months ago

新增功能的使用场景

从用户(玩家)所在游戏对外服中获取用户自身的数据,如用户所绑定的游戏逻辑服、元信息 ...等。

可用于模拟玩家请求。


使用场景

在模拟玩家请求时,有时需要用到玩家的元信息、已绑定游戏逻辑服 ...等相关信息。

通常,我们会在 GM 后台中模拟的玩家请求,但玩家的元信息、已绑定游戏逻辑服 ...等相关信息是无法知道的。那么就需要扩展 ExternalBizRegion 接口,从游戏对外服中获取相关数据。参考 获取游戏对外服的数据与扩展 (yuque.com)


其他参考:GM 后台与逻辑服交互 (yuque.com),模拟玩家的请求来访问 ioGame 其他逻辑服。


建议

如果业务的场景不复杂(也就是不需要玩家元信息、已绑定游戏逻辑服...等相关信息时),建议使用常规的方式来模拟玩家请求(常规的跨服调用)。

在实际中,使用常规的模拟玩家请求就能满足大部分业务场景的需求了。

iohao commented 7 months ago

使用参考

@Slf4j
@RestController
@RequestMapping("other")
public class OtherController {
    static final AtomicLong msgId = GameManagerController.msgId;
    /** 为了方便测试,这里指定一个 userId 来模拟玩家 */
    static final long userId = GameManagerController.userId;

    @GetMapping("/notice")
    public String notice() {
        log.info("other notice");
        // 使用协议碎片特性 https://www.yuque.com/iohao/game/ieimzn
        StringValue data = StringValue.of("other GM web msg " + msgId.incrementAndGet());
        // 模拟请求 : 路由 - 业务数据
        RequestMessage requestMessage = BarMessageKit.createRequestMessage(ExchangeCmd.of(ExchangeCmd.notice), data);

        // 设置需要模拟的玩家
        HeadMetadata headMetadata = requestMessage.getHeadMetadata();
        headMetadata.setUserId(userId);

        // 从游戏对外服中获取一些用户(玩家的)自身的数据,如元信息、所绑定的游戏逻辑服 ...等
        Optional<HeadMetadata> headMetadataOptional = ExternalCommunicationKit.employHeadMetadata(requestMessage);

        if (headMetadataOptional.isPresent()) {
            // 发起模拟请求
            extractedRequestLogic(requestMessage);

            // 打印从游戏对外服获取的元信息
            byte[] attachmentData = headMetadata.getAttachmentData();
            ExchangeAttachment attachment = DataCodecKit.decode(attachmentData, ExchangeAttachment.class);
            return "other notice 玩家的元信息: %s - %s".formatted(attachment, msgId.get());
        } else {
            return "other notice 玩家 %s 不在线,无法获取玩家的元信息 - %s".formatted(userId, msgId.get());
        }
    }

    private void extractedRequestLogic(RequestMessage requestMessage) {
        // 向逻辑服发送请求,该模拟请求具备了玩家的元信息。
        BrokerClient brokerClient = MyKit.brokerClient;
        InvokeModuleContext invokeModuleContext = brokerClient.getInvokeModuleContext();
        invokeModuleContext.invokeModuleVoidMessage(requestMessage);
    }
}

下面是其他游戏逻辑服提供的 action,我们的模拟请求将会访问这个 action,在 code 12 ~ 13 获取并打印了元信息。

@Slf4j
@ActionController(ExchangeCmd.cmd)
public class ExchangeAction {
    ... 省略部分代码
    @ActionMethod(ExchangeCmd.notice)
    public void internalNotice(String msg, FlowContext flowContext) {
        log.info("通过 GM 后台发送一条消息给玩家 [{}] - 消息内容 [{}]", flowContext.getUserId(), msg);
        // 广播消息给玩家
        // 使用协议碎片特性 https://www.yuque.com/iohao/game/ieimzn
        flowContext.broadcastMe(StringValue.of(msg));

        ExchangeAttachment attachment = flowContext.getAttachment(ExchangeAttachment.class);
        log.info("attachment : {}", attachment);
    }
}

小结

总体来说比较简单,关键代码只有一行

Optional headMetadataOptional = ExternalCommunicationKit.employHeadMetadata(requestMessage);

为模拟请求附加上玩家所在游戏对外服中与自身相关的一些信息。

目前 employHeadMetadata 方法中只为模拟请求添加了元信息、所绑定的游戏逻辑服。此外,headMetadataOptional 中还包括了一些其他的信息,如有需要的可从中获取。参考代码如下

headMetadataOptional.ifPresent(externalHeadMetadata -> {
    // 将用户元信息、所绑定的游戏逻辑服设置到 RequestMessage headMetadata 中
    HeadMetadata headMetadata = requestMessage.getHeadMetadata();
    headMetadata.setBindingLogicServerIds(externalHeadMetadata.getBindingLogicServerIds());
    headMetadata.setAttachmentData(externalHeadMetadata.getAttachmentData());
    ... ...
});

其他参考:GM 后台与逻辑服交互 (yuque.com)

iohao commented 7 months ago

https://github.com/iohao/ioGame/releases/tag/21.6