2.4.1 View部分

为了支持脚本系统,下面这些View,都没有定义明确的Control。而是通过Message方式实现,直接向特定的View发送自定义的字符串命令实现控制功能。


  • TemporaryView

    • ChatRoom的创建与进入

      UserInfo.java:


      private void joinRoom(long roomid, long sessionid) {
      	Procedure.execute(ProcedureHelper.nameProcedure("joinRoom", () -> {
      		ChatRoom view = table.Roominfocache.update(roomid);
      		if (null == view) {
      			.........................................
      			view = ChatRoom.createInstance();
      			view.setInfo(new ViewChatRoomInfo(info.getName(), roomid));
      			view.setRoomId(roomid);
      			table.Roominfocache.insert(roomid, view);
      		}
      		view.getMembership().add(sessionid);
      		return true;
      	}));
      }
      

      ChatRoom被保存在一个roomid索引的Cache中,如果Cache中没有则被创建出来,加入Cache。随后当前用户作为ChatRoom成员加入。


      用户加入ChatRoom:

      ChatRoom.java:


      protected void onAttached(long sessionid) {
      	RoomInfo.increment_memberCount_mapkey(roomid, 1);
      }
      

      这里,服务器作了一个简单计数。在成员加入信息通告给所有成员之后,服务器生成这个消息告知服务器应用加入动作已经完成。

      chatroom.js:


      v100.chatviews.ChatRoom.onopen = function(instanceid, memberids) {
      	currentChatView = this[instanceid];
      	showChatFrame(currentChatView);
      
      	ctx.register(currentChatView, "info", function(e) {
      		showChatRoomName(e.value);
      	});
      	ctx.register(currentChatView, "names", function(e) {
      		updateName(e.sessionid, e.value.nickname);
      	});
      	ctx.register(currentChatView, "lastmessage", function(e) {
      		if (e.type == 1 || e.type == 2)
      			showMessageToAll(e.value.user, e.value.msg);
      	});
      }
      

      新成员自身,当前的chatView在客户端创建出来,showChatFrame显示出聊天界面,注册了ChatRoom的3个字段的listener,监听之后的ChatRoom的信息变动。


      v100.chatviews.ChatRoom.onattach = function(instanceid, memberid) {
      	onMemberAttach(memberid);
      }
      

      之前的成员,得到通告,有新成员加入了。随后,新成员的被订阅信息被发给之前的成员。


      function onMemberAttach(sessionid) {
      	var done = false;
      	if (typeof (currentChatView[sessionid]) != "undefined"
      			&& typeof (currentChatView[sessionid].names) != "undefined") {
      		showTextToMessages("[user \"" + currentChatView[sessionid].names.nickname
      				+ "\" enter room]");
      		done = true;
      	}
      	if (!done)
      		setTimeout("onMemberAttach( " + sessionid + ")", 1);
      }
      

      这里使用了一个1ms定时器探测收到的新成员被订阅信息(用户名),收到以后在聊天室报告加入消息。事实上,新成员进入消息与被订阅信息的数据打包送往客户端,只不过先于进入消息之前通告订阅信息逻辑上讲不合理,所以才会有这样的实现。存在避免使用定时器的方案——onMemberAttach使用容器记录新成员sessionid, 处理订阅信息时, 检查sessionid是否被记录,如果是,作为新成员加入处理,容器内删除sessionid,如果不是,按一般逻辑处理。


    • ChatRoom的离开

      • 主动离开

        用户向ChatRoom发送离开Message

        chatroom.js:


        function doLeave() {
        	context.send(currentChatView, "leave=");
        }
        

        ChatRoom.java:


        protected void onMessage(String message, long sessionid) {
        	int index = message.indexOf('=');
        	if (-1 == index)
        		return;
        	final String cmd = message.substring(0, index).trim();
        	final String params = message.substring(index + 1);
        	switch (cmd) {
        	...............................
        	case "leave": {
        		getMembership().remove(sessionid, (byte) 1);
        		return;
        	}
        	}
        }
        

        服务器从成员列表中移除当前用户,设置用户定义closeReason为1

        ChatRoom.java:


        protected void onDetached(long sessionid, byte reason) {
        	RoomInfo.increment_memberCount_mapkey(roomid, -1L);
        	........................
        }
        

        这里,服务器作了一个简单计数。在成员离开信息通告给其余成员之后,服务器生成这个消息告知服务器应用离开动作已经完成。

        离开用户自身

        chatroom.js:


        v100.chatviews.ChatRoom.onclose = function(instanceid) {
        	showMainFrame();
        	currentChatView = null;
        }
        

        showMainFrame,进入房间选择。

        其它用户

        chatroom.js:


        v100.chatviews.ChatRoom.ondetach = function(instanceid, memberid, reason) {
        	onMemberDetach(memberid, reason >= 0);
        }
        

        通过onMemberDetach,报告用户离开信息,(reason==1)>= 0,报告memberid离开聊天室。

      • 断线离开

        ChatRoom.java:


        protected void onDetached(long sessionid, byte reason) {
        	RoomInfo.increment_memberCount_mapkey(roomid, -1L);
        	........................
        }
        

        这里,服务器作了一个简单计数。在成员离开信息通告给其余成员之后,服务器生成这个消息告知服务器应用离开动作已经完成。这里reason == -1

        其它用户

        chatroom.js:


        v100.chatviews.ChatRoom.ondetach = function(instanceid, memberid, reason) {
        	onMemberDetach(memberid, reason >= 0);
        }
        

        这里reason & 0onMemberDetach报告用户memberid断线。


    • ChatRoom的销毁

      Main.java:


      private static void updateViewHallsFromCache(long sessionid, String message, List destroylist) {
      	............................................
      	ProcedureHelper.executeWhileCommit(() -> Engine
      			.getApplicationExecutor().execute(() -> {
      				SessionManager.setViewHallsFromCache();
      				destroylist.forEach(room -> room.destroyInstance());
      			}));
      }
      
      cmdmap.put("deleteroom", (sessionid, params) -> {
      	if (params.length < 3 || !UserInfo.getInstance(sessionid).isCommandMode())
      		return;
      	Procedure.execute(() -> {
      		............................................
      		ChatRoom view = table.Roominfocache.update(removeroomid);
      		List<ChatRoom> roomlist = new ArrayList<>();
      		if (null != view) {
      			roomlist.add(view);
      			table.Roominfocache.delete(removeroomid);
      		}
      		updateViewHallsFromCache(sessionid, "delete room succeed",roomlist);
      		return true;
      	});
      });
      

      Roominfocache中删除room之后通过roomlist保存需要销毁的ChatRoom列表, updateViewHallsFromCache的执行以后, 调度一个事务提交后执行的任务, 该任务再次调度一个执行序列,首先通过SessionManager.setViewHallsFromCache();更新所有聊天室信息,接下来逐一删除保存的ChatRoom,之所以这样做是因为:

      ChatRoom.java:


      protected void onDetached(long sessionid, byte reason) {
              RoomInfo.increment_memberCount_mapkey(roomid, -1L);
              UserInfo info = UserInfo.getInstance(sessionid);
              if (info != null)
                      info.checkUpdateHallInfos();
      }
      

      info.checkUpdateHallInfos() 将前面更新的所有聊天室信息发送给sessionid指定的客户端。所以客户端看起来的效果就是:首先退出聊天界面进入聊天室选择界面, 接下来聊天室选择界面被刷新。

      注意,这里获取info之后需要判断是否为null,用户断线的情况下,用户关联的ViewContext进入关闭状态,这时info将返回null。一般来说,执行View的查询操作之后应该判断是否返回null,避免后续处理产生不必要的异常。

      可以这样做一个实验,进入test聊天室后,输入命令


          								
      1. .cm on 123456
      2. .cm createhall hall0
      3. .cm createroom hall0 room0
      

      上述3条命令输入完毕后,退出聊天室,然后重新进入room0聊天室。

      输入命令


          							
      1. .cm on 123456
      2. .cm deleteroom hall0 room0
      

      命令2发送完毕以后,当前用户自己被从聊天室界面踢出去,可以看到与前一个相同的聊天室选择界面,接下来room0立刻被刷新掉。

      另外,


      cmdmap.put("deletehall", (sessionid, params) -< {
      		............................................
      }
      

      可以删除整个聊天大厅,销毁大厅内所有ChatRoom


上一页 下一页