`
米虫风魂
  • 浏览: 805 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

网络游戏开发中的领域模型

阅读更多

    一直以来我都在思考网络游戏开发的OO模式。如何将领域建模运用到网络游戏这种异步项目中。在我的网络游戏架构中,为了避免多线程引发的诸多问题,逻辑服务模块采用单线程方式,考虑到单线程下阻塞模式会严重影响服务器的性能,因此采用了异步方式进行逻辑的通讯。我想大多的网络游戏开发应该都是采用异步方式。但异步方式,很容易将一个原本完整的逻辑,折分成一个一个request、response等网络命令,服务器端将一些逻辑代码写在收到指令的地方,另一些写在业务类中,同样在收到指令的地方调用。没有一种清晰的思路来区分哪些逻辑该和异步的收发混在一起,哪些逻辑该放在领域层。

    昨天重新看了一些DDD(Domain Driven-Desgin)的文章,复习了一个Entity、Value、Service的概念,下面以玩家在商店购买一个道具为例,整理了一下思路。(由于只是快速草稿,因此很多语法细节是错误的)

 

 

――――――――――应用层―――――――――――― 

// 客户端的购买道具逻辑代理 
class ShoppingAgent : public ServiceAgent 
{ 
public: 
	// 发出购买某道具的 

	void requestBuyItem(int itemId, int qty) 
	{ 
		BuyItemRequest request(itemId, qty); 
		send(request); 
	} 
	void onBuyItemResponse(BuyItemResponse response) 
	{ 
		if(response.errorId == NO_ERROR) 
			m_listener->onBuyItemOk(); 
		else 
			m_listener->onBuyItemFail(); 
	} 
protected: 
	ShoppingListener m_listener; 
} 

// 服务端购买道具的逻辑服务
class ShoppingService : public Service 
{ 
public: 
	// 响应购买道具的请求 

	void onBuyItemRequest(BuyItemRequest r) 
	{ 
		BuyItemResponse response; 
		Shopping shopping; 
		try 
		{ 
			shopping.buyItem(r.playerId, r.shopId, r.itemId, r.qty); 
		} 
		catch(ShoppingException e) 
		{ 
			// 购买失败的错误ID会跟随response发回给客户端 
			response.errorId = e.errorId; 
		} 
		send(response); 
	} 
} 

 

   

    应用层,在这里主要负责对网络的异步通讯封装。ShoppingService作为服务在部署在服务器上,而ShoppingAgent作为访问服务的一个接口部署在客户端。对于客户端的应用上层,不需要调用到任何通讯指令,全部委托给ShoppingAgent。异步的结果,皆通过观察者模式,通知给上层。
   

 

 

――――――――――领域层―――――――――――― 
 
// 购买道具的服务 
//(对应Erice Evans 在Domain 中的service)

class Shopping 
{ 
public: 
	// 购买道具 

	void buyItem(int playerId, int shopId, int itemId, int qty) 
	{ 
		Shop shop = m_respository.getShop(shopId); 
		Player player = m_respository.getPlayer(playerId); 

		// 验证商店是否有要购买的道具 

		validateShopHasItem(shopId, itemId, qty); 
		// 取出道具价格 

		int amount = shop.getPrice(itemId, qty); 
		// 验证玩家是否有足够的钱 
		validatePlayerHasMoney(playerId, amount); 
		// 验证玩家背包是否有足够空间 
		validatePlayerHasSpace(playerId, itemId, qty); 

		// 玩家收到一定数量的道具 
		player.receiveItem(itemId, qty); 
		// 玩家付款 
		player.pay(amount); 
	} 

protected: 
	void validateShopHasItem(shopId, itemId, qty) 
	{ 
		Shop &shop = m_respository.getShop(shopId); 
		if(shop.hasItem(itemId, qty)) 
			throw new ShopHasNoItemException(); 
	} 

	void validatePlayerHasSpace(int playerId, int itemId, int qty) 
	{ 
		Player &player = m_respository.getPlayer(player); 
		Item &item = m_itemRespository.getItem(itemId); 

		if(player.getBag().getSpaceAvailabe() < item.getCapability() * qty) 
		{ 
			throw new NoEnoughSpaceException(); 
		} 
	} 

	void validatePlayerHasMoney(int playerId, int price) 
	{ 
		Player &player = m_respository.getPlayer(player); 

		if(player.hasMoney(price)) 
		{ 
			throw new NoEnoughMoneyException(); 
		} 
	} 
} 

// 商店,提供道具的购买,对道具定价(不同商店卖的东西不同) 
//(对应Erice Evans 在Domain 中的entity)

class Shop 
{ 
public: 
	int getPrice(int itemId, int qty) 
	{ 
		// …查找商品价格并乘上数量 
	} 

	bool hasItem(int itemId, int qty) 
	{ 
		// …查找商品 
	} 
} 

// 玩家 
//(对应Erice Evans 在Domain 中的entity) 
class Player 
{ 
public: 
	void receiveItem(int itemId, int qty) 
	{ 
		getBag()->pack(itemId, qty); 
	} 

	void pay(int money) 
	{ 
		m_money -= money; 
	} 
} 

// 资源仓库,由于是例子,所以先写到一块来

class Respoistory 
{ 
public: 
	Shop& getShop(int id) 
	{ 
	} 

	Player& getPlayer(int id) 
	{ 
	} 

	Item& getItem(int id) 
	{ 
	} 
} 

 

   

    Shopping 同Shop的分离,是我重新看DDD文章后的重大改变。不然之前一直困惑于buyItem()中会对于玩家的一些逻辑操作,似乎不应该是shop这个领域Entity的职责。

    Shopping同ShoppingService是分离的,在我的概念里,领域层应该足够的单纯,应该不用知道异步通讯这回事。对于这两者的职责我是这样分配的:Shopping负责真正的购买逻辑,协调玩家、商店、道具等对象。而ShoppingService主要负责处理异步收到请求,将处理结果返回给客户端。如果需要,分布式的事务也会在这层消化掉。至于逻辑,只是简单的调用领域服务Shopping::buyItem()。

    经过这次整理,感觉总体的思路清晰了很多。但仍还有一些困惑。如果player.receiveItem(),player.pay()不是在同一台服务器上处理怎么办,也就是任何逻辑服务器如果需要影响player的数据,都必须统一通过专门的一台服务器。这时候问题就来了,只要需要访问到别的服务器,那么就有异步操作。也就是要在Shopping类中用到异步通讯,这又违背了一开始我希望领域层不关心异步通讯的期望。如果上提到ShoppingService,似乎是可以的,但这一来又犯糊涂了。到底什么时候要放在ShoppingService,什么时候要放在Shoping?既然想让领域层不关心异步,那么领域层就应该按原本的思路去设计,自然而然会将player.pay()和player.receiveItem()放在shop.buyItem()中。

    如何是好呢???于是我问了一个没有研究过DDD的朋友,关于领域建模的概念,他也只是从我这里听到一些概念。他说,领域建模只是一种分析模型,和最终的设计模型之间本就是不同的,也就是说不管异步不异步,领域建模是不会变的。但设计模型肯定要关心异步还是同步。他这样说,似乎也有道理。但是Eric Evans指出的的Domain层难道不是最终的设计模型中的一部分吗?难道异步还是同步,一定要影响到所有逻辑的编写,而没办法在某一层消化掉吗?

 

分享到:
评论
10 楼 coolnight 2008-12-08  
同步异步在执行结构上就是不一样的,
想统一起来? 那只能全都统一到异步的处理模式上去

def onBuyResponse = {
   ....
}

buyer.requestBuy(Item item, Shop shop, onBuyResponse);

异步的模式性能更好,但是更复杂,这个,想不到什么好办法
9 楼 米虫风魂 2008-08-04  
我也是希望同步异步,单线多线脱离于领域逻辑的编写。但一个interface多种implementation的具体细节,我还是不是很通。就拿buyer和shop来说吧,buyer.buy(Item item, Shop shop)
{
    // ...
    shop.exchange(this.account, this.bag, item);
    // ...
}
在exchange处理完后,buy逻辑才会继续。如果这时候shop在另一台机上,就必须等待。但我们又不希望这样的等待影响到别的客户端的请求。又不愿用多线程来处理请求,结果就只能折分buy的逻辑成为几个请求响应步骤了。于是还是没能解决困惑。

jellyfish 写道
In several of my projects, I had one interface with implementations, single threaded, multi-threaded, jms-sync, jms-asyn, grid1-sync, grid1-async, grid2-sync, grid2-async. all non-single-threaded versions ....

请问什么是jms-sync,jms-asyn,grid1-sync,grid1-async...,我怎么在goolge和baidu中都搜索不到?

jellyfish 写道
Basically, this is just a usage of decorator pattern.

还是不明白decorator怎么解决,是否可以简单举个例子
8 楼 jellyfish 2008-08-03  
The shop object is passed in the buy() method, so the buyer doesn't know the shop in its internal state. You have to pass this object in order to know to buy from where.

Treat buyer as player in your context. Sorry, I didn't follow your context.

The shop interface has nothing to do with the implementation, where you across the network or not, or you use sync or async way or not. Under the implementation package, you may have various implementations to hook in. In several of my projects, I had one interface with implementations, single threaded, multi-threaded, jms-sync, jms-asyn, grid1-sync, grid1-async, grid2-sync, grid2-async. all non-single-threaded versions will eventually delegate the business logic to the single-threaded version, after handling its own concerns. Users can choose which through an input parameter during runtime.
Basically, this is just a usage of decorator pattern.

You can implement an interface using async methods. The key is to manager your data from the start to the end. In my implementations, I always has subpackages request and reply to distinguish the methods, this is extremely helpful for new developers so they wont get lost in the jungles.

My key point is async or sync is not a business concern, and thus should be abstract away from the business interface. Net game players don't care that. I was one of the net players, I hated to see system errors, :-).

My proposed way is to code the business model in a single threaded env first. Then expand it to other async, distributed env. This is a very effective way in my experience and is easier to convince others because you can see the results right away.

Good luck. Been there, done that, not easy. Hope get paid well, :-).
7 楼 米虫风魂 2008-08-01  
jellyfish 写道

Buyer
{
    private CashAccount account;
    private ItemBag bag;

    buy(Item item, Shop shop)
    {
        if (hasSpace())
        {
            double price = shop.getPrice(item); // throw ItemNotFound exception if not found, or return a negative number and then check that.
            if (hasEnoughCash())
            {
                shop.exchange(this.account, this.bag, item);
            }
            else
                throw exception("no enough cash!");
        }
        else
            throw exception("no enough space");
   }
}


我原本将Shopping类作为一个领域层的服务类,提供了购买的服务。当时是考虑到如果player.buy()不应该知道shop,如果shop.buy()不应该知道player。所以才引入一个Shopping类作为服务。这里你用了一个buyer,请问buyyer这里是一个service,还是一个entity?如果是entity的话,那player和buyer之间是否是继承关系?

jellyfish 写道

When you expand this to a client server model, you have the Shop on one side, and the buyer is on the other. In this case, the Shop will be acquired remotely. So shop will have a different implementation than the default one. So use an interface for Shop and has two implementations, the remote one wrap the network code around the default one and delegate all business operations to it.

我一开始也是这种想法,通过一个interface,然后两个implementation,一个负责远程访问封装,一个纯粹用于逻辑。可是由于我的网络是异步访问的,所以很难让一个接口来体现。我也曾问过我们项目中网络开发负责人,如果用成同步方式,可以吗?当时我就是为了能搞成这种方式。结果被驳了一番,焦点是用了同步,就得用多线程,不然别的处理就不要做了,但多线程引入带来的一系列问题,会令项目后期遇上很多麻烦。至少多线程的BUG你就很难debug了。

jellyfish 写道

So the core business should be able to run in one process so that we could test it easily and fast. When we add a behavior to the core, we really don't care the network.

呵呵,我想要独立领域层的一个大目的,就是为了好unit test。
6 楼 米虫风魂 2008-08-01  
JeffreyHsu 写道
建议不要自己去闭门造车
MMOG的开发和企业开发思维是不一样的


同意MMOG和企业开发有不一样的地方,但由于MMOG国内在谈架构的人相对较少,而企业开发的人谈这些概念的较多,所以就在这里发贴。另外我觉得企业开发的很多东西和MMOG也是相通的。难道MMOG就不需要领域建模的思想了?我想领域建模主要是希望大家把关注点从对环境关注(如web开发)拉到领域层面。游戏里有大量复杂的领域模型,甚至往往比企业开发的模型还多样化,所以我个人觉得领域建模主体思想不只适用于企业开发。

JeffreyHsu 写道

一般来说,网游用java开发服务端的不多,用c++和python的比较多
python的高性能网络编程框架twisted比较合适,可以参考一下

另外你可能误会了,其实我也是用c++开发游戏。只是c++圈内谈这些概念的人少,只好混到java这边来了。 

JeffreyHsu 写道

推荐sun的 project darkstar(http://projectdarkstar.com/)的开源项目,这个是sun的大型网游的服务端参考框架


感谢你推荐的网址,我会去好好学习,如果你也是开发游戏的,我也很希望向你多学学游戏的架构。
5 楼 米虫风魂 2008-08-01  
taowen 写道

领域模型和存储与通讯是无关的。又两种做法,一种是把东西都load内存中,然后再内存中操作领域对象,然后该持久的持久,该传输的传输。另外一种做法是proxy pattern。一个最实际的例子是hibernate的lazy load,表面上你是访问一个list,实际上这个时候触发了一条SQL。

推荐你先尝试第一种方案,以一个transaction为单位,在开始的时候把数据load出来,再结束的时候把数据存回去。如果中途需要按需load或者save数据,则要考虑采用更复杂的proxy模式。


我这里主要是遇上了必须用异步来提供服务的问题,数据库的加载,一般是同步机制。

taowen 写道
不过最紧要的是,为什么DDD?你的目的是什么?


之所以后DDD,首先是认同DDD的主体思想,觉得应该让游戏逻辑开发人员更多的精力关注在领域模型上,所以决定将网络服务的异步收发同领域两层分离,找到合适的一种开发模式。当然,主要是关心domain model的概念,倒还不注重于Driven Desgin
4 楼 米虫风魂 2008-08-01  
taowen 写道
我们来做一个重构先:

validatePlayerHasMoney(playerId, amount);  
validatePlayerHasSpace(playerId, itemId, qty);  

这个职责完全是可以移到player上的,player知道自己又多少钱,自己背包又多大,也可以把东西放到自己背包里去。如果是用intellij,把这个方法标记为static,然后按refactor->replace with instance method就可以了。


感谢你的重构建议,的确这段代码写得有点问题,player用得不够好,我现在改成了player.hasSpace()和player.hasCash()两支函数。但我还是比较坚持单独两支validate函数,因为player.hasSpace()和player.hasCash()都只是以bool返回,validate加上了异常的抛出,而player不需要负责异常抛出。
3 楼 JeffreyHsu 2008-08-01  
建议不要自己去闭门造车
MMOG的开发和企业开发思维是不一样的

推荐sun的 project darkstar(http://projectdarkstar.com/)的开源项目,这个是sun的大型网游的服务端参考框架

一般来说,网游用java开发服务端的不多,用c++和python的比较多
python的高性能网络编程框架twisted比较合适,可以参考一下

有空多交流
2 楼 jellyfish 2008-08-01  
assume you run everything in the same process, then do you ddd. So you ignore the network complexity first to see whether you can come up something.

A good design should be able to sustain the changes when you add in the network factor.

Buyer
{
    private CashAccount account;
    private ItemBag bag;

    buy(Item item, Shop shop)
    {
        if (hasSpace())
        {
            double price = shop.getPrice(item); // throw ItemNotFound exception if not found, or return a negative number and then check that.
            if (hasEnoughCash())
            {
                shop.exchange(this.account, this.bag, item);
            }
            else
                throw exception("no enough cash!");
        }
        else
            throw exception("no enough space");
   }
}

When you expand this to a client server model, you have the Shop on one side, and the buyer is on the other. In this case, the Shop will be acquired remotely. So shop will have a different implementation than the default one. So use an interface for Shop and has two implementations, the remote one wrap the network code around the default one and delegate all business operations to it.

So the core business should be able to run in one process so that we could test it easily and fast. When we add a behavior to the core, we really don't care the network.

A good design serves as the skeleton where we stick muscles to it.
1 楼 taowen 2008-08-01  
我们来做一个重构先:

validatePlayerHasMoney(playerId, amount);  
validatePlayerHasSpace(playerId, itemId, qty);  

这个职责完全是可以移到player上的,player知道自己又多少钱,自己背包又多大,也可以把东西放到自己背包里去。如果是用intellij,把这个方法标记为static,然后按refactor->replace with instance method就可以了。

领域模型和存储与通讯是无关的。又两种做法,一种是把东西都load内存中,然后再内存中操作领域对象,然后该持久的持久,该传输的传输。另外一种做法是proxy pattern。一个最实际的例子是hibernate的lazy load,表面上你是访问一个list,实际上这个时候触发了一条SQL。

推荐你先尝试第一种方案,以一个transaction为单位,在开始的时候把数据load出来,再结束的时候把数据存回去。如果中途需要按需load或者save数据,则要考虑采用更复杂的proxy模式。

不过最紧要的是,为什么DDD?你的目的是什么?

相关推荐

    网络游戏开发教程案例

    总的来说,《网络游戏开发教程案例》涵盖了网络游戏开发的多个核心领域,无论是对有经验的开发者还是初学者,都具有很高的学习价值。通过阅读和实践,不仅能够提升技术能力,还可以锻炼英语阅读能力,为今后的网络...

    网络游戏-基于领域模型的云计算网络工作流处理方法、装置和系统.zip

    总的来说,"基于领域模型的云计算网络工作流处理方法、装置和系统"是一种先进的网络游戏开发和运营模式,它结合了DDD的深度业务理解和云计算的高性能计算能力,旨在为玩家提供无缝、流畅的游戏体验,同时也为开发者...

    java网络游戏开发

    Java网络游戏开发是一个涵盖广泛的技术领域,它涉及到网络通信、图形渲染、并发处理、数据库交互等多个方面的知识。在Java平台上进行网络游戏开发,可以利用其强大的跨平台能力,丰富的库支持以及高效的性能来创建...

    网络游戏开发_北航课件

    网络游戏开发是一个涵盖多个技术领域的复杂过程,涉及到网络通信、图形渲染、游戏逻辑、数据库管理、人工智能等多个方面。北京航空航天大学的这门课程旨在系统地教授这些核心知识点,为学生提供全面的网络游戏开发...

    网络游戏开发——论文

    网络游戏开发是一个涵盖广泛的技术领域,涉及众多的编程语言、数据结构、算法以及系统设计。本文将深入探讨网络游戏开发的关键知识点,结合给定的文件标题、描述及标签,我们将重点讨论3D网络游戏设计的位置引擎、...

    Unity3d网络游戏实战 unity坦克 多人联网

    本教程通过实际操作制作一款多人联网的坦克大战游戏,旨在帮助读者掌握Unity3D在网络游戏开发中的应用技巧。 首先,Unity3D是一个强大的跨平台游戏开发引擎,它支持2D和3D游戏制作,广泛应用于手机游戏、桌面游戏...

    网络游戏-倾斜模型和多元模型动态融合的自适应网络可视化方法.zip

    在网络游戏领域,网络可视化是一种重要的技术,用于呈现复杂的网络数据,帮助开发者理解、优化和调试游戏网络架构。本文档“倾斜模型和多元模型动态融合的自适应网络可视化方法”聚焦于如何有效地结合不同类型的网络...

    网络游戏服务器端开发,是你学习网络游戏开发入门的好帮手

    网络游戏服务器端开发是游戏开发领域中的一个重要组成部分,它涉及到网络通信、并发处理、实时性以及数据同步等关键问题。本书“Visual C++网络游戏建模与实现”无疑为初学者提供了一个很好的入门平台,通过C++语言...

    网络游戏开发导论(vc源码)

    网络游戏开发是一个涵盖多个技术领域的复杂过程,而"网络游戏开发导论(vc源码)"则可能是一个基于Visual C++(简称VC)的初级教程或项目,旨在帮助初学者理解和实践游戏开发的基础概念。Visual C++是一款强大的编程...

    神经网络_在游戏中应用

    在游戏开发中,神经网络可以用于以下场景: 1. 角色学习:让游戏角色通过观察和交互学习玩家的策略,提高其在游戏中对抗的能力。 2. 智能路径规划:利用神经网络解决迷宫问题,让游戏角色找到最短或最优路径。 3. ...

    网络游戏-一种网络模型校验方法.zip

    在IT行业中,网络游戏是一种复杂而综合的技术领域,它涉及到网络通信、数据同步、服务器架构、客户端渲染等多个方面。本文将重点解析标题为“网络游戏-一种网络模型校验方法”的资料,该资料可能详细阐述了一种用于...

    网络游戏-三维游戏模型数据处理方法及网络游戏系统.zip

    在网络游戏领域,三维游戏模型数据处理是至关重要的技术环节,它直接影响到游戏的视觉效果、性能和用户体验。本文将深入探讨这一主题,基于"网络游戏-三维游戏模型数据处理方法及网络游戏系统.zip"中的资料,主要...

    unity 3D 游戏开发 家具模型 (可商用)

    在Unity 3D游戏开发中,使用高质量的3D模型是构建沉浸式游戏世界的关键步骤。这个名为"家具模型(可商用)"的资源包专为游戏开发者设计,提供了丰富的家具和建筑材料的3D模型,同时也考虑到了商业用途的合法性。 ...

    网络游戏的开发与设计 毕业设计

    网络游戏的开发与设计是一项复杂而综合的工作,涵盖了多个IT领域的知识。这个毕业设计项目包括了服务器和...对于想进入游戏行业的学生或者开发者来说,这是一个极好的学习和实践平台,可以深入理解网络游戏开发的全貌。

    网络游戏-一种桌面游戏的版面及模型.zip

    在网络游戏的开发过程中,设计师会采用一些常见的模型来简化复杂的游戏系统。例如,经济模型用于平衡游戏内的交易和资源;进度模型决定玩家如何升级和解锁新内容;社交模型促进玩家间的互动和合作。这些模型有助于...

    图形 与 网络游戏 开发

    在“图形与网络游戏开发”这一主题中,我们深入探讨了计算机图形学和网络技术在创建交互式游戏中的应用。此资料包包含丰富的学习资源,如PPT课件、源代码示例以及Word文档中详尽的课后习题解答,为初学者提供了全面...

    游戏开发程序游戏开发程序代码

    游戏开发是计算机科学领域的一个重要分支,涉及到许多不同的技术和编程语言。在这个压缩包中,"游戏开发程序游戏开发程序代码"的标题暗示我们将会探讨与创建游戏相关的代码和项目。让我们深入了解一下游戏开发的一些...

    VC++开发的3D网络游戏<>

    《VC++开发的3D网络游戏圣战&gt;&gt;》是一个深入探讨使用Microsoft Visual C++(简称VC++)编程语言进行3D网络游戏开发的项目实例。这个项目不仅包括了游戏服务端和客户端的完整源代码,还提供了详尽的说明文档和开发日志,...

    《游戏开发大全》经典巨作

    在游戏开发领域,C++因其高效、灵活和面向对象的特性,成为了许多大型游戏开发的首选语言。书中可能首先介绍了C++的基础知识,包括类、对象、继承、多态等概念,这些都是游戏开发中的核心要素。理解并熟练掌握这些...

Global site tag (gtag.js) - Google Analytics