??xml version="1.0" encoding="utf-8" standalone="yes"?>伊人久久无码中文字幕,亚洲国产成人乱码精品女人久久久不卡 ,成人亚洲欧美久久久久http://www.shnenglu.com/jinq0123/archive/2022/11/18/229525.html金庆金庆Fri, 18 Nov 2022 02:03:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/11/18/229525.htmlhttp://www.shnenglu.com/jinq0123/comments/229525.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/11/18/229525.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229525.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229525.htmlHow are dtLinks created in NavMesh

(Jin Qing's Column, Nov., 2022)

dtLink defines a link between 2 polygons. It is an internal data structure.

A dtLink is owned by its source polygon.

The links are used in findPath(), to search all the neighbor polygons, from the current best polygon.

dtLink has 2 required fields:

  • neighbor: the neighbor polygon reference that is linked to.
  • edge: index of the polygon edge that owns this link. It is used in getPortalPoints() and raycast().

If the link is a boundary link, which links to other tile, then it has more fields:

  • side: defines on which side the link is. 8 sides. It is used in findPath() and raycast().
  • bmin: defines the minimum sub-edge area.
  • bmax: defines the maximum sub-edge area. bmin and bmax are used in portal finding.

Links are not saved to navmesh file. They are created on navmesh loading in addTile().

dtNavMesh::addTile() add a tile to the navmesh. A tile is a square block of the map.

The main job of dtNavMesh::addTile() is to create links inside this tile and between this tile and other tiles. Actually a block indexed by (x, y) of constant size has many layers for multi-floor map. A dtMeshTile is actually a layer with an index of (x, y, layer).

5 steps to create all links:

connectIntLinks() iterates all the polygons in the tile and create links for them by the help of neighbors information of the polygons.

2. Base off-mesh connections

baseOffMeshLinks() bases off-mesh connections to their starting polygons and connect connections inside the tile.

Off-mesh connection is between 2 points which are inside a tile or between 2 adjacent tiles.

For each off-mesh connection, there creates a specific polygon consisting of 2 vertices. See DT_POLYTYPE_OFFMESH_CONNECTION.

baseOffMeshLinks() creates 2 links for each off-mesh connection:

  • From the off-mesh connection polygon to the source polygon
  • From the source polygon to the off-mesh connection polygon

The destinaton polygon of the off-mesh connection is skipped here.

connectExtOffMeshLinks() with the source and target tile be this tile.

connectExtOffMeshLinks() searches off-mesh connections that are from this tile to the target tile by checking the side direction of the connection.

It creates a link from off-mesh connection polygon to the target tile. For bidirectional connection, it also creates a link from the target polygon to the off-mesh connection polygon.

So for each off-mesh connection of this tile, 3 or 4 links are created by baseOffMeshLinks() and connectExtOffMeshLinks(), one on the source polygon, one on the destination polygon and 1/2 on the off-mesh connection polygon.

4. Connect with layers in current tile

For each layer other than this layer in this tile:

  • Create links from this layer to other layer
  • Create links from other layer to this layer
  • Create links of the off-mesh connection from this layer to other layer
  • Create links of the off-mesh connection from other layer to this layer

5. Connect with neighbour tiles

Check 9 neighbor tiles' layers, and create links just like previous step:

  • Create links from this layer to neighnor layer
  • Create links from neighnor layer to this layer
  • Create links of the off-mesh connection from this layer to neighnor layer
  • Create links of the off-mesh connection from neighnor layer to this layer

By now, for every off-mesh connection of all the loaded tiles, 3/4 links are created.

Reference

  • https://blog.csdn.net/icebergliu1234/article/details/80322392


金庆 2022-11-18 10:03 发表评论
]]>
UE4 Blueprint Multiple Event BeginPlayhttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217765.html金庆金庆Sat, 31 Jul 2021 07:19:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217765.htmlhttp://www.shnenglu.com/jinq0123/comments/217765.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217765.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217765.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217765.html
(金庆的专?2021.7)

How to do multiple actions on Event BeginPlay in UE4 Blueprints?

Sams-Teach-Yourself-Unreal-Engine-4-Game-Development-in-24-Hours says:
```
Q. When I try to add a second event node, BeginPlay, the Editor shows me the first one already
placed in the Event Graph. Why does this happen?

A. Some events, such as Event Tick and the BeginPlay event, can have only one instance per
Blueprint.
```

https://forums.unrealengine.com/t/do-multiple-things-on-event-begin-play/411/10
```
The Sequence node allows for a single execution pulse to trigger a series of events in order. The node may have any number of outputs, all of which get called as soon as the Sequence node receives an input. They will always get called in order, but without any delay. To a typical user, the outputs will likely appear to have been triggered simultaneously.
```

Youtube video: [How to do Multiple Actions on Event Begin Play Unreal Engine 4 Blueprints](https://www.youtube.com/watch?v=nqG-ztbs230)

金庆 2021-07-31 15:19 发表评论
]]>
W?代游戏主?/title><link>http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sun, 09 May 2021 12:44:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/217680.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/217680.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/217680.html</trackback:ping><description><![CDATA[# W?代游戏主?br /><br />原文Q[Ninth generation of video game consoles](https://en.wikipedia.org/wiki/Ninth_generation_of_video_game_consoles)<br /><br />2020.11Q微?MS) Xbox Series X/S ?Sony PlayStation 5 (PS5) 发布Q标志着游戏Lq入W?代?br /><br />和前代的 Xbox One ?PS4 相比Q新一代主机有可观的性能提升Q支持实时光U跟t,4K分L率,目标帧率?0?br />内部都用了固态硬?SSD)。低配版没有光驱Q仅支持|络和USB?br /><br />定位上要胜过d堂Switch和云游戏服务?Stadia ?Amazon Luna.<br /><br />## 背景<br /><br />W?代时间较ѝ因为摩定律,q去几代一般每代ؓ5q时_但是 MS ?Sony Z中间代?Xbox One X ?PS4 Pro.<br /><br />2020.3 开始的 COVID-19 疫情影响也C代主机的发布延后了?br /><br />## 主要L<br /><br />### PS5<br /><br />?个机型,基本型和数字型,数字型没有光p便宜Q其他一栗?br />PS5和PS4的游戏兼容,只有量游戏不支持,但是可以通过 PS Now 云游戏服务玩 PS4 游戏?br /><br />### Xbox Series X/S<br /><br />MS 延了双L模式Q高端的Xpd和低端的Spd。Spd没有光驱?br />两者都支持外部存储和Xbox Live在线分发。向后兼容以前的游戏Q但不包括Kinect游戏?br />MS鼓励开发商使用 Smart DeliveryQ把Xbox One游戏升到Xbox Series X/S?br /><br />## 其他L<br /><br />* d?Switch<br />* 云游戏^収ͼStadia, Amazon Luna, GeForce Now<img src ="http://www.shnenglu.com/jinq0123/aggbug/217680.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2021-05-09 20:44 <a href="http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Canvas Scaler ?U模?/title><link>http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Tue, 02 Mar 2021 04:00:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/217621.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/217621.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/217621.html</trackback:ping><description><![CDATA[Canvas Scaler ?U模?br /><br />(金庆的专?2021.3)<br /><br />参考:<br />https://wenku.baidu.com/view/83991090336c1eb91b375db8.html<br /><br />Unity Canvas 有个 Canvas Scaler lgQ用来决?Canvas ?Scale(~放)倹{?br />Scale ?X,Y,Z 3个分量,都是同一|卛_各个方向上都是同倍率~放的?br />它有3UScale Mode?br /><br />因ؓ设计时的宽高比与目标机型宽高比不同,Canvas 需要先增减为目标高宽比Q然后再~放?br />Canvas ~放q不影响UI元素的相互位|,只是同比~放?br />但是高宽比调整时会造成UI元素的相对位|变化,如宽度增大后QUI元素会在宽度方向上相互散开?br /><br />可以?Unity Editor Game 左上角分L率下拉菜单中创徏几个低分辨率屏,用来试屏幕大小切换?br />试界面可放|?个按钮,一个锚定到左上Q位于左上,一个锚定到右下Q位于右下,不要strech?br /><br />3U模式如下?br /><br />## Constance Pixel Size<br /><br />Constance Pixel Size 模式下,Scale L??br />目标Z?Canvas 是其分辨率大小?br />因ؓ设计时分辨率与实际分辨率不同Q显CZ不同?br />但是囄和按钮的大小保持固定像素大小?br />如果屏幕变大QUI元素׃怺散开Q变则聚拢?br />如果需要羃放,只能通过E序调节?br /><br />## Scale With Screen Size<br /><br />随屏q大羃放?br />该模式下有个 Reference Resolution (参考分辨率)Q应该设Z机型的分L率?br />在主机型下QScale ?Q与设计昄相同?br /><br />https://blog.csdn.net/u011948420/article/details/112641308<br />2020中国Ud游戏质量白皮?WeTest 报告 Android 手机 TOP300 分L率ؓ 2340*1080 ?31%?br /><br />如果目标机型?4680*2160Q则 Scale 正好?Q如果目标机型ؓ 1179*540Q则Scale?.5.<br /><br />一般情况下宽高比不同,此时Scale法?U,?USceen Match ModeQ?br /><br />### Match Width Or Height<br /><br />匚w宽度或高度?br /><br />此时有个 Match 划动条,可以?Width(0)..Height(1)之间?br />如果?Q匹配宽度,?Scale 为目标屏宽与参考屏宽之比;<br />如果?Q匹配高度,?Scale 为目标屏高与参考屏高之比;<br />如果?.5, 加权匚wQ则是这2个比值的q_倹{?br /><br />匚w宽度Ӟ高度方向上会有UI扩散或聚拢?br />匚w高度Ӟ宽度方向上会有UI扩散或聚拢?br />加权匚wӞUI在高上扩散宽上聚拢,或者在高上聚拢宽上扩散?br /><br />### Expand<br /><br />增加。先增加宽或高到目标宽高比。该增加方向UI元素会散开?br /><br />### Shrink<br /><br />减少。先减少宽或高到目标宽高比。该减少方向UI元素会聚拢?br /><br />## Constant Physical Size<br /><br />固定物理大小?br />随着4k屏的出现Q屏qDPI会很大,许多讑֤的像素点非常?br />同样的按钮,在高DPI下,如果固定像素大小Q则会非常小Q这旉合用该模式?br /><br /><img src ="http://www.shnenglu.com/jinq0123/aggbug/217621.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2021-03-02 12:00 <a href="http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>帧同步是否允许客L指定命o帧号http://www.shnenglu.com/jinq0123/archive/2020/10/08/217474.html金庆金庆Thu, 08 Oct 2020 03:39:00 GMThttp://www.shnenglu.com/jinq0123/archive/2020/10/08/217474.htmlhttp://www.shnenglu.com/jinq0123/comments/217474.htmlhttp://www.shnenglu.com/jinq0123/archive/2020/10/08/217474.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217474.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217474.html
(金庆的专?2020.10)

帧同步服务器以固定的帧率攉每个客户端的输入命oQ每帧打包一个命令Q打上服务器当前帧号Q?br />然后q播该命令到每个客LQ由客户端执行?br />如果没有客户端输入命令,则该命o帧可能只有一个受?br />
客户端的输入命o?U模式:不指定号和指定帧号?br />
客户端不指定帧号的模式下Q客L仅告诉服务器自己的动作,由服务器动作打上当前的服务器帧号Q然后再q播?br />服务器实现较单,只需要缓存当前的所有客L输入Q下一帧时全部打包q播?br />
指定帧号的模式下Q客L要求该命令在指定?服务器)才生效?br />服务器需要缓存该命oQ运行到指定帧号Ӟ才将该命令打包到命o帧ƈq播所有客L?br />
指定帧号在客L有预回滚时较ؓ有利Q因为客L知道自己的命令将在确定的帧执行,卛_自己的预L成功的?br />而如果不指定帧号Q客L需要预自q命o会在哪个服务器到达服务器,而这在网l抖动较大时会预失败?br />
指定帧号会造成命o的g时时间较ѝ而不指定帧号会立即执行?br />
如果|络堵塞了一会儿Q客L发送的命o延时了较长时间才到达服务器,
指定的号可能小于服务器当前帧号Q这时服务器对该命o?U处理方?
一U是直接忽略该命令,因ؓ该命令已无法实现?br />一U是量实现该命令,卛_当前帧执行?br />考虑到客L预测Q?U都是预失败,同样需要回滚,但是在当前执行可能回滚造成的抖动会一炏V?br />如v跛_令,一U是已经跌v来了被拉回地面,因ؓ赯命o被取消了Q一U是跛_IZ停顿了一下,因ؓ赯的时间点被g后了?br />量实现命o应该比丢弃命令更好一炏V?br />
如果是在当前服务器执行q期帧号的命令,那么q?U模式可以合q成一U,x有命令都是指定P
只是有的帧号?Q表C服务器在当前帧执行?br />
通用的同步服务器应该让客户端指定受?br />
指定帧号的命令可以实C格时间间隔的命o序列Q?br />客户端可以一ơ性发送整个命令序列,指定每个命oZ同的帧号?br />q应该允许客L指定命o序列的间隔Q但是首命o是立x行的?br />命o序列的号应该是相对于首命o的?br />如果命o序列的首命o延后执行Q那么整个序列全部将同样延后?br />
客户端是否需要知道自q指定帧号命o被g后执行了Q这样可以让客户端有更好的表玎ͼ
那么成功执行的指定号命令是否也应该让客L知道Q?br />服务器仅仅是客L命o原样q播Q客L命o中可以加入命令序P自行判断命o是否延后了?br />
是否一个玩家的提前发送的指定帧命令提前广播给其他玩家Q?br />q样其他玩家不仅对自己预成功,对该玩家的预也成功?br />但是泄露了自己将要执行的动作会被其他玩家利用?br />所以提前量不能太大Q指定号应该尽量接q服务器帧号?br />服务器就不用~存客户端指令了Q直接收到后q播卛_?br />q样服务器广播的命o帧中有服务器帧号Q也有客L的指定受?br />服务器也׃用管客户端是否指定号了?br />指定帧号和解析成为纯客户端逻辑?br />




金庆 2020-10-08 11:39 发表评论
]]>
rpc应答太快造成h时http://www.shnenglu.com/jinq0123/archive/2020/09/17/217453.html金庆金庆Thu, 17 Sep 2020 07:59:00 GMThttp://www.shnenglu.com/jinq0123/archive/2020/09/17/217453.htmlhttp://www.shnenglu.com/jinq0123/comments/217453.htmlhttp://www.shnenglu.com/jinq0123/archive/2020/09/17/217453.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217453.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217453.html
(金庆的专?2020.9)

