Skip to content
Open
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
com.iohao.game.external.core.netty.micro.join.TcpExternalJoinSelector
com.iohao.game.external.core.netty.micro.join.WebSocketExternalJoinSelector
com.iohao.game.external.core.netty.micro.join.WebSocketExternalJoinSelector
com.iohao.game.external.core.netty.micro.join.HttpExternalJoinSelector
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,9 @@ BrokerClientBuilder initConfig() {
// 注册用户处理器
this.registerUserProcessor(this.brokerClientBuilder);

// 实验性功能
experiment();

return this.brokerClientBuilder;
}

/**
* 实验性功能,将来可能移除的。
*/
private void experiment() {
// ExtRegions.me().add(new MonitorExtRegion());
}

@Override
public void setWithNo(int withNo) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.iohao.game.widget.light.profile;

import org.springframework.core.convert.support.DefaultConversionService;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;

public abstract class AbstractStaticBinder {

// Spring 标准类型转换器
protected static final DefaultConversionService CONVERTER = new DefaultConversionService();

/**
* 绑定数据到目标类的静态字段
*
* @param targetClass 目标类
*/
public void bind(Class<?> targetClass) {
if (!isValidDataSource()) {
return;
}

Field[] fields = targetClass.getDeclaredFields();

Map<String, Object> dataMap = getDataMap();
dataMap.forEach((key, value) -> {
if (key == null || value == null) {
return;
}

String normalizedKey = normalize(key.toString());

for (Field field : fields) {
int mod = field.getModifiers();

if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
String fieldName = field.getName();

if (fieldName.equalsIgnoreCase(key.toString()) || normalize(fieldName).equals(normalizedKey)) {
try {
Object convertedValue = CONVERTER.convert(value, field.getType());
field.set(null, convertedValue);
} catch (Exception e) {
System.err.printf("[%s] 字段 [%s] 赋值失败: %s%n", getSourceName(), fieldName, e.getMessage());
}
break;
}
}
}
});
}

/**
* 数据源是否有效
*/
protected abstract boolean isValidDataSource();

/**
* 获取数据映射(键值对)
*/
protected abstract Map<String, Object> getDataMap();

/**
* 获取数据源名称(用于日志输出)
*/
protected abstract String getSourceName();

