Skip to content

GameServer

crazyjohn edited this page Jan 14, 2016 · 5 revisions

如何用stupidbird写一个游戏服务器

  1. Facada门面/入口代码

/**
 * 游戏服;
 * 
 * @author crazyjohn
 *
 */
public class GameServer {
	static Logger logger = LoggerFactory.getLogger("Server");

	/**
	 * 入口
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) {
		try {
			GameServerConfig config = ConfigUtil.loadConfig(GameServerConfig.class, "game_server.cfg.js");
			Node node = new Commander();
			// 加载配置
			node.configure(config);
			// 全局模块初始化
			Injector globalInjector = Guice.createInjector(new GlobalModules(config, node));
			// io处理器
			GameIoHandler ioHandler = globalInjector.getInstance(GameIoHandler.class);
			node.setIoHandler(ioHandler);
			// 设置注入器
			ioHandler.setGlobalInjector(globalInjector);
			// 启动
			node.startup();
			// dump
			logger.info("Dump executor info: " + node.executor().dump());
		} catch (Exception e) {
			GameMonitor.catchException(e);
			System.exit(0);
		}
	}

}
  1. 整体结构

  1. 切入点

游戏Io处理器

public class GameIoHandler extends NodeIoHandler<PlayerSession> {
	@Inject
	private EntityDBService dbService;
	@Inject
	private UUIDGenerator uuidService;
	private Injector globalInjector;

	@Inject
	public GameIoHandler(NodeDispatcher dispatcher) {
		super(dispatcher);
	}

	@Override
	protected SessionMessage<PlayerSession> createSessionOpenMessage(PlayerSession sessionInfo) {
		Open msg = new Open();
		msg.setSession(sessionInfo);
		return msg;
	}

	@Override
	protected SessionMessage<PlayerSession> createSessionCloseMessage(PlayerSession sessionInfo) {
		Close msg = new Close();
		msg.setSession(sessionInfo);
		return msg;
	}

	@Override
	protected PlayerSession createSessionInfo(IoSession session) {
		PlayerSession sessionInfo = new PlayerSession(session, dbService, uuidService, globalInjector);
		return sessionInfo;
	}

	public void setGlobalInjector(Injector injector) {
		this.globalInjector = injector;
	}

}

在网络会话创建的时候创建一个PlayerSession,再看看玩家会话的内部:

public class PlayerSession extends NodeBindableSession<Player> {
	private final Player player;

	public PlayerSession(IoSession session, EntityDBService dbService, UUIDGenerator uuidService,
			Injector globalInjector) {
		super(session);
		// 构建玩家对象
		player = new Player(this, dbService, uuidService, globalInjector);
	}

	@Override
	public Player object() {
		return player;
	}

	@Override
	public String toString() {
		return this.session.toString();
	}

}

内部注入了Player玩家对象,以此展开业务。

  1. 玩家业务模块结构

HumanModule

  1. 全局管理器结构

ManagerActor

  1. 数据层交互

Save机制

dependency injection(guice)

引擎开发到2016.1.6这天,我准备在其中加入di的东西。其实之前在生产中用过类似的东西,用的是spring来托管,但是进入上线后期发现spring在di这里的效率堪忧,所以去掉了大部分依赖,说以可以说之前对di是一次不愉快的使用(其实很大程度源于时间问题以及对spring di的不了解)。

这次我准备使用guice作为di驱动。原因是看了一个项目使用这个还挺简单方便的,而且据说它的效率是spring的100倍。而且主要这里的思路是源于业务层的架构:

  1. 业务层有Module模块的概念,用来管理玩家指定模块的数据。

  2. 模块之前的交互通过对对指定模块抽象出交互接口。

     /**
      * 玩家物品模块;
      * 
      * @author crazyjohn
      *
      */
     public class HumanItemModule extends BaseHumanModule implements ItemModule {
     	protected List<HumanItemEntity> items = new ArrayList<HumanItemEntity>();
     	@Inject
     	protected EquipModule itemModule;
     	private Logger logger = LoggerFactory.getLogger("Action");
     
     	@Inject
     	public HumanItemModule(Human human) {
     		super(human);
     	}
     
     	@Override
     	public void onLogin(HumanEntity entity) {
     		items = entity.getItems();
     	}
    

如上,物品模块和装备模块交互的时候,通过注入EquipFronter接口来实现。

	public interface EquipFronter {
		public void addEquip(HumanEquipEntity equipEntity);
	}
  1. 通过guice来进行方便的di注入管理。
  2. 以上就是整体的思路,或许后面发现有问题(效率?),还会再次去掉。
  3. 目前不爽的地方就是guice尼玛还需要引入javax.inject和aopalliance这两个包,让整体体积又变大了。
Clone this wiki locally