在压中发现L几个h时Q超时时长设大也会有Q而成功的h延时q小于超时时间?br />查错的第一方向是查|络库中有消息丢失?br />跟踪所有消息,发现时的消息应该是正常处理q返回了?br />于是查接收应{消息后的处理,最l找C码:
```go
func (c *Client) onRpcRet(cbIndex uint32, ...) {
    ii, ok := c.callbacks.Load(cbIndex)
    if !ok {
        // logger.Errorf("onRpcRet can not find cbIndex %d", cbIndex) // 可能是超时已?br />        return
    }
```
打开日志Q发现超时的h对应有该条错误日志?br />此处回调不存在的情况Q正常是先超时删除回调,然后再收到应{?br />现在是先收到了应{,发现找不到回调,然后q了一D|间会被判时无响应?br />
下面代码换个次序就好了Q?br />```go
    if err := c.Session.Send(msg); err != nil {
        ...
        return
    }
    c.callbacks.Store(cbIndex, ...)
```
改ؓ
```go
    // 必须先设回调Q然后发送,因ؓ应答可能会很?br />    c.callbacks.Store(cbIndex, ...)
    if err := c.Session.Send(msg); err...
```

压测时因为加压机CPU是满负蝲q{Q所?Send() ?Store() 之间可能会间隔数毫秒Q?br />_ rpc h处理完成q返回,而应{返回时回调q没讄?br />
?Send() ?Store() 写代码会E微单点Q因?Send() p|后可以直接返回?br />?Store() ?Send() ӞSend() p|则需要相?Delete().



金庆 2020-09-17 15:59 发表评论
]]>
试 tolua 例子 TestErrorStackhttp://www.shnenglu.com/jinq0123/archive/2020/09/03/217437.html金庆金庆Thu, 03 Sep 2020 08:29:00 GMThttp://www.shnenglu.com/jinq0123/archive/2020/09/03/217437.htmlhttp://www.shnenglu.com/jinq0123/comments/217437.htmlhttp://www.shnenglu.com/jinq0123/archive/2020/09/03/217437.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217437.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217437.html
(金庆的专?2020.9)

## Error1

1. 点击 "Error1" 按钮
1. c# showStack.PCall()
1. lua ShowStack()
1. c# Test1()
1. c# try { show.PCall() }
1. lua Show() error('this is error')                

```
LuaException: TestErrorStack:2: this is error
stack traceback:
    [C]: in function 'error'
    TestErrorStack:2: in function <TestErrorStack:1>
    [C]: in function 'Test1'
    TestErrorStack:6: in function <TestErrorStack:5>
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:758)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:Test1(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:27)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:375)
```

lua Show() 中抛 error, ?C# ?try-catch 得到Q?br />通过 toluaL_exception() q回 Lua 调用?ShowStack()Q?br />ShowStack() 中止执行Q传递异常到 c# 调用?OnGUI(),
OnGUI()中断执行Q打印错误信息?br />
## Instantiate Error

1. "Instantiate Error"
1. c# GetFunction("Instantiate").PCall()
1. lua Instantiate()
1. c# UnityEngine.Object.Instantiate(obj)
1. c# TestInstantiate.Awake()
1. c# try { GetFunction("Show").PCall() }
1. lua Show()
1. c# state.ThrowLuaException(e)

```
LuaException: TestErrorStack:2: this is error
stack traceback:
    [C]: in function 'error'
    TestErrorStack:2: in function <TestErrorStack:1>
    [C]: in function 'Instantiate'
    TestErrorStack:12: in function <TestErrorStack:11>
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:758)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestInstantiate:Awake() (at Assets/ToLua/Examples/TestErrorStack/TestInstantiate.cs:15)
UnityEngine.Object:Instantiate(Object)
UnityEngine_ObjectWrap:Instantiate(IntPtr) (at Assets/ToLua/BaseType/UnityEngine_ObjectWrap.cs:203)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:391)
```
?lua 中实例化对象QAwake() 中向lua抛异常:state.ThrowLuaException(e)?br />中止?lua 调用?OnGUI(), 但是新对象的 Start() 成功调用了?
因ؓ Awake() ?catch 了异常,按执行成功处理?br />如果 Awake() 中不 catch, Awake() 执行异常Q也不会?Start() 调用Q但是lua Instantiate() 执行会成功,打印出对象名?br />
## Check Error

1. "Check Error"
1. c# GetFunction("TestRay").PCall()
1. lua TestRay() return Vector3.zero
1. c# CheckRay();  //q回值出?br />
```
LuaException: bad argument #2 (Ray expected, got Vector3)
LuaInterface.LuaDLL:luaL_argerror(IntPtr, Int32, String) (at Assets/ToLua/Core/LuaDLL.cs:692)
LuaInterface.LuaDLL:luaL_typerror(IntPtr, Int32, String, String) (at Assets/ToLua/Core/LuaDLL.cs:706)
LuaInterface.LuaStatePtr:LuaTypeError(Int32, String, String) (at Assets/ToLua/Core/LuaStatePtr.cs:534)
LuaInterface.LuaState:CheckRay(Int32) (at Assets/ToLua/Core/LuaState.cs:1505)
LuaInterface.LuaFunction:CheckRay() (at Assets/ToLua/Core/LuaFunction.cs:781)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:403)
```

## Push Error

1. "Push Error"
1. c# func.Push(Instance);

```
14:04:31.371-157: Type TestLuaStack not wrap to lua, push as UnityEngine.MonoBehaviour, the warning is only raised once
UnityEngine.Debug:LogWarning(Object)
LuaInterface.Debugger:LogWarning(String)
LuaInterface.Debugger:LogWarning(String, Object, Object)
LuaInterface.LuaState:GetMissMetaReference(Type) (at Assets/ToLua/Core/LuaState.cs:1917)
LuaInterface.LuaStatic:GetMissMetaReference(IntPtr, Type) (at Assets/ToLua/Core/LuaStatic.cs:39)
LuaInterface.ToLua:LoadPreType(IntPtr, Type) (at Assets/ToLua/Core/ToLua.cs:2608)
LuaInterface.ToLua:PushUserObject(IntPtr, Object) (at Assets/ToLua/Core/ToLua.cs:2622)
LuaInterface.ToLua:Push(IntPtr, Object) (at Assets/ToLua/Core/ToLua.cs:2636)
LuaInterface.LuaState:Push(Object) (at Assets/ToLua/Core/LuaState.cs:1378)
LuaInterface.LuaFunction:Push(Object) (at Assets/ToLua/Core/LuaFunction.cs:465)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:412)
```

## LuaPushError

1. "LuaPushError"
1. c# GetFunction("PushLuaError").PCall()
1. lua PushLuaError()
1. lua TestStack.PushLuaError()
1. c# PushLuaError()
1. c# try { testRay.Push(Instance); }

仅是警告Q没有异?br />
## Check Error

1. "Check Error"
1. c# GetFunction("Test5").PCall()
1. lua Test5()
1. lua TestStack.Test5()
1. c# Test5()
1. c# GetFunction("Test4").PCall()
1. lua TestStack.Test4()
1. c# Test4()
1. c# try { show.PCall() }

```
LuaException: TestErrorStack:2: this is error
stack traceback:
    [C]: in function 'error'
    TestErrorStack:2: in function <TestErrorStack:1>
    [C]: in function 'Test4'
    TestErrorStack:30: in function <TestErrorStack:29>
    [C]: in function 'Test5'
    TestErrorStack:34: in function <TestErrorStack:33>
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:758)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:Test4(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:85)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:Test5(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:102)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:434)
```

## Test Resume

1. "Test Resume"
1. c# GetFunction("Test6").PCall()
1. lua Test6()
1. lua 协程中调?TestStack.Test6(go)
1. c# toluaL_exception()

lua coroutine resume() q回 false, 不会有错误?br />
## out of bound

1. "out of bound"
1. c# GetFunction("TestOutOfBound").PCall()
1. c# TestOutOfBound()
1. c# toluaL_exception(L, e)

```
LuaException: Transform child out of bounds
stack traceback:
    [C]: at 0x613c2af0
TestLuaStack:TestOutOfBound(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:136)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:454)
```

## TestArgError

1. "TestArgError"
1. c# GetFunction("Test8").PCall()
1. lua Test8()
1. lua TestArgError()
1. c# TestArgError()
1. c# toluaL_exception(L, e)

```
LuaException: TestErrorStack:69: bad argument #1 to 'TestArgError' (number expected, got string)
stack traceback:
    [C]: in function 'TestArgError'
    TestErrorStack:69: in function <TestErrorStack:68>
LuaInterface.LuaDLL:luaL_argerror(IntPtr, Int32, String) (at Assets/ToLua/Core/LuaDLL.cs:692)
LuaInterface.LuaDLL:luaL_typerror(IntPtr, Int32, String, String) (at Assets/ToLua/Core/LuaDLL.cs:706)
TestLuaStack:TestArgError(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:151)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:463)
```

## TestFuncDispose

1. "TestFuncDispose"
1. c# func.Dispose();
1. c# func.PCall();

```
LuaException: LuaFunction has been disposed
LuaInterface.LuaFunction:BeginPCall() (at Assets/ToLua/Core/LuaFunction.cs:73)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:473)
```

## SendMessage

1. "SendMessage"
1. c# gameObject.SendMessage("OnSendMsg");
1. c# OnSendMsg()
1. c# try { GetFunction("TestStack.Test6").PCall() }
1. c# Test6()
1. c# toluaL_exception(L, e);

```
LuaException: this a lua exception
stack traceback:
    [C]: at 0x613c2af0
    [C]: in function 'TestArgError'
    TestErrorStack:69: in function <TestErrorStack:68>
TestLuaStack:Test6(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:122)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnSendMsg() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:277)
UnityEngine.GameObject:SendMessage(String)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:481)
```

SendMessage() {效于直接调用?

## SendMessageInLua

1. "SendMessageInLua"
1. c# GetFunction("SendMsgError").PCall()
1. lua SendMsgError(go)
1. lua go:SendMessage("OnSendMsg");

```
LuaException: this a lua exception
stack traceback:
    [C]: at 0x613c2af0
    [C]: in function 'SendMessage'
    TestErrorStack:38: in function <TestErrorStack:37>
    [C]: in function 'TestArgError'
    TestErrorStack:69: in function <TestErrorStack:68>
TestLuaStack:Test6(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:122)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnSendMsg() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:277)
UnityEngine.GameObject:SendMessage(String)
UnityEngine_GameObjectWrap:SendMessage(IntPtr) (at Assets/Source/Generate/UnityEngine_GameObjectWrap.cs:657)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:488)
```

?"SendMessage" 差不多一栗?br />
## AddComponent

1. "AddComponent"
1. c# GetFunction("TestAddComponent").PCall()
1. c# TestAddComponent()
1. c# try { go.AddComponent<TestInstantiate2>(); }
1. c# TestInstantiate2.Awake()
1. c# state.ThrowLuaException(e);

```
Exception: Instantiate exception 2
LuaInterface.LuaStatePtr.ThrowLuaException (System.Exception e) (at Assets/ToLua/Core/LuaStatePtr.cs:607)
TestInstantiate2.Awake () (at Assets/ToLua/Examples/TestErrorStack/TestInstantiate2.cs:16)
UnityEngine.GameObject:AddComponent()
TestLuaStack:TestAddComponent(IntPtr) (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:237)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:498)
```

## TableGetSet

1. "TableGetSet"

## TestTableInCo

1. "TestTableInCo"
1. c# GetFunction("TestCoTable").PCall()
1. lua TestCoTable()
1. lua q行协程 TestCo(...)
1. lua TestTableInCo(...)
1. c# TestTableInCo()

## Instantiate2 Error

1. "Instantiate2 Error"
1. c# GetFunction("Instantiate").PCall() with go2
1. lua Instantiate()
1. lua UnityEngine.Object.Instantiate(obj)
1. c# TestInstantiate2:Awake()

```
LuaException: Instantiate exception 2
stack traceback:
    [C]: in function 'Instantiate'
    TestErrorStack:13: in function <TestErrorStack:11>
    [C]: in function 'TestArgError'
    TestErrorStack:69: in function <TestErrorStack:68>
TestInstantiate2:Awake() (at Assets/ToLua/Examples/TestErrorStack/TestInstantiate2.cs:11)
UnityEngine.Object:Instantiate(Object)
UnityEngine_ObjectWrap:Instantiate(IntPtr) (at Assets/ToLua/BaseType/UnityEngine_ObjectWrap.cs:203)
LuaInterface.LuaDLL:lua_pcall(IntPtr, Int32, Int32, Int32)
LuaInterface.LuaState:PCall(Int32, Int32) (at Assets/ToLua/Core/LuaState.cs:755)
LuaInterface.LuaFunction:PCall() (at Assets/ToLua/Core/LuaFunction.cs:96)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:573)
```

## Instantiate3 Error

1. "Instantiate3 Error"
1. c# UnityEngine.Object.Instantiate(go2);
1. c# TestInstantiate2.Awake()

```
Exception: Instantiate exception 2
LuaInterface.LuaStatePtr.ThrowLuaException (System.Exception e) (at Assets/ToLua/Core/LuaStatePtr.cs:607)
TestInstantiate2.Awake () (at Assets/ToLua/Examples/TestErrorStack/TestInstantiate2.cs:16)
UnityEngine.Object:Instantiate(GameObject)
TestLuaStack:OnGUI() (at Assets/ToLua/Examples/TestErrorStack/TestLuaStack.cs:580)
```

## TestCycle

1. "TestCycle"
1. c# GetFunction("TestCycle").PCall()
1. c# TestCycle()

试递归调用 luaFunction

## TestNull

1. "TestNull"
1. c# StartCoroutine(TestCo(action));

c# 中创建协E?br />
## TestMulti

1. "TestMulti"
1. c# GetFunction("TestMulStack").PCall()
1. c# TestMulStack()
1. c# try { TestMul0(); }
1. c# TestMul1()
1. throw


金庆 2020-09-03 16:29 发表评论
]]>
lua变量~少local造成unity死锁http://www.shnenglu.com/jinq0123/archive/2020/09/02/217435.html金庆金庆Wed, 02 Sep 2020 05:37:00 GMThttp://www.shnenglu.com/jinq0123/archive/2020/09/02/217435.htmlhttp://www.shnenglu.com/jinq0123/comments/217435.htmlhttp://www.shnenglu.com/jinq0123/archive/2020/09/02/217435.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217435.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217435.html
(金庆的专?2020.9.1)

q几天在开?grpc-tolua, 可以?unity 目中?lua 发?grpc hQ无需生成代码.
现已实现 route_guide CZ中的全部4Urpc.
见:https://github.com/jinq0123/grpc-tolua

昨天的开发中发玎ͼq行 RecordRoute ?RouteChat 之后Unity无法再次q行Q必Lq程?br />?await 语句d `ConfigureAwait(false)` 之后Q好?RouteChat 修复了,?RecoreRoute 依旧?br />
ConfigureAwait(false) 保证?await 之后不会抢占ȝE,是解x锁的一U方法?br />grpc 源码中的 await 同样都跟着q句?br />
今天再次试Q发?RouteChat q未真正修复Q仍有较大可能卡歅R?br />
最初的猜想?Lua 协程QC# 异步造成U程死锁Q因为对异步不熟QL怀疑用有误?br />后来转变为查找让 Unity 卡死的阻塞式操作Q但是因为日志显CZ全,N代码都没l果?br />除了在自q代码中查N误,q在 tolua 代码Qgrpc 代码中查找,到处怀疑?br />今天一整天都在d日志Q重启Unity中度q。提出各U猜惻I又反复验证?br />
依次采用了以下方法:
* 比较C# route_guide 代码Q查扑֌?br />* Z么是 client streaming cd?rpc 有错
* 查看 tolua 协程QTimer
* d Delay, 更改协程的运行次序,提高或降低复现几?br />* 函数入口和返回添加日志,证明其没有阻?br />
查找错误的方法是在代码中d日志Q找到异怹处?br />最l的现象是Unity没有Update()调用了,全部卡死?br />
错误源代码是q样的,错误版本?awaiter 前面没有 local.
```lua
local function await(awaitable)
    local awaiter = awaitable:GetAwaiter()
    coroutine.wait_until(function()
        return awaiter.IsCompleted
    end)
end
```

因ؓ惛_C个特D版本的 await() 用于试, ?awaiter.IsCompleted 改ؓL false?br />复制代码Ӟ发现了变量缺?local.
如果q个成ؓ全局变量Q第2ơ函数调用就会更改第1ơ调用的变量Q我立刻意识Cq就是死锁的原因?br />d local 之后试Q一切都正常了?br />
分析l果是因?awaiter 更改后,await() 在Q务完成前p回了Q?br />以致于后面的d成ؓd式操作,d了主U程?br />
查错效率低的主要问题?Unity 日志在卡d的日志没有显C,q一点直到最后死锁复盘时才发觉?br />因ؓ日志~失Q根据日志得出的所有判断几乎都错了?br />本来可以Ҏ日志快速定位的原因Q现在完全找错方向?br />
如果有工兯告全局变量Q就可以避免q个错误?br />
Unity Profiler, VS Debugger 因ؓ Unity 完全卡死Q所以没什么帮助?br />

金庆 2020-09-02 13:37 发表评论
]]>
C# tolua 之间互传 byte[]http://www.shnenglu.com/jinq0123/archive/2020/08/19/217426.html金庆金庆Wed, 19 Aug 2020 01:03:00 GMThttp://www.shnenglu.com/jinq0123/archive/2020/08/19/217426.htmlhttp://www.shnenglu.com/jinq0123/comments/217426.htmlhttp://www.shnenglu.com/jinq0123/archive/2020/08/19/217426.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217426.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217426.html
(金庆的专?2020.8)