/**
* 归一化处理:移除所有下划线并转为小写
*/
private String normalize(String name) {
return name.replace("_", "").toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.iohao.game.widget.light.profile;

import java.util.HashMap;
import java.util.Map;

public class EnvStaticBinder extends AbstractStaticBinder {

private final Map<String, String> configFileData;

// 构造函数:接受配置文件数据
public EnvStaticBinder(Map<String, String> configFileData) {
this.configFileData = configFileData != null ? configFileData : new HashMap<>();
}

@Override
protected boolean isValidDataSource() {
// 检查配置文件或环境变量是否有数据
return !configFileData.isEmpty() || !System.getenv().isEmpty();
}

@Override
protected Map<String, Object> getDataMap() {
// 合并配置文件和环境变量数据,配置文件优先
Map<String, Object> mergedData = new HashMap<>(configFileData);
System.getenv().forEach((key, value) -> {
mergedData.putIfAbsent(key, value); // 环境变量作为备选
});
return mergedData;
}

@Override
protected String getSourceName() {
return "Env"; // 日志标识仍为 Env,但内部已融合配置文件数据
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
*/
package com.iohao.game.widget.light.profile;

import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -42,9 +44,8 @@
@Slf4j
@ToString
public class Profile {
/** key */
String key;

@Getter
Map<String, Object> map = new ConcurrentHashMap<>();

Profile() {
Expand Down Expand Up @@ -134,10 +135,7 @@ public int getInt(String key, int defVal) {
public void load(Properties properties) {
for (Object o : properties.keySet()) {
String key = o.toString();

Object value = properties.get(o);
// 理论上在这里做数据类型解析会好一些,但现在不着急

String value = properties.getProperty(key);
this.map.put(key, value);
}
}
Expand All @@ -149,18 +147,21 @@ public void load(Properties properties) {
*/
public void load(List<URL> urls) {
// 需要加载的配置文件
urls.forEach(url -> {

try (InputStream inputStream = url.openStream()) {
Properties properties = new Properties();
properties.load(inputStream);

this.load(properties);
urls.forEach(this::load);
}

} catch (IOException e) {
log.error(e.getMessage(), e);
}
});
public void load(URL url) {
if (url == null) {
log.warn("加载配置失败:URL 为空");
return;
}
try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) {
Properties properties = new Properties();
properties.load(reader);
this.load(properties);
} catch (IOException e) {
log.error("读取配置文件异常 [{}]: {}", url, e.getMessage(), e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ public final class ProfileManager {
*/
private final Map<String, Profile> profileMap = new ConcurrentHashMap<>();


public Profile profile() {
return profile(MAIN_CONFIG);
}
Expand Down Expand Up @@ -115,5 +114,93 @@ public void loadMainProfile(String profileConfigName) {

}

/**
* 解析环境列表字符串为环境名称列表
* <pre>
* 支持逗号分隔的多个环境名称,例如: "blocks,local"
* 会自动去除空格和空字符串
* </pre>
*
* @param env 环境名称字符串,支持逗号分隔
* @return 环境名称列表
*/
private List<String> parseEnvList(String env) {
final String separator = ",";
return Arrays.stream(env.split(separator))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}

/**
* 按顺序加载多个环境的配置文件并合并
* <pre>
* env 参数支持逗号分隔的多个环境名称,例如: "blocks,local"
* 会按顺序加载每个环境的配置文件,后面的环境配置会覆盖前面的同名配置项
* </pre>
*
* @param env 环境名称,支持逗号分隔的多个环境,例如: "blocks,local"
* @param fileName 配置文件名 (不需要带 .props 后缀)
* @return 合并后的 Profile 对象,如果加载失败返回 null
*/
public Profile loadEnvFile(String env, String fileName) {
if (StringUtils.isEmpty(env) || StringUtils.isEmpty(fileName)) {
log.warn("环境或文件名为空,跳过加载");
return null;
}

List<String> envList = parseEnvList(env);
if (envList.isEmpty()) {
log.warn("环境列表为空,跳过加载");
return null;
}

log.debug("加载的环境列表 - size {} - {}", envList.size(), envList);

Profile mergedProfile = profile(fileName);
mergedProfile.map.clear();

for (String singleEnv : envList) {
URL fileURL = ResourcePatternResolverProfile.getFileURL(singleEnv, fileName);
if (fileURL != null) {
mergedProfile.load(fileURL);
log.debug("加载环境 [{}] 的配置 - size:{}", singleEnv, mergedProfile.map.size());
} else {
log.warn("环境 [{}] 下未找到配置文件 [{}]", singleEnv, fileName);
}
}

if (mergedProfile.map.isEmpty()) {
log.warn("加载配置文件失败,跳过加载");
return null;
}

log.debug("配置内容 - size:{} - {}", mergedProfile.map.size(), mergedProfile.map);
return mergedProfile;
}

/**
* 加载特定环境的配置文件并填充到静态类
* <pre>
* env 参数支持逗号分隔的多个环境名称,例如: "blocks,local"
* 会按顺序加载每个环境的配置文件,后面的环境配置会覆盖前面的同名配置项
* 所有环境配置加载完成后,再统一进行静态类的映射绑定
* </pre>
*
* @param env 环境名称,支持逗号分隔的多个环境,例如: "blocks,local"
* @param fileName 配置文件名 (不需要带 .props 后缀)
* @param staticClass 需要绑定的静态类
*/
public void loadEnvFileFillStaticClass(String env, String fileName, Class<?> staticClass) {
Profile profile = loadEnvFile(env, fileName);
if (Objects.isNull(profile) || profile.map.isEmpty()) {
log.warn("加载配置文件失败,跳过绑定静态类");
return;
}

ProfileStaticBinder profileStaticBinder = new ProfileStaticBinder(profile.map);
profileStaticBinder.bind(staticClass);
log.debug("配置内容已绑定到静态类 - size:{} - {}", profile.map.size(), profile.map);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.iohao.game.widget.light.profile;

import java.util.Map;

/**
* @Author: caochaojie
* @Date: 2026-01-2215:29
*/
public class ProfileStaticBinder extends AbstractStaticBinder {

private final Map<String, Object> configMap;

public ProfileStaticBinder(Map<String, Object> configMap) {
this.configMap = configMap;
}

@Override
protected boolean isValidDataSource() {
return configMap != null && !configMap.isEmpty();
}

@Override
protected Map<String, Object> getDataMap() {
return configMap;
}

@Override
protected String getSourceName() {
return "Config";
}

}
Loading