Skip to content

重登客户端时,如何缓存实体onEnter_xx,set_xx等的同步事件?

ilvxna edited this page Aug 3, 2018 · 1 revision

:重登客户端时,如何缓存实体onEnter_xx,set_xx等的同步事.... 场景是,客户端意外崩溃,或玩家游戏中途退出游戏。服务器实体仍然保留,等待玩家重新登陆。

我目前的做法是,玩家登陆的过程中,在客户端实体Player.__init__中检测玩家属性state,是否还存在游戏中,如果在游戏中,则直接把玩家丢到游戏场景中

如果玩家还在游戏中,我在Account.__init__中检测玩家属性state的时候,在客户端日志中可以看到服务器已经同时把onEnter_xx,set_xx等事件已经下发了,而此时我的游戏过程还处于验证登陆的UI阶段,游戏场景还未生成完全,无法对这个时段下发的事件进行处理。造成了数据恢复的遗漏。如果是机子性能比较低,那肯定会造成更多数据恢复遗漏。

我尝试过缓存登陆baseApp成功到切换场景后的这个阶段的事件,在切换到实际场景中再重发一次,但是夹杂的其他登陆信息,貌似造成Player实体重复生成了,出现了其他错误。

请问这种情况,应该怎样保证数据恢复事件能确保完整下发呢。

事件系统有暂停接口,加载完再激活。 挂一个全局World脚本跟你有多少个场景是无关的。 你可以挂在摄像机上, 或者挂在clientapp上, 总不可能有多个了吧。 自己想办法。

事件处理机制非常简单, 就是一个队列, 一个往里面塞, 一个取出来执行, 如果暂停了那么processOutEvents就不要执行事件了, 等恢复再执行。

你自己调试一下,在_isPauseOut为true的时候, doingEvents_out是否正确缓存了后续的事件, 在恢复后是否有执行到。

其实这个地方你似乎不需要判断玩家是否在游戏中 , 如果玩家在游戏中, 那么服务器同步过来的场景地图信息一定是游戏场景。

而你的渲染部分应该根据插件告诉你的地图信息加载地图, 不需要知道当前在什么场景中。

你要解决的问题仅仅是你在加载场景过程中可能漏掉的事件, 此时在切换场景中用暂停机制之后再恢复就可以了。

解决方案: 是的,我的问题就是要解决“重登陆”时加载场景过程中遗漏的事件。 说下我的解决过程: 1、重登陆的过程,其实就是接管服务器存在的上一次登陆的玩家实体。按照当前的插件做法,只要客户端一连接上baseapp,就必定会同步实体所有的属性过来。同时这些属性被更新后,还会触发各自的set_xx的属性方法。而这些方法,应该是在恢复场景里被调用,而不是在登陆界面的过程中就被消耗掉。所以,要对这部分的数据进行拦截缓存。最好的地方是在插件的 KBEngine::Client_onCreatedProxies创建实体的方法中,一开始就把事件发送给暂停掉。

2、按1的步骤事件暂停了,还并不会有预期效果。如果按照当前场景只注册当前场景的相关事件的写法,那么由于事件暂停调用过早,此时实际场景还未开始加载,所以场景事件并没有被注册到Event系统里面,后来才注册的事件,是并不会享受到缓存事件的服务,因为在Event::fire_方法中,没注册的事件并不会进行事件缓存。

private static void fire_(Dictionary<string, List<Pair>> events, LinkedList<EventObj> firedEvents, string eventname, object[] args)
                {
                        monitor_Enter(events);
                        List<Pair> lst = null;
                        
                        if(!events.TryGetValue(eventname, out lst))
                        {
                                if(events == events_in)
                                        Dbg.WARNING_MSG("Event::fireIn: event(" + eventname + ") not found!");
                                else
                                        Dbg.WARNING_MSG("Event::fireOut: event(" + eventname + ") not found!");
                                
                                monitor_Exit(events);
                                return;
                        }
                        
                        for(int i=0; i<lst.Count; i++)
                        {
                                EventObj eobj = new EventObj();
                                eobj.info = lst[i];
                                eobj.args = args;
                                firedEvents.AddLast(eobj);
                        }
                        
                        monitor_Exit(events);
                }

3、所以,要想在场景加载完成以及当前场景的场景事件注册完成,就可以激活之前缓存的事件,则实际上,要做的还需要把所有fireOut的事件在缓存起来,不执行。

4、然后在场景加载完成,再遍历之前缓存的fireOut事件,就可以完整的恢复所有场景数据,按照我目前的测试,可以完美恢复。

5、(实际上在步骤1处进行事件暂停,会导致一个问题就实体__init__完成后,无法通知给外部UI层,这里还需要自己改一个不收事件暂停的通知方法。目前的插件,按我当前的理解,想要不遗留的恢复数据和这个通知方式是存在必定的冲突的,不知道我的理解对不对哈)

以上就是我问题的解决方法。

Clone this wiki locally