lua中不区分 string ?byte[], 而在 C# ?string ?byte[] 之间转换涉及~码?br />
C# 中一般这栯{Q?br />
stringcd转成byte[]Q?br />```
byte[] byteArray = System.Text.Encoding.Default.GetBytes(str);
```
byte[]转成stringQ?br />```
string str = System.Text.Encoding.Default.GetString(byteArray);
```

Default ~码是本机当前所用编码,q可以用 ASCII, UTF8 {其他编码?br />
了?lua 中读入一?q制数据Q调?tolua 导出的一个方法,如:
```
public void Print(string s)
{
    byte[] buf = Systen.Text.Encoding.Default.GetBytes(s);
    Debug.Log(Bitconvert.ToString(buf));
}
```
了 Default, ASCII, UTF8, ISO-8859-1(Latin-1), Unicode 发现得到?byte[] 会出错?br />
也试?[C#中用Buffer.BlockCopy()Ҏstring转换为byte array的方法](https://www.cnblogs.com/ChineseMoonGod/p/5689526.html)
发现 tolua 传到 C# ?string 已经是编码过的,直接复制也是错的?br />
xlua 也有相同问题Q[Unity xlua 从lua传递byte[]数据到C#](https://www.jianshu.com/p/63987134c1ba)
使用 MemoryStream对象来传递byte[]数据Q确实有点绕?br />
tolua 中有?LuaByteBufferQ可以用来传?byte[].
[tolua#中的LuaByteBufferc](http://bbs.ulua.org/article/ulua/toluazhongdeluabytebufferlei.html)

?lua ?byte[] ?C#, 只需要将参数 string 改ؓ LuaByteBuffer:
```
public void Print(LuaByteBuffer luaByteBuffer)
{
    byte[] buf = luaByteBuffer.buffer;
    Debug.Log(Bitconvert.ToString(buf));
}
```

更正又单的Ҏ是用 LuaByteBufferAttribute:
```
[LuaByteBufferAttribute]
public void Print(byte[] buf)
{
    Debug.Log(Bitconvert.ToString(buf));
}
```

最l发C需?LuaByteBufferAttributeQ直接用 byte[] pQ?br />```
public void Print(byte[] buf)
{
    Debug.Log(Bitconvert.ToString(buf));
}
```

C# ?byte[] ?lua, 默认?"System.Byte[]"(userdata)Q可以用 tostring() 转ؓ lua string:
```Lua
s = tolua.tolstring(result)
```

如果可以Q应该给数据加上标签[LuaByteBufferAttribute]Q这样传?lua 是 string?br />或者在C#Z个LuaByteBuffer把byte[]传给lua?br />


金庆 2020-08-19 09:03 发表评论
]]>
Unity使用异步grpchttp://www.shnenglu.com/jinq0123/archive/2020/06/22/217366.html金庆金庆Mon, 22 Jun 2020 07:19:00 GMThttp://www.shnenglu.com/jinq0123/archive/2020/06/22/217366.htmlhttp://www.shnenglu.com/jinq0123/comments/217366.htmlhttp://www.shnenglu.com/jinq0123/archive/2020/06/22/217366.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217366.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217366.html
(金庆的专?2020.6)

Unity 保证 async Ҏq行在主U程中,所以用异步方式调用 grpc 可以大大化网l通信的代码?br />
以下CZ中将 grpc ?RouteGuide CZUd Unity 中运行?br />https://github.com/grpc/grpc/tree/master/examples/csharp/RouteGuide

其中 Main() 中的代码Ud Start() 中运行,d调用Ҏ异步调用, GetFeature() Ҏ GetFeatureAsync()?br />
完整代码见:https://gitee.com/jinq0123/unity-grpc-async

```
using Grpc.Core;
using Routeguide;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Routeguide.Program;

public class Test : MonoBehaviour
{
    // Start is called before the first frame update
    async void Start()
    {
        var channel = new Channel("127.0.0.1:50052", ChannelCredentials.Insecure);
        var client = new RouteGuideClient(new RouteGuide.RouteGuideClient(channel));

        // Looking for a valid feature
        await client.GetFeatureAsync(409146138, -746188906);

        // Feature missing.
        await client.GetFeatureAsync(0, 0);

        // Looking for features between 40, -75 and 42, -73.
        await client.ListFeatures(400000000, -750000000, 420000000, -730000000);

        // Record a few randomly selected points from the features file.
        await client.RecordRoute(RouteGuideUtil.LoadFeatures(), 10);

        // Send and receive some notes.
        await client.RouteChat();

        await channel.ShutdownAsync();

        Debug.Log("End of test.");
    }
}
```


金庆 2020-06-22 15:19 发表评论
]]>
有房间匹配和无房间匹?/title><link>http://www.shnenglu.com/jinq0123/archive/2020/04/17/217238.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Fri, 17 Apr 2020 01:52:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2020/04/17/217238.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/217238.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2020/04/17/217238.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/217238.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/217238.html</trackback:ping><description><![CDATA[# 有房间匹配和无房间匹?br /><br />(金庆的专?2020.4)<br /><br />|游的匹配可以分?U:有房间匹配和无房间匹配?br /><br />* 有房间匹?br />    + h匚wx索ƈ加入一个房_或者开一个新戉K?br />    + 匚wq程中可以看到房间h数增加?br />    + 可以列出其他戉KQ加入指定房_如棋牌类大厅<br />    + 可以无需{待人满卛_始,开始后q可以l加?br />* 无房间匹?br />    + h匚w卌入等待状态,匚w成功才能看到所有h<br />    + 匚w成功, 如h满了, 才可以进入游?br />    + 无法加入一个已l开始的游戏<br />    <br />有房间的用户体验好。无戉K匚w可以更精匹配?br />有房间匹配可能需要处理长旉Z满时试合ƈ戉K?br /><br /><br /><img src ="http://www.shnenglu.com/jinq0123/aggbug/217238.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2020-04-17 09:52 <a href="http://www.shnenglu.com/jinq0123/archive/2020/04/17/217238.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>open-match匚w程http://www.shnenglu.com/jinq0123/archive/2019/01/31/216228.html金庆金庆Thu, 31 Jan 2019 02:21:00 GMThttp://www.shnenglu.com/jinq0123/archive/2019/01/31/216228.htmlhttp://www.shnenglu.com/jinq0123/comments/216228.htmlhttp://www.shnenglu.com/jinq0123/archive/2019/01/31/216228.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/216228.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/216228.html

