JavaScript,一开始定位为浏览器表单验证的“玩具语言”,发展到如今,却能胜任各种舞台。nodejs,es6等等,奠定了其作为后端语言的地位。
在JVM里,我们也可以直接使用javascript代码,因为jdk已默认绑定其实现。由于拥有动态语言灵活的特点,我们可以用来作很多有趣的事情。在游戏服务端开发,我们越来越多可以看到它的身影。
下面我们就来看下javascript在游戏服务器里的应用。
数值是游戏的灵魂,很多游戏其实玩的就是数据。所以游戏里有各种各样的公式计算,最常见的就是玩家战斗力的计算。如果我们把游戏的数值公式硬性写进代码里,那么当策划需要在程序运行过程中修改公式的话,我们就要热更代码了。但如果我们把公式作为数据配置,那么策划只需要重载下数据表,成本就低很多。要在游戏里运行公式的话,用java写一套基于栈的公式计算器也是可以实现的,但比较麻烦。最简单的,莫过于使用javascript这个神器了。代码如下:
@Test
public void testCalculator() throws Exception {
String formula = "a + b * c + Math.max(5,10)";
ScriptEngineManager engineManager= new ScriptEngineManager();
ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");
Map params = new HashMap<>();
params.put("a", 3);
params.put("b", 4);
params.put("c", 5);
Object value = scriptEngine.eval(formula, new SimpleBindings(params));
double result = Double.valueOf(value.toString());
Assert.assertEquals(33.0, result);
}
从上面的例子,我们可以看出,java可以执行javascript代码。SimpleBindings里绑定的变量除了可以是基本类型,还可以数据对象!这样的话,我们就可以传入一个javabean。从而将js代码作为数据配置。话不多说,看个例子就懂了。
假设我们有一个js文件,里面只有一行配置
config.maxPackagePerSecond = 30
同时我们也有一个javabean,其字段与上面的一致
/**
* 防火墙配置
* @author kingston
*
*/
public class FireWallConfig {
/** 每秒最大收包数量 */
private int maxPackagePerSecond;
public int getMaxPackagePerSecond() {
return maxPackagePerSecond;
}
public void setMaxPackagePerSecond(int maxPackagePerSecond) {
this.maxPackagePerSecond = maxPackagePerSecond;
}
@Override
public String toString() {
return "FireWallConfig [maxPackagePerSecond=" + maxPackagePerSecond + "]";
}
}
那么,使用下面的代码,我们就可以直接把js配置映射到对应的javabean里了,非常神奇。
注意:SimpleBindings里的config变量,必须与js配置的变量一致。
@Test
public void testReadConfig() throws Exception {
// 这里只是把js文件作为文本流读取出来
String content = FileUtils.readText("firewall.cfg.js");
FireWallConfig config = new FireWallConfig();
Map params = new HashMap<>();
params.put("config", config);
ScriptEngineManager engineManager= new ScriptEngineManager();
ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");
Object response = scriptEngine.eval(content, new SimpleBindings(params));
System.err.println(config);
}
程序执行结果
FireWallConfig [maxPackagePerSecond=30]
在游戏运行过程中,我们经常需要查看程序的内存数据,或者执行部分代码块。如果使用java方式,我们就需要将需要的代码编译成class,再拿到游戏里进行热更求值。但如果采用脚本语言,例如js或者groovy,我们就可以直接使用源代码。
从上面看以看出,java可以直接运行js代码,只需要以下几行代码
ScriptEngineManager engineManager= new ScriptEngineManager();
ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");
Object response = scriptEngine.eval(jsCode);
那么,我们可以执行任意有效的js代码,要么查看内存数据,要么执行任意代码块。
// 查看内存数据
var playerSize = com.kingston.jforgame.server.game.player.PlayerManager.getInstance().getOnlinePlayers();
com.kingston.jforgame.server.logs.LoggerUtils.error("在线玩家数据为" + playerSize);
// 调用java代码
com.kingston.jforgame.server.game.player.PlayerManager.getInstance().kickPlayer(123);
结合JMX,我们的javascript简直无敌。具体见
-->使用JMX监控游戏服务器
当然,javascript在游戏服务器的应用肯定不止如此。随着对javascript的深入了解,相信能够发现更多有趣的应用。