/* Library Syscom Treatment part - Mai 2001 - by JP DANDRIEUX */ /* V1.0 */ /* This library contains the main functions needed for the construction of the treatment */ /* of the SCOL communication system's stream */ /* struct TtextParam= [ Tid : S, FontSize : S, FontName : S, Color : S ] mkTtextParam;; struct TaudioParam= [ Aid : S, FreqMultiplyer : S, nBuffers : S, Sample : S, Stereo : S ] mkTaudioParam;; struct TvideoParam= [ Vid : S, Framerate : S, Width : S, nbcolor : S, Quality : S ] mkTvideoParam;; */ defcom SSYCtransmitScript=SYCtransmitScript I S;; defcom CSYCexecScript=SYCexecScript S;; defcom CSYCupdateMember=updateMember I I I I I I;; defcom CSYCupdateSalon=updateSalon I S I I I S S S;; defcom CSYCmute=SYCmute I I I I I;; defcom CSYCforward=SYCforward S I I S S;; defcom CSYCreceive=SYCreceive I I I S S;; defcom CSYCchangeRights=SYCchangeRights I I I;; defcom CSYCiStopStartToReceive=SYCiStopStartToReceive I I I I I I;; /* SYCmyplayparams is the list of my players params */ typeof SYCmyplayparams=[TaudioParam TvideoParam TtextParam];; struct Tcoord= [ Id : I, /* User Id */ Chan : Chn /* Channel corresponding to that user */ ] mkTcoord;; struct Tcom= [ Cid : I, /* User Id */ Rights: I, /* Rule for the right, default value is 0 */ Mute : [I I I] /* 0,1,10 or 11 each for : Muted by me, Muted by himself the order is Audio, Video, Text */ ] mkTcom;; struct Tsalon= [ SalonId : I, /* Id provided by the Scol server for unicity */ Name : S, /* Name of the salon, may not be unique */ Server : Tcoord, /* In P2P the server is always the other correspondant */ WhoAmI : I, /* 0: client, 1 : server, 2 : P2P */ Type : I, /* 100 Audio or 010 Video or 001 text or any combination*/ Params : [S S S], /* Audio, Video, Text */ Members : [Tcom r1] /* everyone in the communication except me */ ] mkTsalon;; /* ListChannel is the list of all the com channel existing */ typeof SYCchannelList = [Chn r1];; /* SYCclientList is the list of all users present in any salon i am server */ typeof SYCclientList=[Tcoord r1];; /* SYCserverList is the list users owning a salon i suscribed to */ typeof SYCserverList=[Tcoord r1];; /* P2PList is the list of users i am in a P2P session with */ typeof SYCp2pList=[Tcoord r1];; /* SYCsalonList is the list of salons i am involved in */ typeof SYCsalonList=[Tsalon r1];; /* SYCMyTcpServers is the ports where i have opened a Tcp Server */ typeof SYCMyTcpServers=[I Srv];; /* SYCnewChannel contains the last com channel opened with me or by me */ typeof SYCnewChannel=Chn;; /* SYCMINTOSPEAK is the minimum right value to send message to the salon */ var SYCMINTOSPEAK=0;; /* SYCMINTOHEAR is the minimum right value to receive message from the salon */ var SYCMINTOHEAR=0;; /* AvInServer is the variable setting if a com type is availabled via the SCOL server 100 -> audio is available | 10 -> video is available | 1-> text is available or any combination */ var AvInServer=1;; /* apply a function on every element of a list */ fun SYCapply_on_list(l,f,x)= if l==nil then 0 else let l -> [a nxt] in (exec f with [a x]; SYCapply_on_list nxt f x);; /* Teste si "sa" contient "ty", donc si la com de type ty est autorisée dans le salon */ fun SYCinclude (sa, ty) = let (sa/100)*100 -> a in let ((sa-a)/10)*10 -> v in let sa-a-v -> t in if ty==a || ty==v || ty==t then 1 else 0 ;;/* tested */ /* SYCTwoProxys is a user function called when the direct IP communication cannot be made */ typeof SYCTwoProxys = fun [I S] I;; /* SYCbroad is the TCP function who sends the buffer to a contact The function TwoProxy can launch a "_DMSsend this SSYCtransmitScript [Contact.Id ComScript]" or any similar way to use the SCOL server for the communication. So the ComScript is transfered to the proper client and the client execute it with __SYCexecScript. The parameter type is used to control the type of buffer sent 100->audio, 10->video 1->text. If nobuffer is sent then type must be 0*/ fun SYCbroad (contact, com)= let com ->[communic comscript type] in if contact.Chan !=nil then ( let communic -> [commu] in _on contact.Chan commu; 1 ) else ( if SYCinclude AvInServer type then exec SYCTwoProxys with [contact.Id comscript] else 0 ) ;; proto __SYCreceive=fun [I I I S S] I;; proto __SYCforward=fun [S I I S S] I;; /* The following function is the defcom called via TwoProxys by the scol server function __transmitScript It decodes the script and in case of "__received" or "__forward" functions the buffer must be put in a different variable and executed outside a script every other case lead to a script execution strcatn "__SYCreceive "::(itoh salon.Server.Id)::" "::(itoh salonid)::" ":: (itoh captype)::" \""::param::"\" "::buffer::nil strcatn "__SYCforward "::"\""::listeid::"\" "::(itoh salonid)::" "::(itoh captype):: " \""::param::"\" "::buffer::nil] */ fun __SYCexecScript(script)= let (strfind " " script 0) -> l in let substr script 0 l -> funname in let substr script l+1 strlen script ->scpt in let (strfind " " scpt 0) -> l in let substr scpt 0 l -> serverid in let substr scpt l+1 strlen scpt ->scpt in let (strfind " " scpt 0) -> l in let substr scpt 0 l -> salonid in let substr scpt l+1 strlen scpt ->scpt in let (strfind " \"" scpt 0) -> l in let substr scpt 0 l -> captype in let substr scpt l+2 strlen scpt ->scpt in let (strfind "\" " scpt 0) -> l in let substr scpt 0 l -> param in let substr scpt l+2 strlen scpt ->scpt in let substr scpt 0 (strlen scpt) -> buffer in ( if !strcmp funname "__SYCreceive" then ( __SYCreceive (htoi serverid) (htoi salonid) (htoi captype) param buffer ) else if !strcmp funname "__SYCforward" then __SYCforward serverid (htoi salonid) (htoi captype) param buffer else _scriptc _channel script ) ;; /* This function is called by SYCcreateServer and SYCcreateChannel to store the new created channelin the SYCnewChannel variable */ fun storechannel()= set SYCnewChannel=_channel ;; fun _closed()= 1 ;; fun _connected()= 1 ;; /* SYCcreateServer create a TCP/IP server on the communication port "port" it creates also a script that saves locally in SYCnewChannel each channel openned on the server */ fun SYCcreateServer(port)= if SYCMyTcpServers==nil then ( let ( _setserver (_envchannel _channel) port "storechannel" ) -> server in ( if server != nil then ( set SYCMyTcpServers=[port server]; 1; ) else 0; ) ) else let SYCMyTcpServers->[usedport usedserver] in 1 ;; /*Creation of a server whose port is between i and j fun[I I] I Pay attention the result is wether the port opened wether the first parameter if an opened port already exists in the SYCMyTcpServers variable */ fun SYCchooseServer(i,j)= if i>=j then nil else if SYCcreateServer(i) then ( /*_DLGMessageBox _channel nil "Port" (itoa i) 0;*/ i ) else SYCchooseServer i+1 j ;; /* SYCcloseServer close the server "server" */ fun SYCcloseServer(server)= if server == nil then nil else _closeserver server ;; /* SYCcreateChannel open a new channel on the server located at "fulladdress"=IP:Port it creates also a script that saves in SYCnewChannel each channel openned */ fun SYCcreateChannel (fulladdress) = _openchannel fulladdress "storechannel" _envchannel _channel ;; /* SYCcloseChannel close the channel "chan" */ fun SYCcloseChannel (chan) = if chan == nil then nil else _scriptc chan "_closechannel" ;; /* Get one element in the list, with the right Id... three times because of namespace conflict :) */ fun SYCgetSalonById(list, value)= if list == nil then nil else let list ->[first nxt] in if first.SalonId == value then first else SYCgetSalonById nxt value ;; fun SYCgetCoordById(list, value)= if list == nil then nil else let list ->[first nxt] in if first.Id == value then first else SYCgetCoordById nxt value ;; fun SYCgetComById(list, value)= if list == nil then nil else let list ->[first nxt] in if first.Cid == value then first else SYCgetComById nxt value ;; /* Remove one element in the list, with the right Id... three times because of namespace conflict :)*/ fun SYCremoveSalonById(list, value)= 0 ;; fun SYCremoveCoordById(list, value)= 0 ;; fun SYCremoveComById(list, value)= 0 ;; /* SYCcom2Coord makes a Tcoord list from a Tcom list */ fun SYCcom2Coord(comlist)= if comlist==nil then nil else let comlist->[com nxt] in (SYCgetCoordById SYCclientList com.Cid)::SYCcom2Coord nxt ;; /* __SYCupdateMember update or create if the user doesn't already exists it is called by DMSSend, because the direct link via channel may not exist */ fun __SYCupdateMember(salonid,hisid,rights,mutea,mutev,mutet)= let SYCgetSalonById SYCsalonList salonid -> ThisSalon in if ThisSalon != nil then let SYCgetComById ThisSalon.Members hisid -> ThisMember in if ThisMember != nil then /* update if the user exist */ ( set ThisMember.Rights = rights; let ThisMember.Mute->[a v t] in set ThisMember.Mute = [a+mutea v+mutev t+mutet]; 1 ) else /* create if it does not exist */ ( let (mkTcom[hisid rights [mutea mutev mutet]])->newmember in set ThisSalon.Members = newmember::ThisSalon.Members; 1 ) else ( _fooS "This Salon does not exist... (wrong Id in __SYCupdateMember)"; 0 ) ;; /* SYCmodifyMember is used to change the rights parameter of a member via __updatemember for the entire salon members */ fun SYCmodifyMember(salonid,hisid,rights)= let SYCgetSalonById SYCsalonList salonid -> salon in let salon.Members ->memberlist in let SYCcom2Coord memberlist->listcom in ( SYCapply_on_list listcom @SYCbroad [[CSYCupdateMember [salonid hisid rights 0 0 0]] strcatn "CSYCupdateMember "::(itoh salonid)::" "::(itoh hisid):: " "::(itoh 0)::" "::(itoh 0)::" "::(itoh 0)::nil 0 ]; __SYCupdateMember salonid hisid rights 0 0 0 ) ;; /* __SYCupdateSalon update or create if the salon doesn't already exists. The default value for the member list is nil proto __SYCupdateSalon = fun [I S I I I S S S] I;; */ fun __SYCupdateSalon(salonid,name,serverid,whoami,type,parama,paramv,paramt)= let ( if whoami == 0 then /* the server is registered */ SYCgetCoordById SYCserverList serverid else if whoami == 2 then /* in a min p2p the other side is server*/ SYCgetCoordById SYCp2pList serverid else /* i am the server */ mkTcoord[serverid nil] ) -> ThisServer in ( let (SYCgetSalonById SYCsalonList salonid) -> ThisSalon in if ThisSalon != nil then /* update */ ( set ThisSalon.Name = name; set ThisSalon.Server = ThisServer; set ThisSalon.WhoAmI = whoami; set ThisSalon.Type = type; set ThisSalon.Params = [parama paramv paramt]; set ThisSalon.Members = nil; /* member list is filled with __SYCupdateMember */ 1 ) else /* create if it does not exist */ ( set SYCsalonList= ( mkTsalon [salonid name ThisServer whoami type [parama paramv paramt] nil] )::SYCsalonList; 1 ) ) ;; /* SYCmodifySalon is used to change the param of a salon for the entire salon members. BE CAREFUL the server who sends this function is NOT updated, because its param (whoami) are different from the others */ fun SYCmodifySalon(salonid,name,serverid,whoami,type,parama,paramv,paramt)= let SYCgetSalonById SYCsalonList salonid -> salon in let salon.Members ->memberlist in let SYCcom2Coord memberlist->listcom in ( SYCapply_on_list listcom @SYCbroad [[CSYCupdateSalon [salonid name serverid whoami type parama paramv paramt]] (strcatn "CSYCupdateSalon "::(itoh salonid)::" \""::name:: "\" "::(itoh serverid)::" "::(itoh whoami)::" ":: (itoh type)::" \""::parama::"\" \""::paramv::"\" \""::paramt::"\""::nil) 0 ] ) ;; /* mute change the mute flag in salonid for hisid. All 3 mute must be filled. 0 is the default value for no change. 1 is the value to mute in another client list, 10 is the value to mute in my own list. To unmute give negative value (-1, -10)*/ fun __SYCmute(salonid,hisid,mutea,mutev,mutet)= let (SYCgetSalonById SYCsalonList salonid) -> ThisSalon in if ThisSalon != nil then let (SYCgetComById ThisSalon.Members hisid) -> ThisMember in if ThisMember != nil then /* update if the user exist */ ( let ThisMember.Mute->[a v t] in set ThisMember.Mute = [a+mutea v+mutev t+mutet]; 1 ) else ( _fooS "This Member does not exist... (wrong Id in __SYCmute)"; 0 ) else ( _fooS "This Salon does not exist... (wrong Id in __SYCmute)"; 0 ) ;; /* SYCiStopSTartToSend change locally the mute flag in salonid for hisid. All 3 mute must be filled. 0 is the default value for no change. 1 is the value to mute, -1 to unmute */ fun SYCiStopSTartToSend(salonid,myid,hisid,mutea,mutev,mutet)= __SYCmute salonid hisid 10*mutea 10*mutev 10*mutet ;; /* SYCiStopSTartToReceive change for hisid the mute flag in salonid for myid. All 3 mute must be filled. 0 is the default value for no change. 1 is the value to mute, -1 to unmute */ fun __SYCiStopStartToReceive(salonid,myid,hisid,mutea,mutev,mutet)= let SYCgetSalonById SYCsalonList salonid->salon in if salon.WhoAmI==0 then let (strcatn "__SYCiStopStartToReceive "::(itoh salonid)::" ":: (itoh myid)::" "::(itoh hisid)::" "::(itoh mutea)::" "::(itoh mutev) ::" "::(itoh mutet)::nil) ->cscript in SYCbroad salon.Server [ [CSYCiStopStartToReceive [salonid myid hisid mutea mutev mutet]] cscript 0 ] else if salon.WhoAmI==1 then let (strcatn "__SYCmute "::(itoh salonid)::" ":: (itoh myid)::" "::(itoh mutea)::" "::(itoh mutev) ::" "::(itoh mutet)::nil) ->cscript in SYCbroad (SYCgetCoordById SYCclientList hisid) [[CSYCmute [salonid myid mutea mutev mutet]] cscript 0] else let (strcatn "__SYCmute "::(itoh salonid)::" ":: (itoh myid)::" "::(itoh mutea)::" "::(itoh mutev) ::" "::(itoh mutet)::nil) ->cscript in SYCbroad salon.Server [[CSYCmute [salonid myid mutea mutev mutet]] cscript 0] ;; /* The SYCdiffuse* functions are user functions called when the received buffer was recorded with the same param as the local player. These functions must test the size of the sample and bufferise it if it was truncated The parameters are "salonid senderid buffer" */ typeof SYCdiffuseAudio=fun [I I S] I;; typeof SYCdiffuseVideo=fun [I I S] I;; typeof SYCdiffuseText=fun [I I S] I;; /* The SYCdifferentParam* functions are user functions called when the received buffer was recorded with different param than the player. These functions must test the size of the sample and bufferise it if it was truncated The parameters are "salonid senderid buffer param" you can configure here the possibility of starting a new player with param or stopping the current player to restart him with the new param. Or convert the buffer to fit the SYCtheirParams*/ typeof SYCdifferentParamAudio=fun [I I S S] I;; typeof SYCdifferentParamVideo=fun [I I S S] I;; typeof SYCdifferentParamText=fun [I I S S] I;; /* SYCreceive connait le salonid et l'id de l'emetteur (dans param). Il compare ses paramètres d'affichage avec param pour pouvoir envoyer le buffer au player concerné. Une version avancée pourait convertir le buffer recu pour qu'il convienne aux paramètres de lecture ou aux paramètres de salon */ fun __SYCreceive (myid,salonid,captype,param,buffer) = /* we may here test myid with the the currentid (or DMSid)*/ let (buffer)->buff in let strextr param -> [senderparam nxt] in let senderparam -> [senderid end] in let SYCmyplayparams -> [aparam vparam tparam] in if captype==100 then let strcatn senderid::" "::aparam.FreqMultiplyer::" "::aparam.nBuffers ::" "::aparam.Sample::" "::aparam.Stereo::nil -> a in if !(strcmp param a) then exec SYCdiffuseAudio with [salonid (atoi senderid) buff] else exec SYCdifferentParamAudio with [salonid (atoi senderid) buff param] else if captype==10 then let strcatn senderid::" "::vparam.Framerate::" "::vparam.Width::" ":: vparam.nbcolor::" "::vparam.Quality::nil -> v in if !(strcmp param v) then exec SYCdiffuseVideo with [salonid (atoi senderid) buff] else exec SYCdifferentParamVideo with [salonid (atoi senderid) buff param] else exec SYCdifferentParamText with [salonid (atoi senderid) buff param] ;; fun tr(listid,salon,captype,param,buffer)= if listid==nil then nil else let listid ->[diffusionid nxt] in ( if (atoi diffusionid)!=salon.Server.Id then let (strcatn "__SYCreceive "::(itoh atoi diffusionid)::" ":: (itoh salon.SalonId)::" "::(itoh captype)::" \"":: param::"\" "::buffer::nil) ->cscript in ( SYCbroad (SYCgetCoordById SYCclientList (atoi diffusionid)) [[CSYCreceive [(atoi diffusionid) salon.SalonId captype param buffer]] cscript captype]; 1 ) else ( __SYCreceive (atoi diffusionid) salon.SalonId captype param buffer; 1 ); tr nxt salon captype param buffer; 1 ) ;; /* Forward se déclenche seulement chez les serveur de com, elle transmet à __SYCreceive via le canal associé à l'id. Avant elle vérifie que l'id qui envoit (celle de param) a le droitd'envoyer au salon */ fun __SYCforward (strlistid,salonid,captype,param,buffer) = let strextr strlistid ->[listid end] in let strextr param -> paramlist in let paramlist -> [first nxt] in let hd first -> senderId in let SYCgetSalonById SYCsalonList salonid -> salon in let SYCgetComById salon.Members (atoi senderId) -> member in if member.Rights!=nil && member.Rights>=SYCMINTOSPEAK then ( tr listid salon captype param buffer; 1 ) else 0 ;; /* SYCbroadContactList lance un __SYCforward (via SYCbroad) au serveur par contact qui n'est pas mute. Si on est en p2p il lance un __SYCreceive au lieu du __SYCforward */ fun SYCbroadContactList(memberlist,server,tuple)= let tuple->[listeid salonid captype buffer param] in /* [S I I S S ] */ if memberlist==nil then let SYCgetSalonById SYCsalonList salonid->salon in if salon.WhoAmI==0 then ( SYCbroad server [[CSYCforward [listeid salonid captype param buffer]] strcatn "__SYCforward "::"\""::listeid::"\" "::(itoh salonid):: " "::(itoh captype)::" \""::param::"\" "::buffer::nil captype ] ; 1 ) else if salon.WhoAmI==1 then ( __SYCforward listeid salonid captype param buffer; 1 ) else if listeid==nil then 0 else let (strcatn "__SYCreceive "::(itoh salon.Server.Id)::" ":: (itoh salonid)::" "::(itoh captype)::" \"":: param::"\" "::buffer::nil) ->cscript in ( SYCbroad server [[CSYCreceive [salon.Server.Id salonid captype param buffer]] cscript captype ] ; 1 ) else let memberlist->[head tail] in let head.Mute -> [a v t] in let if captype==100 then a else if captype==10 then v else t -> test in ( if (test == 0) && head.Rights>=SYCMINTOHEAR then ( if listeid == nil then mutate tuple<-[(itoa head.Cid) _ _ _ _] else let strcat (strcat (itoa head.Cid) " ") listeid -> newlist in mutate tuple<-[newlist _ _ _ _]; 1 ) else 1; SYCbroadContactList tail server tuple; 1 ) ;; typeof i= I;; /* SYCsend2Target diffuse à travers SYCbroadContactList aux membres de salon si ils sont concernés par la conversation. tuple = [listtargetId salonId captype buffer captureparam] C'est ici que se produit le découpage par paquet de 8ko de la com si il y a lieu. */ fun SYCsend2Target(salon,tuple)= let tuple -> [_ _ captype buffer _] in if SYCinclude salon.Type captype then ( set i=0; mutate tuple<-[_ salon.SalonId _ _ _]; while i ThisSalon in if ThisSalon != nil then let (SYCgetComById ThisSalon.Members hisid) -> ThisMember in if ThisMember != nil then set ThisMember.Rights = newrights else ( _fooS "This Member does not exist... (wrong Id in ChangeRights)"; 0 ) else ( _fooS "This Salon does not exist... (wrong Id in ChangeRights)"; 0 ) ;; /* This function only usable by an admin send a SycchangeRights to every member of a salon it uses SYCcom2Coord with SYCclientList, SYCclientList contains all the members of the salon because the user is admin. There are no Rights in a p2p salon. */ fun SYCnewRight(salonid,memberid,newrights)= let SYCgetSalonById SYCsalonList salonid->salon in let salon.Members ->memberlist in let SYCcom2Coord memberlist->listcom in SYCapply_on_list listcom @SYCbroad [[CSYCchangeRights [salonid memberid newrights]] strcatn "__SYCchangeRights "::(itoh salonid)::" "::(itoh memberid):: " "::(itoh newrights)::nil 0 ]; 1 ;; /* TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS */ fun testchan(num,str)= _DLGMessageBox _channel nil (itoa num) str 0; 1 /* SYCcreateServer(3010) */ ;; fun main()= _showconsole; let "toto" -> word in let 123456 -> num in __SYCexecScript (strcatn "testchan "::(itoh num)::" \""::word::"\""::nil) ; 1 ;;