# open-match匚w程


(金庆的专?2019.1)

https://github.com/GoogleCloudPlatform/open-match

open-match 是一个通用的游戏匹配框架?br />由游戏提供自定义的匹配算法(以docker镜像的方式提供)?br />
分ؓ多个q程Q各q程之间׃n一?redis.

* 前端, 接收玩家加入 redisQ成功后通知玩家戉K服地址
* 后端Q设|一局游戏的匹配规则,讄戉K服地址
* MMFOrcQ启动匹配算?MMF)
* MMF, 自定义匹配算法,d redis 获取玩家Q匹配成功就结果写?redis. 仅匹配一局退出?br />
游戏服中q接 open-match 的前端与后端的进E,分别UCؓ frontendclient ?Director?br />输入?部䆾Q一是玩家信息,二是对局信息?br />Director 向后端输入对局信息Q就会收C个接一个的对局人员列表.
Director 需要ؓ每个对局开戉KQ然后通知后端戉K地址?br />后端房间地址写入 redis, 然后前端d到房间地址Q就通知 frontendclientQ让玩家q入戉K?br />

## test/cmd/frontendclient


模拟大厅服或l队服,q接前端API, h匚w玩家/队伍。成功后收到房间服(DGS)的地址(Assignment)?br />
Player 实际上是一个队伍,其中ID字段是用I格分隔的多个ID.
虽然参数cd都是 Player, CreatePlayer() 参数为整个队伍,?GetUpdates() 参数是单个玩家?br />
main() 中创建多个玩Ӟ每个玩家调用 GetUpdates() 以获取结果,go waitForResults() 中处理结果?br />waitForResult() d中的匹配结果,压入 resultsChanQ但好像 resultsChan 仅用于打华ͼ?br />所有玩家合q到 g 实例中,然后调用 CreatePlayer() h匚w?br />
cleanup() 调用 DeletePlayer() 来删除匹配请求,不仅需删除整个队伍Q也需要删除单个玩家?br />
好像最后取l果没取对地方,应该?resultChan 中获?Assignment, q用该地址 udpClient().

看了该示例就可以理解 frontend.proto

## examples/backendclient


MatchObject.Properties 是从 testprofile.json d的,应该改名?Profile 是否更好点?
pbProfile ?MatchObjectQProfile {同?MatchObject?
Profile 的定义是 MMF 所需的所有参数?br />`pbProfile.Properties = jsonProfile` 重复?遍?br />
ListMatches()列出q个Profile的所有匹配?br />收到一个匹配后Q须用CreateAssignments()房间服地址, UCؓ Assignment, 发送到所有游戏客L?br />

## cmd/frontendapi


CreatePlayer() ?Player 对象写入 redis, 键gؓ Player.Id, cd?HSET?br />?Player 的每?attributeQ添加到 ZSET 中去?br />此处 Player 是一l玩家?br />
GetUpdates() 每隔2sdredis, Player数据有变化时发送。此?Player 是单个玩家?br />
如果CreatePlayer()中队伍只有一个玩Ӟ
则写入的Player与GetUpdates()中读取的玩家是同一个redis键?br />

## cmd/backendapi


CreateMatch() ?profile cd?MatchObject, 是一个比赛的限制条g?br />profile 先写?redis, 键ؓ profile.Id.
`requestKey := xid() + "." + profile.Id`,
q将 requestKey 加入 redis 集合 "profileq"?br />然后?s查询 redis, 看是否有 requestKey 键出玎ͼq返回该倹{?br />
ListMatch() ?s调用一?CreateMatch().

DeleteMatch() 仅仅删除 Id q个键?br />
CreateAssignments() 为多个队伍设|Assignment, x间地址?br />遍历所有Roster中的Player对象Q在redis中设|Assignment.
(Assignment 更改后,会触发前端更新?
所?Player.Id ?"proposed" Ud "deindexed"Q这两个?ZSET, 分gؓ加入旉?br />Roster 应该是比赛中的阵营,如红方,蓝方Q每个阵营中可有多个队伍?br />
DeleteAssignments() 仅仅遍历所?Player 对象来删?Assignment 字段?br />

## cmd/mmforc


匚w程是由 mmforc (matchmaking function orchestrator) 控制的?br />
mmforc 每秒?redis ?profileq 中取?100 个成? 其中 profileq 是个setcdQ?br />使用命o为`SPOP profileq 100`.

Ҏ?profile, 创徏一?k8s dQ?br />
```
    // Kick off the job asynchrnously
    go mmfunc(ctx, profile, cfg, defaultMmfImages, clientset, &pool)
```

每隔10s, q有所有匹配Q务都完成后,需?`checkProposals`, 卛_?evaluator d?br />
profileq 中的元素 profile 为字W串QmatchObjectID.profileID?br />?profileID 为键Q可以从 redis d profile 的内? profile 是个 MatchObject 对象?br />
profile 的内容ؓ json Ԍ其中 "jsonkeys.mmfImages" ?mmf (matchmaking function) 镜像?br />
如果profiledp|Q或?mmfImages 为空Q则使用默认的镜像。mmfImages 未来会支持多个镜像?br />
通过 MMF_* 环境变量传入各种参数.

## mmf


CZQexamples\functions\golang\manual-simple

从环境变?"MMF_PROFILE_ID" 解析?profileID, q向 redis 查询(HGETALL) profileQHSET cd?br />
?profile 中取 pools 字段Q即匚w条g?br />pools 分ؓ多个 pool, 每个 pool 中有多个 filter, 每个 filter ?redis 取符合的 Player.

profile 用到以下字段Q?br />
* "properties.playerPool"
  jsonԌ是一些过滤条Ӟ?#8220;mmr: 100-999”
* "properties.roster"
  json? 是多个队伍大,?“red: 4”

CZ见:`examples\backendclient\profiles\testprofile.json`

### 单匹配过E?/h3>
simple mmf 的匹配过E如下:

1. ?redis 查询 profileQ获取过滤条件和各队伍大?br />1. 每个qo条g?redis 查询Q所有结果的交集为可选成?br />1. 去除 ignoreList, xq?800s 内已匚w成功的成员,?proposal ?deindexed ZSET 列表?br />1. 如果可选成员个数太,?insufficient_players q?br />1. 分配各个队伍成员
1. ?redis 记录l果

### l果


profile 中添?rosterQ即各阵营成员名单,存入 prososalKey.
保存不分队伍的成员名单?br />然后?"proposalq" d prososalKey

### l节


poolRosters ?(pool? filter attribute) 为键Qgؓ Player ID 列表.
保存?redis 查询的符合条件的 Player ID.

overlaps ?pool 名ؓ键,保存W合该pool中所有filter?Player ID 列表Q去?ignore list.

rosters ?profile 中的 "properties.rosters" 字段。不知何用?
遍历 rosters, 为每个阵营的每个player扑ֈ对应pool的PlayerID, 保存?mo.Rosters.
其中 profileRosters 好像没用?br />


金庆 2019-01-31 10:21 发表评论
]]>
open-match的redis数据http://www.shnenglu.com/jinq0123/archive/2018/09/28/215964.html金庆金庆Fri, 28 Sep 2018 06:01:00 GMThttp://www.shnenglu.com/jinq0123/archive/2018/09/28/215964.htmlhttp://www.shnenglu.com/jinq0123/comments/215964.htmlhttp://www.shnenglu.com/jinq0123/archive/2018/09/28/215964.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/215964.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/215964.html

open-match的redis数据


(金庆的专?2018.9)

open-match 是Google的开源游戏匹配服框架?br />
https://github.com/GoogleCloudPlatform/open-match

匚w中所用到的玩家数据会保存?redis 中。查?br />`open-match\internal\statestorage\redis\playerq\playerq.go`
可以了解redis保存的数据?br />
redis中有以下键倹{?br />

playerID


玩家ID形如Q`bfd09c94d646493f834a4abe83a5a68c`, cd?hash. 有以下字D:

playerID.properties


保存玩家数据的json丌Ӏ如Q?br />
{
    "char.paladin": 1538102377,
    "map.eastworld": 1538102377,
    "mmr.rating": 1740,
    "mode.ctf": 1538102377,
    "timestamp.enter": 1538102377
}


玩家数据的每值都要求为整?

indices


set cdQ保存所有玩家数据键。如Q?br />
127.0.0.1:6379> smembers indices
1) "timestamp.enter"
2) "map.eastworld"
3) "mmr.rating"
4) "mode.ctf"
5) "char.paladin"

好像没用到?br />

玩家数据?/h2>
?`timestamp.enter`, cd?zset.
每个玩家数据键都建立了一个排序集Q按该玩家数据值排序,成员?playerID?br />
例如Q?br />
127.0.0.1:6379> zrange mmr.rating 1 3 WITHSCORES
1) "ef3736ef2f7941f1a159f279703d5f58"
2) "746"
3) "17ca3bf3a2134c2c90cbe48ebc29f9cb"
4) "891"
5) "b6450b311f3f413595e824897015c462"
6) "891"


金庆 2018-09-28 14:01 发表评论
]]>手机q行 Unity Grpchttp://www.shnenglu.com/jinq0123/archive/2018/08/12/215842.html金庆金庆Sun, 12 Aug 2018 04:24:00 GMThttp://www.shnenglu.com/jinq0123/archive/2018/08/12/215842.htmlhttp://www.shnenglu.com/jinq0123/comments/215842.htmlhttp://www.shnenglu.com/jinq0123/archive/2018/08/12/215842.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/215842.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/215842.html手机q行 Unity Grpc

(金庆的专?2018.8)

* 安装 Unit2018, 支持 .NET 4.x
* 创徏一个项目,开?.NET 4.x
    Edit->Project Settings->Player->Other Settings
      ->Configuration->Script Runtime Version->.Net 4.x Equivalent
* ?https://packages.grpc.io/ ?Daily Builds 下蝲最新的
    grpc-protoc_windows_x86-VERSION.zip
    grpc_unity_package.VERSION.zip
* grpc_unity_package.VERSION.zip
    解压?Assets 目录?br />* ?github grpc 复制 examples/protos/helloworld.proto ?Assets/protos/helloworld.proto
* 创徏 Assets/Scripts/Greeter/, q在该目录下q行
    protoc.exe -I../../../protos --csharp_out=. ../../../protos/helloworld.proto --grpc_out=. --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe
    + 需要先?grpc-protoc_windows_x86-VERSION.zip 解压 protoc.exeQgrpc_csharp_plugin.exe
    + 生成 Helloworld.cs HelloworldGrpc.cs
* 客户端代?br />    + 创徏Channelq接服务?br />    channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
    + 创徏客户端ƈ发出h
    var client = new Greeter.GreeterClient(channel);
    HelloReply reply = client.SayHello(new HelloRequest { Name = "Jin Qing" });
* 完整代码见:https://gitee.com/jinq0123/unity-grpc-sample
* 最后打包安装到手机试通过


