link commands link user class Server( net, Cmds, buffer, Tsock_user, Tuser_sock, port, version ) method open_net() # open up network to listen for connections until server is shut down net := open(":" || port, "nl") | stop("Open failed: ", sys_errstr(&errno)) write("Virtual Environment Server " || version || " started on port ", port) end # method open() method run() local L, socket_list, sock, xx, message repeat { #Check to see which socket is ready for reading # socket_list := [net || !Tuser_sock ] # doesn't create list - why???? socket_list := [ net ] every xx := key(Tuser_sock) do { put(socket_list, Tuser_sock[xx] ) } # it is very important not to do select( socket_list, 0 ) - that returns # instantly and creates a tight loop which bombs the cpu - this # waits for input on one of the sockets in the list L := select( socket_list ) every sock := !L do { write(" every sock type(sock) ", type(sock) ) # Make new conncetion if listener ready & user is ready to login if sock === net then { buffer := read(net) #see if command is valid and a login request if Cmds.IsCommand(buffer) then { if (Cmds.GetCommand(buffer))[1] === "login" then { user := User(Cmds.GetMessage(buffer, 8)) Tsock_user[net ] := user Tuser_sock[user] := net message :="User " || user.uid || " has logged in. " ForwardMessage( net, message ) # inform other users write(net, "\\say Connection made. There are ", *Tuser_sock, " online at this time.") } else { write(net, "\\say Connection not made, client login error") close(net) } } else { write(net, "\\say Connection Not Made, Not valid command") close(net) } # create another net listener socket to replace the last one # note that the last one gets connected to a user in the lower # level code regardless of whether the login works, and is thus # not listing for any new users net := open(":" || port, "nl") | stop("Open failed: ", sys_errstr(&errno)) write("There are ", *Tuser_sock, " online\n", "Sockets: ", *Tsock_user) buffer := "" } else { # Read from connected, logged-in Users if buffer := read( sock ) then { ExecuteCommand( sock ) } else { # user is no longer connected DeleteUser( sock ) } } } # end every sock := } # end repeat end # method run() #validate command and service it method ExecuteCommand( sock ) local UserCmd if Cmds.IsCommand(buffer) then { UserCmd := (Cmds.GetCommand(buffer))[1] if Cmds.ValidCommand(UserCmd) then { case UserCmd of { "say": { ForwardMessage( sock, buffer ) } "logout": { DeleteUser( sock ) } default: write("Command not implemented yet.") } } else { fail } } else { fail } end #delete user from server & close conection method DeleteUser( sock ) local n, Xuser, message Xuser := Tsock_user[sock] message := "User: "|| Xuser.uid || " is logging out. " ForwardMessage( sock, message ) # inform other users write(sock ,"\\logout " ) # trailing blanks are necessary here # this command goes back to originator like an ack and # does the actual work on the client side WSync() delete( Tsock_user, sock ) delete( Tuser_sock, Xuser ) close( sock ) # write("Deleted user ", Xuser, "\nNumber Users: ", (*Tuser_sock)) , "::" , (*Tsock_user)) )_ write("Deleted user ", Xuser.uid ) end #write message to all users - including self method ForwardMessage(sock_from, buffer ) local sock, newbuf, sender if *Tuser_sock == 1 then { write( sock_from, "\\say No other users connected to talk to.") } sender := Tsock_user[ sock_from ].uid newbuf := "\\say "|| sender || ": " || Cmds.GetMessage(buffer,6) every sock := !Tuser_sock do { write(sock ,newbuf) } end # method ForwardMessage() ### class Server( net, Cmds, buffer, # Tsock_user, Tuser_sock, # port, version ) initially version := "v.02" port := "4500" Tuser_sock := table() # same data in both tables, keyed for Tsock_user := table() # forward or reverse lookup buffer := "" #server commands table & methods Cmds := Commands() end # class Server