代码初始化

This commit is contained in:
2026-05-07 15:54:43 +08:00
parent a078ca46e5
commit 3522f1011f
274 changed files with 15704 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
# CrossModule
**跨服独立进程**:仅启动 **一个 CmdRpc TCP 服务**(默认 **9200**),处理来源为 **`CommandSource.CROSS_TCP`** 的命令。
区服通过 **`CrossTcpClient`**(在 ServerModule)作为 **TCP 客户端** 连到本进程。
## 进程职责
| 职责 | 说明 |
|------|------|
| 监听 TCP | `CrossTcpServer``ApplicationReadyEvent``bind(cross.tcp.port)` |
| Pipeline | `CrossTcpChannelInitializer``LengthPrefixedProtobuf` 解码 + `CmdRpcTcpFrameHandler` |
| 命令路由 | `CrossGameCommandRegistry` 只注册 `@Cmd(source=CROSS_TCP)` 的 Bean |
| 链路安全(可选) | `CrossLinkAcceptProperties``cross.link.expected-token` 非空时校验注册 token |
## 网络拓扑
```mermaid
flowchart LR
subgraph Zone["ServerModule"]
CC[CrossTcpClient]
end
subgraph Cross["CrossModule :9200"]
XT[CrossTcpServer]
PL[pipeline]
REG[CrossGameCommandRegistry]
end
CC <-->|单 TCP 长连接 长度前缀+CmdRpcEnvelope| XT
XT --> PL --> REG
```
## 入站 Pipeline(与 BaseModule 一致)
```text
Socket
→ LengthFieldBasedFrameDecoder(由 LengthPrefixedProtobuf 配置)
→ CmdRpcTcpFrameHandler
→ CmdRpcChannelDispatchlinkSource = CROSS_TCP
→ GameCommand.execute
→ 编码响应帧写回
```
跨服侧 **`CrossRpcInvoker``null`**:跨服命令里 **不应** 再同步 `roundTrip` 回调区服(Demo 未实现跨服主动调区服)。
## 已实现的 `CROSS_TCP` 命令
### 业务:Service_2 / Method2
- **`CrossForwardCommand`**`AbstractTypedPbCommand`
- 入参:`CrossForwardCts`
- 出参:`CrossForwardStc`
- Demo 逻辑:给 `trace` 加前缀并写入 `cross_time_ms`
### 链路:Service_4(注册 / 心跳)
| method | 命令类 | 作用 |
|--------|--------|------|
| Register | `CrossLinkRegisterCommand` | 校验 `server_id`、可选 `expected-token` |
| Heartbeat | `CrossLinkHeartbeatCommand` | 回显 `client_time_ms`,写入 `cross_time_ms` |
```mermaid
sequenceDiagram
participant Z as 区服 CrossTcpClient
participant X as CrossModule
Z->>X: Service_4 Register + link_seq + CrossLinkRegisterCts
X-->>Z: 同 link_seq + CrossLinkRegisterStc(ok/message)
loop 每 heartbeat-interval-ms
Z->>X: Service_4 Heartbeat + link_seq
X-->>Z: CrossLinkHeartbeatStc
end
Z->>X: Service_2 Method2 + link_seq + CrossForwardCts
X-->>Z: CrossForwardStc
```
## 配置(`src/main/resources/application.yml`
```yaml
cross:
tcp:
port: 9200 # Netty 监听端口
link:
expected-token: "" # 非空则须与区服 cross.token 一致
app:
logging:
dir: logs/CrossModule
```
## 启动
主类:`com.huangzj.cross.CrossModuleApplication`
建议 **先于区服** 启动,否则区服 `CrossTcpClient` 预热与心跳会失败(日志告警),待跨服起来后下次业务或心跳会重连。
## 日志
`logback-spring.xml`:控制台 + 按级别分文件(`info.log` / `error.log`),目录由 `app.logging.dir` 指定。
## 扩展跨服 RPC
1.**ProtoBufModule** 增加 `service/method` 与消息体。
2. 在本模块新增 `@Cmd(source=CROSS_TCP, ...)``GameCommand``AbstractTypedPbCommand`
3. 若需强类型自动解码,使用 **`AbstractTypedPbCommand`**;启动时 **`ProtoRegistryAutoConfigurer`** 会为跨服进程注册对应 `ProtoInfo`
+55
View File
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>huangzj</groupId>
<artifactId>comm-framework</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>CrossModule</artifactId>
<packaging>jar</packaging>
<name>CrossModule</name>
<dependencies>
<dependency>
<groupId>huangzj</groupId>
<artifactId>ProtoBufModule</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>huangzj</groupId>
<artifactId>BaseModule</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.huangzj.cross.CrossModuleApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,18 @@
package com.huangzj.cross;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
/**
* 跨服独立进程:只起 TCP + 命令注册,记得先让它跑起来区服才能 proxy。
*/
@SpringBootApplication(scanBasePackages = "com.huangzj")
@ConfigurationPropertiesScan(basePackages = "com.huangzj")
public class CrossModuleApplication {
/** 轻量进程,一般先于区服启动。 */
public static void main(String[] args) {
SpringApplication.run(CrossModuleApplication.class, args);
}
}
@@ -0,0 +1,26 @@
package com.huangzj.cross.cmd;
import com.huangzj.base.cmd.AbstractTypedPbCommand;
import com.huangzj.base.cmd.Cmd;
import com.huangzj.base.cmd.CommandContext;
import com.huangzj.base.cmd.CommandSource;
import com.huangzj.cmd.gen.CrossForwardCts;
import com.huangzj.cmd.gen.CrossForwardStc;
import com.huangzj.cmd.gen.PbService;
import com.huangzj.cmd.gen.Service2_Method;
import org.springframework.stereotype.Component;
/** 跨服示例:{@link PbService#Service_2} + {@link Service2_Method#Method2}。 */
@Component
@Cmd(
source = CommandSource.CROSS_TCP,
serviceId = PbService.Service_2_VALUE,
methodId = Service2_Method.Method2_VALUE)
public class CrossForwardCommand extends AbstractTypedPbCommand<CrossForwardCts, CrossForwardStc.Builder> {
@Override
protected void run(CommandContext ctx, CrossForwardCts cts, CrossForwardStc.Builder stcBuilder) {
// Demo:给 trace 加前缀便于区分「跨服加工」;生产可在此落库、转发其它服等
stcBuilder.setTrace("cross:" + cts.getTrace()).setCrossTimeMs(System.currentTimeMillis());
}
}
@@ -0,0 +1,18 @@
package com.huangzj.cross.cmd;
import com.huangzj.base.cmd.CommandSource;
import com.huangzj.base.spring.SpringGameCommandRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* 跨服进程只认 {@link CommandSource#CROSS_TCP} 的命令,别的来源不会进这张表。
*/
@Component
public class CrossGameCommandRegistry extends SpringGameCommandRegistry {
/** 启动时扫 Bean,只收集带 {@code @Cmd(source=CROSS_TCP)} 的实现。 */
public CrossGameCommandRegistry(ApplicationContext applicationContext) {
super(applicationContext, CommandSource.CROSS_TCP);
}
}
@@ -0,0 +1,26 @@
package com.huangzj.cross.cmd;
import com.huangzj.base.cmd.AbstractTypedPbCommand;
import com.huangzj.base.cmd.Cmd;
import com.huangzj.base.cmd.CommandContext;
import com.huangzj.base.cmd.CommandSource;
import com.huangzj.cmd.gen.CrossLinkHeartbeatCts;
import com.huangzj.cmd.gen.CrossLinkHeartbeatStc;
import com.huangzj.cmd.gen.PbService;
import com.huangzj.cmd.gen.Service4_Method;
import org.springframework.stereotype.Component;
/** 区服 ↔ 跨服长连接心跳(仅 {@link CommandSource#CROSS_TCP})。 */
@Component
@Cmd(
source = CommandSource.CROSS_TCP,
serviceId = PbService.Service_4_VALUE,
methodId = Service4_Method.Heartbeat_VALUE)
public class CrossLinkHeartbeatCommand extends AbstractTypedPbCommand<CrossLinkHeartbeatCts, CrossLinkHeartbeatStc.Builder> {
@Override
protected void run(CommandContext ctx, CrossLinkHeartbeatCts cts, CrossLinkHeartbeatStc.Builder stcBuilder) {
// 原样回显客户端时间,便于区服侧算 RTT;cross_time_ms 为跨服处理时刻
stcBuilder.setClientTimeMs(cts.getClientTimeMs()).setCrossTimeMs(System.currentTimeMillis());
}
}
@@ -0,0 +1,44 @@
package com.huangzj.cross.cmd;
import com.huangzj.base.cmd.AbstractTypedPbCommand;
import com.huangzj.base.cmd.Cmd;
import com.huangzj.base.cmd.CommandContext;
import com.huangzj.base.cmd.CommandSource;
import com.huangzj.cmd.gen.CrossLinkRegisterCts;
import com.huangzj.cmd.gen.CrossLinkRegisterStc;
import com.huangzj.cmd.gen.PbService;
import com.huangzj.cmd.gen.Service4_Method;
import com.huangzj.cross.config.CrossLinkAcceptProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* 区服 TCP 连上跨服后的注册握手(仅 {@link CommandSource#CROSS_TCP})。
*/
@Component
@RequiredArgsConstructor
@Cmd(
source = CommandSource.CROSS_TCP,
serviceId = PbService.Service_4_VALUE,
methodId = Service4_Method.Register_VALUE)
public class CrossLinkRegisterCommand extends AbstractTypedPbCommand<CrossLinkRegisterCts, CrossLinkRegisterStc.Builder> {
private final CrossLinkAcceptProperties acceptProperties;
@Override
protected void run(CommandContext ctx, CrossLinkRegisterCts cts, CrossLinkRegisterStc.Builder stcBuilder) {
String expected = acceptProperties.getExpectedToken();
// 跨服未配置 expected-token 时不校验,方便本地多区服联调;生产环境建议两边配置一致
if (StringUtils.hasText(expected) && !expected.equals(cts.getToken())) {
stcBuilder.setOk(false).setMessage("token mismatch");
return;
}
// server_id 用于日志、路由扩展;空则拒绝,避免匿名连接占坑
if (!StringUtils.hasText(cts.getServerId())) {
stcBuilder.setOk(false).setMessage("server_id required");
return;
}
stcBuilder.setOk(true).setMessage("ok " + cts.getServerId());
}
}
@@ -0,0 +1,17 @@
package com.huangzj.cross.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 跨服侧:区服注册时可选的 token 校验(为空则不校验)。
*/
@Getter
@Setter
@ConfigurationProperties(prefix = "cross.link")
public class CrossLinkAcceptProperties {
/** 非空时须与区服 {@code cross.token} 一致才允许注册。 */
private String expectedToken = "";
}
@@ -0,0 +1,17 @@
package com.huangzj.cross.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 跨服进程自己监听的 TCP 端口,长度前缀 + Protobuf 那一套。
*/
@Getter
@Setter
@ConfigurationProperties(prefix = "cross.tcp")
public class CrossTcpProperties {
/** Netty 监听端口,长度前缀 + protobuf 帧。 */
private int port;
}
@@ -0,0 +1,29 @@
package com.huangzj.cross.net;
import com.huangzj.base.cmd.CommandSource;
import com.huangzj.base.cmd.ProtoRegistry;
import com.huangzj.base.frame.LengthPrefixedProtobuf;
import com.huangzj.base.net.CmdRpcTcpFrameHandler;
import com.huangzj.cross.cmd.CrossGameCommandRegistry;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/** 跨服 TCPpipeline 上挂帧解码 + CmdRpc 业务 handler。 */
@Component
@RequiredArgsConstructor
public class CrossTcpChannelInitializer extends ChannelInitializer<SocketChannel> {
private final CrossGameCommandRegistry crossGameCommandRegistry;
private final ProtoRegistry protoRegistry;
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(LengthPrefixedProtobuf.newTcpFrameDecoder());
ch.pipeline()
.addLast(
new CmdRpcTcpFrameHandler(
crossGameCommandRegistry, null, CommandSource.CROSS_TCP, protoRegistry));
}
}
@@ -0,0 +1,95 @@
package com.huangzj.cross.net;
import com.huangzj.cross.config.CrossTcpProperties;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import jakarta.annotation.PreDestroy;
import java.net.BindException;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* 跨服侧的 TCP 入口:Spring 起来之后绑端口,应用关掉时把线程组也收干净。
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CrossTcpServer {
private final CrossTcpProperties properties;
private final CrossTcpChannelInitializer crossTcpChannelInitializer;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
private Channel serverChannel;
/** 等 Spring 容器就绪再绑端口,避免 Bean 还没齐就开始接连接。 */
@EventListener(ApplicationReadyEvent.class)
public void start() throws InterruptedException {
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(crossTcpChannelInitializer);
serverChannel = b.bind(properties.getPort()).sync().channel();
log.info("Cross TCP {}", properties.getPort());
} catch (Exception e) {
if (findBindException(e) != null) {
shutdownGroupsOnly();
int port = properties.getPort();
throw new IllegalStateException(
"跨服 TCP 端口 "
+ port
+ " 已被占用(Address already in use)。请关闭占用该端口的进程,或在 application.yml 里修改 cross.tcp.port。"
+ " Windows 可查:netstat -ano | findstr :" + port,
e);
}
shutdownGroupsOnly();
throw e;
}
}
private static BindException findBindException(Throwable t) {
while (t != null) {
if (t instanceof BindException) {
return (BindException) t;
}
t = t.getCause();
}
return null;
}
/** 绑定失败时尚未 assign serverChannel,只收掉线程组,避免泄漏。 */
private void shutdownGroupsOnly() {
if (bossGroup != null) {
bossGroup.shutdownGracefully(0, 1, TimeUnit.SECONDS).syncUninterruptibly();
bossGroup = null;
}
if (workerGroup != null) {
workerGroup.shutdownGracefully(0, 1, TimeUnit.SECONDS).syncUninterruptibly();
workerGroup = null;
}
}
/** 进程退出时把 channel 和线程组都关掉,别留一堆 Netty 线程 hanging。 */
@PreDestroy
public void stop() throws InterruptedException {
if (serverChannel != null) {
serverChannel.close().syncUninterruptibly();
}
if (bossGroup != null) {
bossGroup.shutdownGracefully(0, 1, TimeUnit.SECONDS).syncUninterruptibly();
}
if (workerGroup != null) {
workerGroup.shutdownGracefully(0, 1, TimeUnit.SECONDS).syncUninterruptibly();
}
}
}
@@ -0,0 +1,10 @@
# 开发环境
cross:
tcp:
port: 9200
link:
expected-token: ""
app:
logging:
dir: logs/CrossModule
@@ -0,0 +1,10 @@
# 部署环境:监听端口、注册校验 token、日志目录建议走环境变量
cross:
tcp:
port: ${CROSS_TCP_PORT:9200}
link:
expected-token: ${CROSS_LINK_EXPECTED_TOKEN:}
app:
logging:
dir: ${APP_LOG_DIR:/var/log/comm-framework/cross}
@@ -0,0 +1,5 @@
spring:
application:
name: cross-module
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 目录由本模块 application.yml 的 app.logging.dir 指定,与 ServerModule 互不干扰 -->
<springProperty name="LOG_DIR" scope="context" source="app.logging.dir" defaultValue="logs/CrossModule"/>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!-- INFO、WARN:不含 ERROR,便于日常浏览 -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>14</maxHistory>
<totalSizeCap>512MB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- ERROR 单独落盘,告警/排障只盯这个文件即可 -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>512MB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- DEBUG 仅进控制台(若把 root 调到 DEBUG);需要落盘时在 application.yml 里单独配 logger + appender -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_ERROR"/>
</root>
</configuration>
@@ -0,0 +1,10 @@
# 开发环境
cross:
tcp:
port: 9200
link:
expected-token: ""
app:
logging:
dir: logs/CrossModule
@@ -0,0 +1,10 @@
# 部署环境:监听端口、注册校验 token、日志目录建议走环境变量
cross:
tcp:
port: ${CROSS_TCP_PORT:9200}
link:
expected-token: ${CROSS_LINK_EXPECTED_TOKEN:}
app:
logging:
dir: ${APP_LOG_DIR:/var/log/comm-framework/cross}
@@ -0,0 +1,5 @@
spring:
application:
name: cross-module
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 目录由本模块 application.yml 的 app.logging.dir 指定,与 ServerModule 互不干扰 -->
<springProperty name="LOG_DIR" scope="context" source="app.logging.dir" defaultValue="logs/CrossModule"/>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!-- INFO、WARN:不含 ERROR,便于日常浏览 -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>14</maxHistory>
<totalSizeCap>512MB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- ERROR 单独落盘,告警/排障只盯这个文件即可 -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>512MB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- DEBUG 仅进控制台(若把 root 调到 DEBUG);需要落盘时在 application.yml 里单独配 logger + appender -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_ERROR"/>
</root>
</configuration>
@@ -0,0 +1,7 @@
com\huangzj\cross\config\CrossTcpProperties.class
com\huangzj\cross\net\CrossTcpServer$1.class
com\huangzj\cross\cmd\CrossGameCommandRegistry.class
com\huangzj\cross\CrossModuleApplication.class
com\huangzj\cross\net\CrossTcpCommandHandler.class
com\huangzj\cross\net\CrossTcpServer.class
com\huangzj\cross\cmd\CrossForwardCommand.class
@@ -0,0 +1,6 @@
F:\coding\comm-framework\CrossModule\src\main\java\com\huangzj\cross\cmd\CrossGameCommandRegistry.java
F:\coding\comm-framework\CrossModule\src\main\java\com\huangzj\cross\config\CrossTcpProperties.java
F:\coding\comm-framework\CrossModule\src\main\java\com\huangzj\cross\CrossModuleApplication.java
F:\coding\comm-framework\CrossModule\src\main\java\com\huangzj\cross\net\CrossTcpServer.java
F:\coding\comm-framework\CrossModule\src\main\java\com\huangzj\cross\cmd\CrossForwardCommand.java
F:\coding\comm-framework\CrossModule\src\main\java\com\huangzj\cross\net\CrossTcpCommandHandler.java