金庆 2018-08-12 12:24 发表评论
]]>
kubernetes导出有状态服?/title><link>http://www.shnenglu.com/jinq0123/archive/2018/07/14/215783.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sat, 14 Jul 2018 03:43:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2018/07/14/215783.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/215783.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2018/07/14/215783.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/215783.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/215783.html</trackback:ping><description><![CDATA[<div>kubernetes导出有状态服?br /><br />(金庆的专?2018.7)<br /><br />|游服务器中的房间服务器是有状态服务器Q可以用 kubernetes statefulset 开启多个实例?br /><br />Z让客L能够直连戉K服务器,除了 statefulset 要求?headless 服务Q?br />q须为每个实例创?NodePort cd的服? q且选择Pod和禁止{发?br /><br />下面 bootcamp.yml 先创Z bootcamp headless 服务(clusterIP: None), <br />又创Z bootcamp StatefulSet, 实例个数?2.<br />然后创徏 bootcamp-0,1,2 服务Q分别对?bootcamp-0,1,2 pod.<br /><br />服务个数大于实例个数Q是x试下服务没有对应的实例时的表现?br /><br />|游中的匚w服务器将分配一个房间给客户端,列D bootcamp-0,1,2 pod 所在节点的外网 IP,<br />q同对应服务的端口,发送给客户端,让客L直连?br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ cat bootcamp.yml </span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">  name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">  namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">  labels:</span><br /><span style="color: #0000ff; font-family: Courier;">    name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">  ports:</span><br /><span style="color: #0000ff; font-family: Courier;">    - port: 8080</span><br /><span style="color: #0000ff; font-family: Courier;">  clusterIP: None  # StatefulSet要求Headless服务</span><br /><span style="color: #0000ff; font-family: Courier;">  selector:</span><br /><span style="color: #0000ff; font-family: Courier;">    app: bootcamp  # 选择 bootcamp 应用</span><br /><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: apps/v1beta1</span><br /><span style="color: #0000ff; font-family: Courier;">kind: StatefulSet</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">  name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">  namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">  serviceName: bootcamp  # 上面?Headless 服务?/span><br /><span style="color: #0000ff; font-family: Courier;">  replicas: 2</span><br /><span style="color: #0000ff; font-family: Courier;">  template:</span><br /><span style="color: #0000ff; font-family: Courier;">    metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">      labels:</span><br /><span style="color: #0000ff; font-family: Courier;">        app: bootcamp  # 应用名,与服务中?selector 对应</span><br /><span style="color: #0000ff; font-family: Courier;">    spec:</span><br /><span style="color: #0000ff; font-family: Courier;">      containers:</span><br /><span style="color: #0000ff; font-family: Courier;">      - name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">        image: docker.io/jocatalin/kubernetes-bootcamp:v1</span><br /><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">  name: bootcamp-0</span><br /><span style="color: #0000ff; font-family: Courier;">  namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">  type: NodePort  # 对外服务</span><br /><span style="color: #0000ff; font-family: Courier;">  externalTrafficPolicy: Local  # 不要转发到其他节?/span><br /><span style="color: #0000ff; font-family: Courier;">  selector:</span><br /><span style="color: #0000ff; font-family: Courier;">    app: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">    statefulset.kubernetes.io/pod-name: bootcamp-0  # 选择 pod</span><br /><span style="color: #0000ff; font-family: Courier;">  ports:</span><br /><span style="color: #0000ff; font-family: Courier;">  - protocol: TCP</span><br /><span style="color: #0000ff; font-family: Courier;">    nodePort: 30880  # 对外端口</span><br /><span style="color: #0000ff; font-family: Courier;">    port: 8080</span><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">  name: bootcamp-1</span><br /><span style="color: #0000ff; font-family: Courier;">  namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">  type: NodePort</span><br /><span style="color: #0000ff; font-family: Courier;">  externalTrafficPolicy: Local</span><br /><span style="color: #0000ff; font-family: Courier;">  selector:</span><br /><span style="color: #0000ff; font-family: Courier;">    app: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">    statefulset.kubernetes.io/pod-name: bootcamp-1</span><br /><span style="color: #0000ff; font-family: Courier;">  ports:</span><br /><span style="color: #0000ff; font-family: Courier;">  - protocol: TCP</span><br /><span style="color: #0000ff; font-family: Courier;">    nodePort: 30881</span><br /><span style="color: #0000ff; font-family: Courier;">    port: 8080</span><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">  name: bootcamp-2</span><br /><span style="color: #0000ff; font-family: Courier;">  namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">  type: NodePort</span><br /><span style="color: #0000ff; font-family: Courier;">  externalTrafficPolicy: Local</span><br /><span style="color: #0000ff; font-family: Courier;">  selector:</span><br /><span style="color: #0000ff; font-family: Courier;">    app: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">    statefulset.kubernetes.io/pod-name: bootcamp-2</span><br /><span style="color: #0000ff; font-family: Courier;">  ports:</span><br /><span style="color: #0000ff; font-family: Courier;">  - protocol: TCP</span><br /><span style="color: #0000ff; font-family: Courier;">    nodePort: 30882</span><br /><span style="color: #0000ff; font-family: Courier;">    port: 8080</span><br /><br />因ؓ statefulset 的每个实例有不同的标{,所以可以ؓ服务选择一个实例?br /><br />利用 externalTrafficPolicy: Local 讄来禁止{发?br />参?service.spec.externalTrafficPolicy 的说明:<br /><br />https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-type-nodeport<br /><br />Setting service.spec.externalTrafficPolicy to the value Local will only proxy requests to local endpoints, never forwarding traffic to other nodes and thereby preserving the original source IP address. If there are no local endpoints, packets sent to the node are dropped, ...<br /><br />因ؓ有可能多个Pod开在同一节点上,所以对外端口设成了不同?30880-30882?br />如果限制每个节点只开一个实例,则对外端口可以设成同一个?br /><br />创徏服务Q?br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl apply -f bootcamp.yml </span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp" created</span><br /><span style="color: #0000ff; font-family: Courier;">statefulset.apps "bootcamp" created</span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp-0" created</span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp-1" created</span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp-2" created</span><br /><br />服务如下Q?br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get service -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp     ClusterIP   None             <none>        8080/TCP         3m</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-0   NodePort    10.96.128.137    <none>        8080:30880/TCP   3m</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-1   NodePort    10.109.2.56      <none>        8080:30881/TCP   3m</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-2   NodePort    10.102.181.193   <none>        8080:30882/TCP   3m</span><br /><br />2个实例:<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get pod -n jq -o wide</span><br /><span style="color: #0000ff; font-family: Courier;">NAME         READY     STATUS    RESTARTS   AGE       IP            NODE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-0   1/1       Running   0          4m        10.244.0.42   host-10-240-79-10</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-1   1/1       Running   0          4m        10.244.1.63   host-10-240-79-11</span><br /><br />讉K服务必须指定节点Q不会自动{发:<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.10:30880</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-0 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.10:30881</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.10:30881; Connection timed out</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30880</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.11:30880; Connection timed out</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><br />没有负蝲均衡Q?0881端口L讉K bootcamp-1Q?br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><br />也可以从外网讉K.<br /><br />30882 端口无法q接Q?br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30882</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.11:30882; Connection refused</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.10:30882</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.10:30882; Connection refused</span><br /><br />3个端口都有监听:<br /><br /><span style="color: #0000ff; font-family: Courier;">[root@host-10-240-79-11 tmp]# netstat -ntl | grep 3088</span><br /><span style="color: #0000ff; font-family: Courier;">tcp6       0      0 :::30881                :::*                    LISTEN     </span><br /><span style="color: #0000ff; font-family: Courier;">tcp6       0      0 :::30882                :::*                    LISTEN     </span><br /><span style="color: #0000ff; font-family: Courier;">tcp6       0      0 :::30880                :::*                    LISTEN     </span><br /><br />iptables-save 输出如下, 其中 10.244是Pod的网Dc?br /><br />没有实例q行的节点上Q会丢弃hQ?br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-XLB-LJXDQ4W47M42IZBH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-XLB-U5NEOQT6R5WSBVOH</span><br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-LJXDQ4W47M42IZBH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -m comment --comment "jq/bootcamp-1: has no local endpoints" -j KUBE-MARK-DROP</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-U5NEOQT6R5WSBVOH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -m comment --comment "jq/bootcamp-0: has no local endpoints" -j KUBE-MARK-DROP</span><br /><br />有实例运行的节点上会转发l?Pod 8080Q?br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-XLB-U5NEOQT6R5WSBVOH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-XLB-LJXDQ4W47M42IZBH</span><br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-LJXDQ4W47M42IZBH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -m comment --comment "Balancing rule 0 for jq/bootcamp-1:" -j KUBE-SEP-LJQA4WUIKJUQ5ALU</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-U5NEOQT6R5WSBVOH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -m comment --comment "jq/bootcamp-0: has no local endpoints" -j KUBE-MARK-DROP</span><br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-SEP-LJQA4WUIKJUQ5ALU -s 10.244.1.63/32 -m comment --comment "jq/bootcamp-1:" -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-SEP-LJQA4WUIKJUQ5ALU -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp -j DNAT --to-destination 10.244.1.63:8080</span><br /><br />30882 端口无法q接<br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-EXTERNAL-SERVICES -p tcp -m comment --comment "jq/bootcamp-2: has no endpoints" -m addrtype --dst-type LOCAL -m tcp --dport 30882 -j REJECT --reject-with icmp-port-unreachable</span><br /><br />试下扩容:<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get statefulset -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">NAME       DESIRED   CURRENT   AGE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp   2         2         45m</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl scale --replicas=3 statefulset/bootcamp -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">statefulset.apps "bootcamp" scaled</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get statefulset -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">NAME       DESIRED   CURRENT   AGE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp   3         3         47m</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get pod -n jq -o wide</span><br /><span style="color: #0000ff; font-family: Courier;">NAME         READY     STATUS    RESTARTS   AGE       IP            NODE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-0   1/1       Running   0          48m       10.244.0.42   host-10-240-79-10</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-1   1/1       Running   0          48m       10.244.1.63   host-10-240-79-11</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-2   1/1       Running   0          45s       10.244.2.60   host-10-240-79-12</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.12:30882</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-2 | v=1</span><br /></div><img src ="http://www.shnenglu.com/jinq0123/aggbug/215783.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2018-07-14 11:43 <a href="http://www.shnenglu.com/jinq0123/archive/2018/07/14/215783.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>|游服务?Services-based ?Cells-based 架构http://www.shnenglu.com/jinq0123/archive/2018/03/10/215551.html金庆金庆Sat, 10 Mar 2018 10:03:00 GMThttp://www.shnenglu.com/jinq0123/archive/2018/03/10/215551.htmlhttp://www.shnenglu.com/jinq0123/comments/215551.htmlhttp://www.shnenglu.com/jinq0123/archive/2018/03/10/215551.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/215551.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/215551.html|游服务?Services-based ?Cells-based 架构

(金庆的专?2018.3)

无缝世界|游服务器架构的设计思\
http://blog.csdn.net/SmartTony/article/details/6842065
提出?U网游服务器架构Q?br />
* Services-based architecture Z服务的架?br />* Cells-based architecture Zcell的架?br />
Z服务的架构按功能划分成不同服务,不同服务实现于不同的q程?br />而基于cell的架构用相同的Cellq程l成一个集,每个Cellq程包含全部的功能?br />
Z服务的架构一直是L的架构,自从 microservice ?service mesh 概念相􋹁行Q?br />Z微服务的架构应该会成为未来的方向?br />
在无~大地图|游中,应该用基于cell的架构来实现地图及相兛_能,
其他与位|无关的功能则应该尽量按服务实现?br />考虑到服务来回调用g时较大,对于实时性强的功能,如PK也应该在cell上实现?br />
可以考虑客户端通过专门的网xq接cell, 使用udp/kcp来减时延?br />而其他非实时性服务由Tcp|关转发?br />服务器内部可全部l一采用 gRPC.



金庆 2018-03-10 18:03 发表评论
]]>
|游同步中的旉?/title><link>http://www.shnenglu.com/jinq0123/archive/2018/01/18/215477.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Thu, 18 Jan 2018 13:28:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2018/01/18/215477.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/215477.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2018/01/18/215477.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/215477.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/215477.html</trackback:ping><description><![CDATA[<div>|游同步中的旉?br /><br />(金庆的专?2018.1)<br /><br />旉知觉Qtemporal perceptionQ?对客观现象的延箋性和序性的反应。实际上是对事g和运动的知觉?br />-- http://dict.youdao.com/w/temporal_perception<br /><br />|游旉以服务器的ؓ准?br />|游客户端落后服务器例如1帧时_所以表现的是过ȝ场景?br />因ؓ玩家自n的行为是已知的,所以本地可以预自w角色的状态,前于其他角艌Ӏ?br />处于前旉的主角与处于q去旉的其他角色共处?br /><br />This raises the problem of interaction between objects displayed in present time space (the player's avatar) and objects displayed in a past time space (remote characters, AI entities). One solution is to make the LCT vary according to the distance from the player's avatar. This idea is called temporal perception, or presentation time or sometimes local perception filters and comes from the analogy with the appearance of the stars in the sky: the farther the distance, the longer the time the light takes to come to us [Singhal-Zyda].<br /><br />-- http://www.xlgps.com/article/99968.html 带宽限制下的视觉实体属性传?br /><br />主角可以与过L间的其他角色交互Q距越q,可以允许的滞后时间就长?br />q个概念UC旉感知qo?temporal perception filter)Q?br />或者本地感知过滤器(local perception filter)?br /><br />可以用星星作cLQ我们看到的一光年q的星星其实是一q前的星星?br /><br />Local Perception Filter Demo<br />http://mikolalysenko.github.io/local-perception-filter-demo/<br /></div><img src ="http://www.shnenglu.com/jinq0123/aggbug/215477.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2018-01-18 21:28 <a href="http://www.shnenglu.com/jinq0123/archive/2018/01/18/215477.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lua和C++之间调用效率试http://www.shnenglu.com/jinq0123/archive/2017/08/30/215209.html金庆金庆Wed, 30 Aug 2017 09:25:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/08/30/215209.htmlhttp://www.shnenglu.com/jinq0123/comments/215209.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/08/30/215209.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/215209.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/215209.htmlLua和C++之间调用效率试

