手游服务端框架之GM金手指的设计
来源:互联网 发布:大数据开发难学吗 编辑:程序博客网 时间:2024/06/02 23:33
玩过单机游戏的朋友,应该对金山游侠这个软件很熟悉把。当初我经常嫌刷怪升级非常辛苦,很多时候都是直接用金山游侠来修改游戏的经验或者等级内存,直接把角色调得很牛逼。
游戏开发也非常需要这些可以修改玩家数据的金手指。在游戏里,它有个更加专业的名称,叫GM(GameMaster)命令。
有了GM命令,我们就看好很方便让角色快速升级或者直接获取极品道具。当然,只要是数据,都可以有对应的GM命令,只要项目有需要。
下面,我们就来看一下GM系统的一种实现方式。
假设我们需要有一种指令,比如输入playerLv加一个数字表示要达到的等级,只要服务端受到这条指令,就直接将当前角色升到目标等级。不同的GM命令,指令的前缀和参数都是不同的,也就是说,每个指令都有自己固定的格式。在Java里,我们可以用正则表达式来定义这样的格式。
1.首先来看一些我们的gm命令抽象类,该类有几个作用,例如,定制具体的gm命令参数的格式,解析参数的方法,以及执行逻辑的方法。
import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;import com.kingston.game.database.user.player.Player;import com.kingston.game.gm.message.ResGmResultMessage;/** * 抽象gm命令 * @author kingston */public abstract class AbstractGmCommand {/** * 正则表达式模式 * @return */public abstract String getPattern();/** * 帮助文档 * @return */public abstract String help();/** * 是否匹配 * @param pattern * @param content * @return */public boolean isMatch(Pattern pattern, Matcher matcher, String content) {return matcher.matches();}/** * 返回正则表达式解析的一系列参数 * @param matcher * @param message * @return */public List<String> params(Matcher matcher, String message) {List<String> params = new ArrayList<>();for (int i=1; i<matcher.groupCount()+1; i++) {params.add(matcher.group(i));}return params;}/** * 执行逻辑 * @param player * @param params * @return */public abstract ResGmResultMessage execute(Player player, List<String> params);}2.定义一个gm命令的管理工具,负责缓存所有的gm命令。当收到玩家的gm指令后,如果判断当前玩家的IP地址或者角色有执行的权限,那么再通过枚举的方式,找到能够处理请求的具体gm命令。从而执行对应的逻辑。
public class GmManager {private static volatile GmManager instance;private GmManager() {}/** 缓存gm指令的模式与对应的逻辑处理者 */private Map<Pattern, AbstractGmCommand> commands = new HashMap<>();private final String SCAN_PATH = "com.kingston.game.gm.command";public static GmManager getInstance() {if (instance == null) {synchronized(GmManager.class) {if (instance == null) {instance = new GmManager();instance.init();}}}return instance;}private void init() {Set<Class<?>> clazzs = ClassScanner.getClasses(SCAN_PATH, new ClassFilter() {@Overridepublic boolean accept(Class<?> clazz) {return AbstractGmCommand.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers());}});for (Class<?> clazz:clazzs) {try{AbstractGmCommand command = (AbstractGmCommand) clazz.newInstance();String regex = command.getPattern();commands.put(Pattern.compile(regex), command);}catch(Exception e) {e.printStackTrace();}}}/** * 处理gm入口 * @param playerId * @param content * @return */public void receiveCommand(long playerId, String content) {Player player = PlayerManager.getInstance().get(playerId);//判断权限if (!hasExecPower(player)) {return;}for (Map.Entry<Pattern, AbstractGmCommand> entry:commands.entrySet()) {Pattern pattern = entry.getKey(); AbstractGmCommand command = entry.getValue();Matcher matcher = pattern.matcher(content);if (command.isMatch(pattern, matcher, content)) {List<String> params = command.params(matcher, content);ResGmResultMessage result = command.execute(player, params);MessagePusher.pushMessage(playerId, result);return;}}ResGmResultMessage failedMessage = ResGmResultMessage.buildFailResult("找不到对应的gm命令");MessagePusher.pushMessage(playerId, failedMessage);}/** * 是否有执行权限 * @param player * @return */private boolean hasExecPower(Player player) {//这里根据具体业务进行拦截return true;}public static void main(String[] args) {Pattern pattern = Pattern.compile("^reloadConfig\\s+([a-zA-Z_]+)");String expr = "reloadConfig CofingPlayer_Level";Matcher matcher = pattern.matcher(expr);if (matcher.matches()) {List<String> params = new ArrayList<>();for (int i=1; i<matcher.groupCount()+1; i++) {params.add(matcher.group(i));}System.err.println(params);}}}3.举个例子,上面的修改玩家等级的gm命令。格式为:playerLv [level],对应的正则表达式模式为
^playerLv\\s+(\\d+)该命令的完整代码
/** * 修改玩家等级的gm * @author kingston */public class GmPlayerLevelCommand extends AbstractGmCommand {@Overridepublic String getPattern() {return "^playerLv\\s+(\\d+)";}@Overridepublic String help() {return "修改玩家等级(playerLv [level])";}@Overridepublic ResGmResultMessage execute(Player player, List<String> params) {int newLevel = Integer.parseInt(params.get(0));ConfigPlayerLevel configLevel = ConfigDatasPool.getInstance().configPlayerLevelContainer.getConfigBy(newLevel);if (configLevel == null) {return ResGmResultMessage.buildFailResult("目标等级参数无效");}player.setLevel(newLevel);player.setUpdate();DbService.getInstance().add2Queue(player);return ResGmResultMessage.buildSuccResult("修改玩家等级成功");}}4.还有一个指令也很常见。比如游戏运行过程中,策划童鞋发现数值填错了(他们经常这么干)。我们不想重启服务但又希望内存里的策划配置能刷新。所以,我们需要有热更新配置的gm命令。回顾一下我们前一篇文章,关于配置数据库的设计。里面将策划的所有配置统一由ConfigDatasPool管理。如此,我们很容易通过反射的方式,动态实例化新的配置容器,读取新的配置并替换原有的container。
重载配置的gm指令格式为
^reloadConfig [tableName]对应的正则表达式为
^reloadConfig\\s+([a-zA-Z_]+)完整代码如下:
/** * 修改配置表的gm * @author kingston */public class GmReloadConfigCommand extends AbstractGmCommand {@Overridepublic String getPattern() {return "^reloadConfig\\s+([a-zA-Z_]+)";}@Overridepublic String help() {return "修改配置表(^reloadConfig [tableName])";}@Overridepublic ResGmResultMessage execute(Player player, List<String> params) {String tableName = params.get(0);String containerName = tableName + "Container";try {Field field = ConfigDatasPool.class.getDeclaredField(containerName);Class<?> type = field.getType();Reloadable newContainer = (Reloadable) type.newInstance();newContainer.reload();field.set(ConfigDatasPool.getInstance(), newContainer);return ResGmResultMessage.buildSuccResult("重载["+tableName+"]表成功");} catch (Exception e) {e.printStackTrace();}return ResGmResultMessage.buildFailResult("找不到目标配置表");}}5.GM命令也属于玩家请求,那么也是需要对应的Controller,Message,RequestMapper。都是很简单的申明,这里就不贴代码了。
文章预告:下一篇主要介绍使用观察者模式实现事件驱动
手游服务端开源框架系列完整的代码请移步github ->>game_server
阅读全文
1 0
- 手游服务端框架之GM金手指的设计
- 手游服务端框架之配置与玩家数据库设计
- 手游服务端框架之网关
- 手游服务端框架之模仿SpringMvc处理玩家请求
- 手游服务端框架之消息线程模型
- 手游服务端框架之使用Guava构建缓存系统
- 手游服务端框架之客户端协议组合下发
- 手游服务端框架之每日重置逻辑
- TYPESDK手游聚合SDK服务端设计思路与架构之二:服务端设计
- 手游服务端框架之后台管理工具
- 手游服务端框架之使用事件驱动模型解决业务高耦合
- 手游服务端框架之使用Redis实现跨服排行榜
- 端游、手游服务端常用的架构
- 手游服务端开发
- TYPESDK手游聚合SDK服务端设计思路与架构之三:流程优化之订单保存与通知
- TYPESDK手游聚合SDK服务端设计思路与架构之四:流程优化之信息安全与订单校验
- TYPESDK手游聚合SDK服务端设计思路与架构之五:流程优化之特殊流程处理
- 游戏开发-GM系统的设计
- c++switch的基本用法
- 一个简单的Makefile示例
- centos服务器安装桌面
- 杭电acm4562守护雅典娜 (dp+圆)
- 【简记】Java Web 内幕——Mybatis初始化,数据源与连接池,事务管理
- 手游服务端框架之GM金手指的设计
- 玲珑OJ 1149
- C语言复习总结篇(二)
- HTML/css学习之-filter:drop-shadow给文字设置一个阴影+@keyframes 规则
- <HTTP协议详解>由浅入深看HTTP
- VS2012中自定义工具“MyApplicationCodeGenerator”与文件“My Project\Application.myapp”相关联,但在项目中找不到该自定义工具的输出
- SYN和RST 学习
- Linux基础之权限管理ACL
- learning rate