Module Pager3 Version: 1.0 Authors: Bob Le Gob Code based on Pager2 v3.0 - Authors: Laurent PLUMAT, Marc BARILLEY, Christophe LOREK Last update: 02/02/2003 ************************************************************************************ Pager3 v1.0 fixes a bug described below. Beyong this fix, there's no difference with Pager2 v3.0. This module is provided as a new starting point for the community, letting the official module in its unmodified official version. Scolians who wish to implement the correction in Pager2 can do it, but further developments on the Pager, if any, will be done in Pager3. Reproducing the bug: ******************** - Create a new project with SCS2: - Add a Contact module with autostart option checked. - Add a Login module - Add a Pager2 module - Links: shell.start -> pager2.start pager2.in -> pager2.show shell.start -> login.start login.loginChanged -> pager2.!changeLogin - Zones: - Server side: - Contact - Button - Client side: - Login - Button, Text - Pager2 - pager, box Launch the site, wait for the first client to automatically appear then launch a second client. Change the login of one of the client. You'll see that the other client has now lost the list of pager connected clients. The problem appears if only two clients are connected, and if one of them changes its login. Explaining the bug: ******************* - spager.pkg - Server part -------------------------- - The '!changeLogin' Pager2 action is a server-side action. It's handled by the cbchangeLogin() callback, which, through cbSendNewLogin(), sends intra module messages to all connected clients, requesting them to call the __changeLogin() function and providing the old and new login of the client that triggered the action, probably from a Login module. - cpager.pkg - Client part -------------------------- - The client part handles a list of connected clients (allClients) that contains all clients except yourself. - The client part searches in this list the client that changed its login. - Once found, it removes it from the clients list, as well as from the compList object. - If the clients list is now empty, then it calls one Resizing function. - It paints the Container object to update the display. - It updates the data of the client that changed its login. - After a check to ensure how/why the change of login occured, the updated client is added to the clients list and in the compList object. Then the Container object is painted again. - The rest is of no importance to us. - What happens in the Resizing function ? Beyond a possible resizing, there are a few interesting actions: - It destroys the compList object. - It destroys the compText object used to display the "You are just now the only connected people to this site" message. - It checks if the clients list is empty or not: - If yes, it creates the compText again. - If not, it creates and fills the compList again. - Then it paints the Container object. - OK, what happens when we are reproducing the bug with our two clients, let's say me and another one. - The other one changes its login. - It's removed from my clients list. - My list is now empty, so I call the Resizing function. I destroy my compList and my compText, and I create the compText again. - I update the client data and then add the updated client to my clients list and to the compList ... except that the compList hasn't been re-created and no more exists. Here's our main bug. - Let's investigate a bit further. Once the problem occurs, what happens if new clients arrive in the site. If we test this, we see that the client who now has the display problem doesn't seem to update anything anymore. - The 'start' Pager2 action is a server-side action that triggers the cbStart() callback. This one uses the cbSendUsersList() function to send the list of other clients to the new client as well as to warn the other clients of the arrival of this new client. This is done through intra module messages requesting the trigger of the __addUser() function and providing the login of the transmitted client (be it the new client or another client, depending on the client destination and content of the message). - So, if a third client connects to our test site, it will trigger the __addUser(newclientlogin) function by me (who have the display problem) and by the second client. This one will have no problem, but what happens for me ? - The new client is added to the clients list. - We check if there's an existing client at position 1 in the clients list. If there's no client, this means that our client is the first and single client in the clients list. - If this is the case, we call the above Resizing function which will create and display the compList (as our clients list is not empty and contains one client). - If this is not the case, we just add the new client to the compList. - And obviously, despite a painting of the Container object, my own interface doesn't seem to react anymore. Why ? The fact is that even if I do not have any sign of existence of the second client in my interface after he changed its login, I added it again to my clients list. This means that when the third client arrives, my clients list isn't empty, and thus, when I add the third client, I now have two clients in the list and I have a client at position 1 in the list. Thus the Resizing function is not called and I try again to add the client to a still non existing compList ... And we are stuck again. No hope to get out of this as long as there are other clients in the site... - "Luckily", solving the main bug will remove this second problem too as it's only a consequence of the main bug, and not a bug by itself. Solving the bug: **************** - In a few words, the problem is that our compList object isn't created again when the only other client in the site changes its login. - The faulty code part in the __changeLogin() function is the following one: ... /* verification que le login n'existe pas encore (deconnexion reconnexion) */ let search_in_list allClients @cbloginBylogin nlogin -> fu in if fu == nil then let order nlogin 0 allClients -> place in ( set allClients = addlogin f allClients place 0; if contPager == nil then nil else ( _ADDcompList listPager place [nlogin if (f.online == 1)||(f.online == 3) then NoMsgBMP else IncomingMsgBMP]; _PAINTcontainer (contPager) ) ) else ... - Correction: ... /* verification que le login n'existe pas encore (deconnexion reconnexion) */ let search_in_list allClients @cbloginBylogin nlogin -> fu in if fu == nil then let order nlogin 0 allClients -> place in ( set allClients = addlogin f allClients place 0; if contPager == nil then nil else ( /* Bug correction from Pager2 - Bob Le Gob Feb. 2003 */ if ((sizelist allClients) == 1) then ( if inZone == 1 then cbcontZoneResize [nil ztaille.Zx ztaille.Zy ztaille.Zw ztaille.Zh] nil else cbcontResize nil nil nil ztaille.Zw ztaille.Zh; _SDELcompList listPager nlogin; 0; ) else 0; /* End of correction - Bob Le Gob Feb. 2003 */ _ADDcompList listPager place [nlogin if (f.online == 1)||(f.online == 3) then NoMsgBMP else IncomingMsgBMP]; _PAINTcontainer (contPager) ) ) else ... - Explanation: Once the updated client is added again to our clients list, and if this added client is the single one in the list, we call the Resizing function, which will create again the compList that will be used to display our updated client. As the Resizing function fills in the compList, I delete the client added by this function and let the original code add it again with the proper features. And this is OK.