(金庆的专?2017.8)

仿照 http://www.cnblogs.com/archy_yu/p/3185608.html ?Lua ?C++ 调用q行试?br />
代码见:https://github.com/jinq0123/TimerLuaIntf

使用 LuaIntf l定 Lua ?C++。用 boost timer 计时?br />依赖?lua-cpp, lua-intf, boost-timer ?conan 安装?br />conan 会下载源码,~译Q然后生?conanbuildinfo.props l?VS 导入Q?br />其中讑֥了所?include, lib 目录Q链接库Q运行库?br />
代码大概如下Q?br />
    cout << "C++ calls lua add() many times:\n";
    {
        boost::timer::auto_cpu_timer t;
        for (int i = 0; i < COUNT; ++i)
            test.dispatchStatic("add", 123, 456);
    }
    cout << "C++ calls lua add_times() once:\n";
    {
        boost::timer::auto_cpu_timer t;
        test.dispatchStatic("add_times", 123, 456, COUNT);
    }
    cout << "Lua calls C++ add() many times:\n";
    {
        boost::timer::auto_cpu_timer t;
        test.dispatchStatic("test_c_add", 123, 456, COUNT);
    }
    cout << "Lua calls C++ add_times() once:\n";
    {
        boost::timer::auto_cpu_timer t;
        test.dispatchStatic("test_c_add_times", 123, 456, COUNT);
    }

试4U调用:
* C++ 调用 1kw ?lua add()
* C++ 调用 1 ?lua add_times(), 其中调用 add() 1kw ?br />* Lua 调用 C++ add() 1kw ?br />* Lua 调用 C++ add_times() 1 ơ,其中调用 add() 1kw ?br />
输出如:
C++ calls lua add() many times:
 2.759473s wall, 2.761218s user + 0.000000s system = 2.761218s CPU (100.1%)
C++ calls lua add_times() once:
 0.436400s wall, 0.436803s user + 0.000000s system = 0.436803s CPU (100.1%)
Lua calls C++ add() many times:
 0.535802s wall, 0.530403s user + 0.000000s system = 0.530403s CPU (99.0%)
Lua calls C++ add_times() once:
 0.000005s wall, 0.000000s user + 0.000000s system = 0.000000s CPU (n/a%)
 
l论是:
* C++ 调用 Lua 可达 3百万?s
* Lua 内部调用函数可达 2千万ơ强/s
* Lua 调用 C++ 函数可达 2千万ơ弱/s


金庆 2017-08-30 17:25 发表评论
]]>
protobuf定义低带宽的Ud消息http://www.shnenglu.com/jinq0123/archive/2017/08/23/215189.html金庆金庆Wed, 23 Aug 2017 03:50:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/08/23/215189.htmlhttp://www.shnenglu.com/jinq0123/comments/215189.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/08/23/215189.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/215189.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/215189.htmlprotobuf定义低带宽的Ud消息

(金庆的专?2017.8)

用protobuf定义协议Ӟ可利用protobuf的编码方式将Ud消息压羃到尽量小?br />
service Battle {
    // Ud变化量。将q播l其他玩Ӟ包括自己?/span>
    // Ud受步镉K制。自动{向移动方向?/span>
    rpc Move(Movement) returns (rpc.EmptyMsg);
    // 转向最l面向。将q播l其他玩Ӟ包括自己?/span>
    rpc TurnTo(Rotation) returns (rpc.EmptyMsg);
}

// 位置Q移动和角度都用整数向量表示Q单位ؓ分米.
// 使用整数占用的带宽比点数少?/span>
// U3d使用以米为单位的点敎ͼ需要{化?/span>
message Position {
    optional sint32 x = 1;
    optional sint32 y = 2;
}

// Ud时发送移动量。不是发送绝对位|,使用更小的数字,占用更少带宽?/span>
message Movement {
    optional sint32 x = 1;
    optional sint32 y = 2;
}

// 角度向量在满精度的前提下,应该量使用l对值小的数据表C,占用更少带宽.
message Rotation {
    optional sint32 x = 1;
    optional sint32 y = 2;
}

// 位置和面向。用于初始化对象?/span>
message PosAndRot {
    optional Position position = 1;
    optional Rotation rotation = 2;
}

说明

* 名字短点。因为RPC消息中需带服务全名和Ҏ名,所以package, service, method名字要短?br />* 坐标用整数表C。QҎ会固定占?个字节,而接q?值的整数值只占用不到一个字节?br />* 使用Ud变化量。地囑֏能很大,l对坐标D大会占用变多字节Q而移动量L接近0
* 角度使用向量更容易处理。也可用整数的角度?0-360)表示Q但处理ȝ?br />* Ud隐含了{向。所以{向消息不多,不必太在意大?br />* sint~码压羃最大。要求地囑֝标应该都?附近Q正负各半?br />* 单位选择不要太细Q分c_厘米都可以?/div>

金庆 2017-08-23 11:50 发表评论
]]>
Lua区分公有U有接口http://www.shnenglu.com/jinq0123/archive/2017/08/18/215169.html金庆金庆Fri, 18 Aug 2017 10:51:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/08/18/215169.htmlhttp://www.shnenglu.com/jinq0123/comments/215169.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/08/18/215169.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/215169.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/215169.htmlLua区分公有U有接口

(金庆的专?2017.8)

Lua语言没有提供public, private的概念,所有模块或cM的接口都是公有的?br />
可以通过注释来区分公有私有接口?br />
ldoc 提供了一?@section 标签Q可用作此功?br />
--- Test module.
-- @module my_mod
local M = {}

--- Public functions
-- @section public

--- foo.
-- @int a a test input
-- @treturn int result
function M.foo(a)
    return a + 1
end

--- Private functions
-- @section private

--- goo.
-- @int a a test input
-- @treturn int result
function M.goo(a)
    return a + 2
end

return M

ldoc 生成文档后是q样的:

金庆 2017-08-18 18:51 发表评论
]]>
Unity2017可以使用grpchttp://www.shnenglu.com/jinq0123/archive/2017/08/11/215153.html金庆金庆Fri, 11 Aug 2017 03:30:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/08/11/215153.htmlhttp://www.shnenglu.com/jinq0123/comments/215153.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/08/11/215153.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/215153.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/215153.htmlUnity2017可以使用grpc

(金庆的专?2017.8)

gRPC ?Google 发布的多语言 RPC 库,?https://github.com/grpc/grpc?br />
Unity 2017.1 .Net4.6 支持 async, await, 原来 grpc ?Unity 上的限制已经消除?br />
http://www.sohu.com/a/156779221_280780?qq-pf-to=pcqq.group

CZ可以q行Qhttps://github.com/ahakits/unity-grpc-sample


CZ自带Server, 也可以运?grpc ?hello world server CZ?br />输入 127.0.0.1, ?Ch" 创徏 Channel.
然后输入一个名字,?"Send" 卛_收到应答q显C?br />

金庆 2017-08-11 11:30 发表评论
]]>
|游开发目录结?/title><link>http://www.shnenglu.com/jinq0123/archive/2017/04/27/214889.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Thu, 27 Apr 2017 02:01:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2017/04/27/214889.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/214889.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2017/04/27/214889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/214889.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/214889.html</trackback:ping><description><![CDATA[<div>|游开发目录结?br /><br />(金庆的专?2017.4)<br /><br />合理讄|游开发目录结构,可以方便目录权限讄?br /><br />以前分ؓ3大块Q分别对应美术,E序Q策划的目录?br /><br />Art<br />Code<br />Design<br /><br />也有可能是分?个SVN库?br />个h們֐于分成多个库QSVN应该只保存文本文件?br /><br />对于U3d开发,术和策划需要更改客L开发目录?br /><br />Art<br />Design<br />Client<br />Server<br />Common<br /></div><img src ="http://www.shnenglu.com/jinq0123/aggbug/214889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2017-04-27 10:01 <a href="http://www.shnenglu.com/jinq0123/archive/2017/04/27/214889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>proto文g按包名分子目?/title><link>http://www.shnenglu.com/jinq0123/archive/2017/04/17/214862.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 17 Apr 2017 06:40:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2017/04/17/214862.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/214862.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2017/04/17/214862.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/214862.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/214862.html</trackback:ping><description><![CDATA[<div>proto文g按包名分子目?br /><br />(金庆的专?2017.4)<br /><br />服务器客L之间的protobuf协议定义在客L与服务器公共目录下,包名为rpc.<br />服务器内部协议定义在服务器目录下Q包名ؓsvr.<br />rpc.EmptyMsg ?svr.EmptyMsg 分别定义在各自的根目录,文g名都?empty_msg.proto.<br />q行时就会报错:<br /><br /><span style="color: #0000ff;">[libprotobuf ERROR E:\deps\protobuf-3.2.0\protobuf-3.2.0\src\google\protobuf\</span><br /><span style="color: #0000ff;">descriptor_database.cc:57] File already exists in database : empty_msg.proto</span><br /><span style="color: #0000ff;">[libprotobuf FATAL E:\deps\protobuf-3.2.0\protobuf-3.2.0\src\google\protobuf\</span><br /><span style="color: #0000ff;">descriptor.cc:1275] CHECK failed: generated_database_->Add(encoded_file_descriptor, size):</span><br /><br />原因囄同一个文件名"empty_msg.proto"往descriptor_databaseddescriptor?br /><br />如果按包名分子目录,文g名就可以分开?"rpc/empty_msg.proto" ?"svr/empty_msg.proto".<br /><br /></div><img src ="http://www.shnenglu.com/jinq0123/aggbug/214862.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2017-04-17 14:40 <a href="http://www.shnenglu.com/jinq0123/archive/2017/04/17/214862.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>behaviac动态库q行出错http://www.shnenglu.com/jinq0123/archive/2017/03/16/214755.html金庆金庆Thu, 16 Mar 2017 03:40:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/03/16/214755.htmlhttp://www.shnenglu.com/jinq0123/comments/214755.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/03/16/214755.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/214755.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/214755.htmlbehaviac动态库q行出错

(金庆的专?2017.3.16)

游戏是静态链接的q行库,dbehaviac动态库后,q行出错Q?br />
>    ucrtbased.dll!free_dbg_nolock(void * const block, const int block_use) ?996    C++
     ucrtbased.dll!_free_dbg(void * block, int block_use) ?1030    C++
     libbehaviac_msvc_debug.dll!operator delete(void * block) ?17    C++
     libbehaviac_msvc_debug.dll!std::_Deallocate(void * _Ptr, unsigned int _Count, unsigned int _Sz) ?132    C++
     libbehaviac_msvc_debug.dll!std::allocator<std::_Container_proxy>::deallocate(std::_Container_proxy * _Ptr, unsigned int _Count) ?720    C++
     libbehaviac_msvc_debug.dll!std::_Wrap_alloc<std::allocator<std::_Container_proxy> >::deallocate(std::_Container_proxy * _Ptr, unsigned int _Count) ?988    C++
     libbehaviac_msvc_debug.dll!std::_String_alloc<std::_String_base_types<char,std::allocator<char> > >::_Free_proxy() ?661    C++
     libbehaviac_msvc_debug.dll!std::_String_alloc<std::_String_base_types<char,std::allocator<char> > >::~_String_alloc<std::_String_base_types<char,std::allocator<char> > >() ?629    C++
     libbehaviac_msvc_debug.dll!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >() ?1018    C++
     libbehaviac_msvc_debug.dll!behaviac::CTagObject::Save(behaviac::IIONode * node, const char * szClassName) ?100    C++
     libbehaviac_msvc_debug.dll!behaviac::Agent::InitVariableRegistry() ?476    C++
     libbehaviac_msvc_debug.dll!behaviac::Agent::Init_(int contextId, behaviac::Agent * pAgent, short priority, const char * agentInstanceName) ?227    C++
     giant_test_client.exe!behaviac::Agent::InitAgent(behaviac::Agent * pAgent, const char * agentInstanceName, const char * agentInstanceNameAny, bool bToBind, int contextId, short priority) ?56    C++
     giant_test_client.exe!behaviac::Agent::Create<PlaneAgent>(const char * agentInstanceName, int contextId, short priority) ?88    C++
     giant_test_client.exe!Client::InitPlayer() ?36    C++
     giant_test_client.exe!Client::Client() ?23    C++
     giant_test_client.exe!main() ?50    C++
     giant_test_client.exe!invoke_main() ?64    C++
     giant_test_client.exe!__scrt_common_main_seh() ?253    C++
     giant_test_client.exe!__scrt_common_main() ?296    C++
     giant_test_client.exe!mainCRTStartup() ?17    C++
     kernel32.dll!@BaseThreadInitThunk@12()    未知
     ntdll.dll!___RtlUserThreadStart@8()    未知
     ntdll.dll!__RtlUserThreadStart@8()    未知

看来是string跨越动态库析构?br />如果string在动态库中构造又析构Q动态库的运行时库可以与ȝ序不同?br />但如果用ȝ序的q行时申请内存,在动态库中用另一个运行时库释放,׃产生上面的错误?br />
可以建立一个最化的程序来复现上面的错误?br />新徏一个行为树QؓAgent创徏一个属性?br />然后ȝ序运行库设ؓQ多U程调试 (/MTd)Q而不是:多线E调?DLL (/MDd)?br />调试q行创徏Agent时就会出上面错误?

疑问是这是动态库内部调用Q怎么会生跨库析构呢Q?br />
断点q入string的构造过E可以发玎ͼstring不是动态库构造的Q而是ȝ序构造的Q?br />
>    test_client.exe!behaviac::StringUtils::internal::ToString(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & val) ?138    C++
     test_client.exe!behaviac::StringUtils::Detail::ToStringPtrHanler<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,0>::ToString(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & v) ?192    C++
     test_client.exe!behaviac::StringUtils::Detail::ToStringEnumHanler<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,0>::ToString(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & v) ?228    C++
     test_client.exe!behaviac::StringUtils::Detail::ToStringStructHanler<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,0>::ToString(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & val) ?242    C++
     test_client.exe!behaviac::StringUtils::ToString<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & val) ?256    C++
     test_client.exe!behaviac::SetFromString_t<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,0>::Get(std::basic_string<char,std::char_traits<char>,std::allocator<char> > & value) ?790    C++
     test_client.exe!behaviac::CProperty<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >::GetValueToString(const behaviac::Agent * self) ?848    C++
     libbehaviac_msvc_debug.dll!behaviac::CTagObject::Save(behaviac::IIONode * node, const char * szClassName) ?97    C++
     libbehaviac_msvc_debug.dll!behaviac::Agent::InitVariableRegistry() ?476    C++
     libbehaviac_msvc_debug.dll!behaviac::Agent::Init_(int contextId, behaviac::Agent * pAgent, short priority, const char * agentInstanceName) ?227    C++
     test_client.exe!behaviac::Agent::InitAgent(behaviac::Agent * pAgent, const char * agentInstanceName, const char * agentInstanceNameAny, bool bToBind, int contextId, short priority) ?56    C++
     test_client.exe!behaviac::Agent::Create<PlaneAgent>(const char * agentInstanceName, int contextId, short priority) ?88    C++
     test_client.exe!Client::InitPlayer() ?36    C++
     test_client.exe!Client::Client() ?23    C++
     test_client.exe!main() ?50    C++
     test_client.exe!invoke_main() ?64    C++
     test_client.exe!__scrt_common_main_seh() ?253    C++
     test_client.exe!__scrt_common_main() ?296    C++
     test_client.exe!mainCRTStartup() ?17    C++
     kernel32.dll!75c3336a()    未知
     [下面的框架可能不正确?或缺失,没有?kernel32.dll 加蝲W号]    
     ntdll.dll!76fa9902()    未知
     ntdll.dll!76fa98d5()    未知

原因?IProperty::GetValueToString()是虚函数Q实际实现是׃E序实现模板cR?br />    behaviac::CProperty<>::GetValueToString()

解决ҎZ用behaviac静态库q且静态链接运行时库?br />

金庆 2017-03-16 11:40 发表评论
]]>
跨服Lua调用http://www.shnenglu.com/jinq0123/archive/2017/03/02/214722.html金庆金庆Thu, 02 Mar 2017 09:12:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/03/02/214722.htmlhttp://www.shnenglu.com/jinq0123/comments/214722.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/03/02/214722.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/214722.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/214722.html跨服Lua调用

(金庆的专?2017.3)

跨服Lua调用是指服务器集内部A服调用B服上的脚本?br />
服务器之间已l实现RPC调用QLua调用是Rpc调用的简化方式?br />
CZQ?br />    -- Tell remote server svr_id that game_clt_id is disconnected.
    local arguments = { "Remote.ClientDisconnected", game_clt_id }
    remote_runner.run_mfa(svr_id, "event.dispatcher",
        "dispatch", arguments)

以上例子相当于调用了其他服务器上的代码:
    require("event.dispatcher").dispatch("Remote.ClientDisconnected", game_clt_id)

需要参CؓQ?br />* q程服务器ID
* Lua模块?br />* Lua模块中的函数?br />* 调用参数列表

remote_runnerq样实现Q?br />
-- q行另一服务器上的Lua代码
-- 也支持本服运?本服RPC调用)

local M = {}

local log = require("log"):new("remote_runner")
local pb = require("protobuf")
local serpent = require("serpent")

-- on_result(result) 生成 rpc 回调函数 cb(resp_str)
local function get_mfa_cb(on_result)
    if (not on_result) then return nil end
    assert("function" == type(on_result))

    local cb = function(resp_str)
        assert("string" == type(resp_str))
        local resp = pb.decode("svr.RunLuaMfaResponse", resp_str)
        local ok, copy = serpent.load(resp.returned_dump)
        assert(ok, "Run mfa returns invalid value.")
        log:debug("RunLuaMfaResponse: %s", serpent.line(copy))
        on_result(table.unpack(copy)) -- 回调时执?/span>
    end  -- cb

    return cb
end  -- get_mfa_cb()

-- Run module function with arguments on remote server.
-- CZ rum_mfa(123, "event.dispatcher", "dispatch", {"EventName", 1,2,3}, nil)
function M.run_mfa(svr_id, module_name, function_name, arguments, on_result)
    assert("number" == type(svr_id))
    assert("string" == type(module_name))
    assert("string" == type(function_name))
    assert("table" == type(arguments))
    assert(nil == on_result or "function" == type(on_result))
    log:debug("Request to call Svr_%s %s.%s()", svr_id, module_name, function_name)
    local req = {
        module_name = module_name,
        function_name = function_name,
        arguments_dump = serpent.dump(arguments)
    }
    local req_str = pb.encode("svr.RunLuaMfaRequest", req)
    local cb = get_mfa_cb(on_result)
    c_rpc.request_svr(svr_id, "svr.RunLua", "RunMfa", req_str, cb)
end  -- run()

return M

通过Rpc服务RunLua.RunMfa实现。run_lua.proto如下定义

syntax = "proto3";
package svr;

// 服务器内部跨服调用Lua
service RunLua {
    // q行 module.function(...arguments...)
    rpc RunMfa(RunLuaMfaRequest) returns (RunLuaMfaResponse);
}

message RunLuaMfaRequest {
    string module_name = 1;
    string function_name = 2;
    // arguments_dump = serpent.dump({1,2,3})
    string arguments_dump = 3;
}

message RunLuaMfaResponse {
    // Get returned table copy:
    // local ok, copy = serpent.load(returned_dump)
    string returned_dump = 1;
}

服务q样实现Q?br />
-- svc_run_lua.lua
-- Run lua by other servers.

local M = {}

local log = require("log"):new("svc_run_lua")
local pb = require("protobuf")

-- Run module.function(...arguments...)
function M.RunMfa(ctx, content)
    local req = pb.decode("svr.RunLuaMfaRequest", content)
    log:debug("RunMfa %s.%s", req.module_name, req.function_name)  -- todo: from where?
    local mod = require(req.module_name)
    local fun = mod[req.function_name]
    local ok, arguments = serpent.load(serpent.dump(req.arguments_dump))
    assert(ok, "Illegal arguments.")
    local result_table = table.pack(fun(table.unpack(arguments)))
    local resp = { returned_dump = serpent.dump(result_table) }
    local resp_str = pb.encode("svr.RunLuaMfaResponse", resp)
    c_rpc.reply_to(ctx, resp_str)
end  -- Run()

return M


金庆 2017-03-02 17:12 发表评论
]]>
|游服务器csv配置设计http://www.shnenglu.com/jinq0123/archive/2017/02/15/214681.html金庆金庆Wed, 15 Feb 2017 08:18:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/02/15/214681.htmlhttp://www.shnenglu.com/jinq0123/comments/214681.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/02/15/214681.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/214681.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/214681.html|游服务器csv配置设计

(金庆的专?2017.2)

巨h|络Lua手游服务器中csv配置pȝ的设?br />
配置文g?csv 格式?br />csv文g全部攄?csv/ 目录?br />csv/可以有子目录?br />csv目录下所有csv文g可以热更新?br />
{划数据也会导出到csv.
csv~码为utf8带BOM?br />如果不带BOMQexcel打开中文昄q?br />
xlsx扚w转ؓutf8的csv
http://blog.csdn.net/jq0123/article/details/49512877

要求csv解析器允许多行字D,允许双引P忽略BOM?br />csv解析采用Qhttps://github.com/jinq0123/csv_parser_RFC4180

csv按以下规范徏立:
W?行:字段说明Q可以多行,可以中文Q仅供注释用?br />       说明攄1行的好处是:多行说明不媄响下面的数据昄Q征途的csv也是说明在先?br />W?行:字段名,英文Q区分大写Q尽量不用特D字W?br />       同一表内各字D名必须不同?br />       全部写Q用下划U连接单词,?server_type", "equip_id".
W?行:cdQ有3U:string, int, float, ~省为string, 无法识别也按string处理?br />       int, float数据~省?, 解析有错时也?处理?br />
所有csv加蝲后,通过以下接口查询

* 获取表格 get_table("test/test.csv") -> csv/test/test.csv
* 取全部记?table.get_all_records()
* 按烦引取记录Q可能多个)
    + 取第1?table.get_record("field1", val1, "field2", val2)
    + 取多?table.get_records("field1", val1, "field2", val2)
* 按列名取字段
    + record.get_string("field3")
    + record.get_int("field3")
    + record.get_float("field3")
* 自动索引Q首ơ查询徏立烦?br />* 索引可以是多列组?br />


金庆 2017-02-15 16:18 发表评论
]]>
hiredis异步接口装q导出到Luahttp://www.shnenglu.com/jinq0123/archive/2017/01/05/214574.html金庆金庆Thu, 05 Jan 2017 10:42:00 GMThttp://www.shnenglu.com/jinq0123/archive/2017/01/05/214574.htmlhttp://www.shnenglu.com/jinq0123/comments/214574.htmlhttp://www.shnenglu.com/jinq0123/archive/2017/01/05/214574.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/214574.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/214574.htmlhiredis异步接口装q导出到Lua

(金庆的专?2017.1)

hiredis 不支?Windows, Windows 下?wasppdotorg / hiredis-for-windows ?br />Linux 下仍?redis/hiredis?br />hiredis-for-windows 是以 hiredis 0.13.3 为基UL的?br />
hiredis-for-windows 需要稍加修正:
    * 去除 inline ?br />    * TCP_NODELAY 改在q接之前讄?br />详见其Issue.

Cluster 支持采用 shinberg/cpp-hiredis-cluster。这是个CPP库,支持异步Q?br />要求 hiredis >= 0.12.0?br />jinq0123/cpp-hiredis-cluster ?develop 分支上更改了接口Q让它更好用?br />
因ؓ|络库是boost asio, 所以需要asio适配器,采用 jinq0123/hiredis-boostasio-adapter?br />
cpp-hiredis-cluster 提供的是l一的Command接口Q接收字W串命oQ返?redisReply.
对于常用命oQ需要更单的接口?br />在Lua手游服务器代码中新徏CRedisc,装 cpp-hiredis-clusterQ?br />为常用redis命o装更好用的接口?br />CRediscd装了asio, 接口是根据应用需要定义的Q所以是专用接口Q?br />不在 cpp-hiredis-cluster 中实现?br />
bool CRedis::Init(io_service& rIos, const std::string& sHost, uint16_t uPort)
创徏 RedisCluster 对象?br />io_service 用于创徏一?redis 事g适配器,
RedisCluster创徏需要一个适配器?br />sHost, uPort 用于初始化连接redis cluster, 获取集群信息?br />

using Cmd = RedisCluster::AsyncHiredisCommand;

bool CRedis::Init(io_service& rIos, const std::string& sHost, uint16_t uPort)
{
    m_pAdapter.reset(new Adapter(rIos));
    try
    {
        m_pCluster.reset(Cmd::createCluster("127.0.0.1", 7000, *m_pAdapter));
    }
    catch (const RedisCluster::ClusterException &e)
    {
        LOG_ERROR("Cluster exception: " << e.what());
        return false;
    }

    return true;
}

static Cmd::Action handleException(const RedisCluster::ClusterException &exception,
    RedisCluster::HiredisProcess::processState state)
{
    // Check the exception type.
    // Retry in case of non-critical exceptions.
    if (!dynamic_cast<const RedisCluster::CriticalException*>(&exception))
    {
        LOG_WARN("Exception in processing async redis callback: "
            << exception.what() << " Retry...");
        // retry to send a command to redis node
        return Cmd::RETRY;
    }
    LOG_ERROR("Critical exception in processing async redis callback: "
        << exception.what());
    return Cmd::FINISH;
}

static void handleSetReply(const redisReply &reply, const CRedis::SetCb& setCb)
{
    if (!setCb) return;
    if (reply.type == REDIS_REPLY_STATUS)
    {
        const std::string OK("OK");
        if (OK == reply.str)
        {
            setCb(true);
            return;
        }
    }

    LOG_WARN("Set reply: " << reply.str);
    setCb(false);
}

void CRedis::Set(const string& sKey, const string& sValue, const SetCb& setCb)
{
    assert(m_pCluster);
    Cmd::commandf2(*m_pCluster, sKey,
        [setCb](const redisReply& reply) {
            handleSetReply(reply, setCb);
        },
        handleException,
        "set %s %s", sKey.c_str(), sValue.c_str());
}

static void handleGetReply(const redisReply& reply,
    const CRedis::ReplyStringCb& hdlStrReply)
{
    if (!hdlStrReply) return;
    using Rt = CRedis::ReplyType;
    if (reply.type == REDIS_REPLY_NIL)
    {
        hdlStrReply(Rt::NIL, "");
        return;
    }
    std::string sReply(reply.str, reply.len);
    if (reply.type == REDIS_REPLY_STRING)
        hdlStrReply(Rt::OK, sReply);
    else
        hdlStrReply(Rt::ERR, sReply);
}

void CRedis::Get(const std::string& sKey, const ReplyStringCb& hdlStrRepl)
{
    assert(m_pCluster);
    Cmd::commandf2(*m_pCluster, sKey,
        [hdlStrRepl](const redisReply& reply) {
            handleGetReply(reply, hdlStrRepl);
        },
        handleException,
        "get %s", sKey.c_str());
}

handleException 是Cmd::command() 接口中需要的异常处理Q这里是量重试?br />
Cmd::command() 中的W?个参数是 redis 应答的回调,d redisReply, 然后触发命o的回调?br />
CRedis::Get() 执行 redis GET 命oQ固?个参敎ͼq回是字W串Qnil, 或错误?br />
    enum class ReplyType
    {
        OK = 0,  // redis replys string/integer/array
        NIL = 1,  // redis replys nil
        ERR = 2,  // redis replys error status
    };

    // 单的常用命o会自动解析reply, 使用更易用的回调?/span>
    using ReplyStringCb = function<void (ReplyType, const string& sReply)>;

CRedis::Get() 的回调就?ReplyStringCb?br />
void CRedis::Set() 的回调只需知道成功或失败,SetCb 定义为:
    using SetCb = function<void (bool ok)>;

p|时会有错误信息,已统一打印日志Q不再暴露出来?br />
SET 命o完整的参数列表还有其他参敎ͼ
set key value [EX seconds] [PX milliseconds] [NX|XX]

因ؓ常用的就 "set key value", 其他扩展的命令需要用通用?command() 接口Q?br />q要读?redisReply.

使用 LuaIntf 导出 CRedis ?Lua.
因ؓ只有一?CRedis, 所以导Zؓ模块 c_redis:

#include <LuaIntf/LuaIntf.h>

namespace {

void Set(const string& sKey, const string& sValue, const LuaRef& luaSetCb)
{
    // Default is empty callback.
    auto setCb = ToFunction<CRedis::SetCb>(luaSetCb);
    GetRedis().Set(sKey, sValue, setCb);
}

void Get(const string& sKey, const LuaRef& luaReplyStringCb)
{
    auto replyStringCb = ToFunction<CRedis::ReplyStringCb>(
        luaReplyStringCb);
    GetRedis().Get(sKey, replyStringCb);
}

}  // namespace

void Bind(lua_State* L)
{
    LuaBinding(L).beginModule("c_redis")
        .addFunction("set", &Set)
        .addFunction("get", &Get)
    .endModule();
}

需要将 lua 的回调函数{?cpp 的回调:

template <class Function>
Function ToFunction(const LuaIntf::LuaRef& luaFunction)
{
    // Default is empty.
    if (!luaFunction)
        return Function();  // skip nil
    if (luaFunction.isFunction())
        return luaFunction.toValue<Function>();  // Todo: catch
    LOG_WARN_TO("ToFunction", "Lua function expected, but got "
        << luaFunction.typeName());
    return Function();
}

Lua q样调用Q?br />c_redis.set("FOO", "1234")
c_redis.set("FOO", "1234", function(ok) print(ok) end)
c_redis.get("FOO", function(reply_type, reply)
    assert("string" == type(reply))
    if 0 == reply_type or 1 == reply_type then
        print("FOO="..reply)
    else
        print("Error: "..reply)
    end
end)

金庆 2017-01-05 18:42 发表评论
]]>
ejabberd为游戏免除注册限?/title><link>http://www.shnenglu.com/jinq0123/archive/2016/11/03/214377.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Thu, 03 Nov 2016 04:11:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2016/11/03/214377.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/214377.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2016/11/03/214377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/214377.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/214377.html</trackback:ping><description><![CDATA[<div>20161103_ejabberd为游戏免除注册限?br /><br />(金庆的专?2016.11)<br /><br />ejabberd聊天服务器默认会限制同一IP注册帐号间?00s?<br />在游戏中需要ؓ每个角色注册一个聊天帐P不应该有此限制?<br />可以更改服务器代码,为游戏服务器免除q一注册间隔旉?<br /><br />假设游戏服用专用的帐L录ejabberd, 然后U帐号免除注册限制?br /><br />在ejabberd.yml配置讉K控制列表(ACL)中添?game_masterQ?br /><br /><span style="color: #800000;">    acl:</span><br /><span style="color: #800000;">      game_master:</span><br /><span style="color: #800000;">        user:</span><br /><span style="color: #800000;">          - "game_master_1@localhost"</span><br /><span style="color: #800000;">          - "game_master_2@localhost"</span><br /><br />game_master 帐号预先创徏Q供游戏服务器登录ejabberd.<br /><br />mod_register的配|中Q将 "access_from: deny" 改ؓ "access_from: all". <br />"access_from: deny" 表示M用户都不能注册帐P只能是登录前注册?<br />"access_from: all" 表示d用户也能注册帐号?<br />q样game_master帐号先登录,然后可以注册新帐号了?<br /><br />d access_from_without_time_limitQ允许game_master无注册限制?br /><br /><span style="color: #800000;">    mod_register:</span><br /><span style="color: #800000;">        access_from: all</span><br /><span style="color: #800000;">        access: register</span><br />    <br /><span style="color: #800000;">        ## Allow some user register accounts without registration_timeout limit.</span><br /><span style="color: #800000;">        access_from_without_time_limit:</span><br /><span style="color: #800000;">          - allow: game_master</span><br /><br />实际上也可以q样直接配置Q? <br /><br /><span style="color: #800000;">    access_from_without_time_limit:</span><br /><span style="color: #800000;">      - allow:</span><br /><span style="color: #800000;">        - user: game_master_1@localhost</span><br /><span style="color: #800000;">        - user: game_master_2@localhost</span><br /><br />配置?acl 的好处是Q可以用<br /><br /><span style="color: #0000ff;">    ejabberdctl reload_config</span><br /><br />重新加蝲 acl, 而模块配|部分无法重新加载?br /><br />acl 配置q可以在 http admin 界面更改?br /><br />代码需E加更改Q修改mod_register.erl.<br /><br />try_register/5 d From 参数改ؓ try_register/6<br /><br /><span style="color: #800000;">    try_register(User, Server, Password, From, SourceRaw, Lang)</span><br /><br />q将其中<br /><br /><span style="color: #800000;">    case check_timeout(Source) of</span><br /><br />Ҏ<br /><br /><span style="color: #800000;">    CheckTimeout = case check_from_without_time_limit(From, Server) of</span><br /><span style="color: #800000;">        allow -> true;</span><br /><span style="color: #800000;">        _ -> check_timeout(Source)</span><br /><span style="color: #800000;">    end,</span><br /><span style="color: #800000;">    case CheckTimeout of</span><br /><br />卛_果From是gm帐号ӞCheckTimeout直接通过Q不再判?00s的间隔?br /><br /><span>check_from_without_time_limit/2 仿照check_from/2Q这样实玎ͼ</span><br /><br /><span style="color: #800000;">    check_from_without_time_limit(JID, Server) -></span><br /><span style="color: #800000;">        Access = gen_mod:get_module_opt(Server, ?MODULE, access_from_without_time_limit,</span><br /><span style="color: #800000;">                                        fun(A) -> A end,</span><br /><span style="color: #800000;">                                        none),</span><br /><span style="color: #800000;">        acl:match_rule(Server, Access, JID).</span><br />    <br />dQ?br /><br /><span style="color: #800000;">    mod_opt_type(access_from_without_time_limit) -></span><br /><span style="color: #800000;">        fun acl:access_rules_validator/1;</span><br /><span style="color: #800000;">    ...</span><br /><span style="color: #800000;">    mod_opt_type(_) -></span><br /><span style="color: #800000;">        [..., access_from_without_time_limit, ...].</span><br />   </div><img src ="http://www.shnenglu.com/jinq0123/aggbug/214377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">金庆</a> 2016-11-03 12:11 <a href="http://www.shnenglu.com/jinq0123/archive/2016/11/03/214377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Unity3d导出Recast geomset.txt http://www.shnenglu.com/jinq0123/archive/2016/01/12/212645.html金庆金庆Tue, 12 Jan 2016 03:28:00 GMThttp://www.shnenglu.com/jinq0123/archive/2016/01/12/212645.htmlhttp://www.shnenglu.com/jinq0123/comments/212645.htmlhttp://www.shnenglu.com/jinq0123/archive/2016/01/12/212645.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/212645.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/212645.html阅读全文

金庆 2016-01-12 11:28 发表评论
]]>
Unity3d导出场景地图寻\http://www.shnenglu.com/jinq0123/archive/2015/12/04/212413.html金庆金庆Fri, 04 Dec 2015 10:39:00 GMThttp://www.shnenglu.com/jinq0123/archive/2015/12/04/212413.htmlhttp://www.shnenglu.com/jinq0123/comments/212413.htmlhttp://www.shnenglu.com/jinq0123/archive/2015/12/04/212413.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/212413.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/212413.htmlUnity3d导出场景地图寻\

(金庆的专?

Unity3d中用无渲染的透明盒子摆出地面和阻档区域?br />        this.renderer.enabled = false;
所有这些盒子设为Navigation Static.
L盒子Navigation Layer设ؓ Not Walkable.
用EditorObjExporter.cs 导出q些盒子到obj文g?br />http://wiki.unity3d.com/index.php?title=ObjExporter



用Recast Demo 打开obj文gQ就可以试地图寻\了?br />



金庆 2015-12-04 18:39 发表评论
]]>
þþƷһ| 㽶þþþþúݺɫ| þ| ձɫվWWWþ| Ʒþþþþù| ŷƷרþ| ݺۺϾþۺ88| þþƷ˳| 99þþƷ| AVþþƷɫ| þùƷþþ| Ʒþþþþþ| þþþ99ƷƬţţӰ| 97Ʒ˾þþô߽| þֻоƷ߹ۿ| þ99Ʒ99þ| þۺϺݺɫۺ| 91Ʒ91þ| þ¶Ʒ| 㽶þҹɫƷ| ŷɫ۾þþƷ| þۺϳDž| 99REþþƷﶼǾƷ | 99Ʒþþþþþ| Ļһþվ| 鶹ŷۺϾþ | ɫ99þþþø߳ۺӰԺ| ޹˾Ʒþþùһ | AVþþƷ | þþƷAVȫ| ˾þô߽ۺĻ| þþþavëƬ| ޾Ʒһþþ| 97þ㽶߿ۿ| 99þ99þþƷƬ| þùѹۿƷ3| þþþ㽶Ƶ| ŷһþۺ| þþþһƷ| ƷþþþþӰԺ| þþþþþþòҰ߳|