Open topic with navigation
Multi User Environment
Using viznet we can recreate the Network communication tutorial to easily
support multiple users by using the client server approach.
Server
The first thing to do is to add the required imports, start the Vizard
engine, set the background, and position the display so that the full
map will be visible as the server is used to watch other interactions.
import viz
import viznet
viz.go()
viz.clearcolor(.2,.2,.4)
viz.MainView.setPosition([70,45,-60])
viz.MainView.lookAt([0,0,10])
Next we create a list of objects which must be identical to the list
of objects on the client. These are the allowable character displays for
each client. We then add the map, and finally an empty dictionary to store
client information in.
objectList = ['duck.wrl','logo.wrl','vcc_male.cfg','vcc_female.cfg']
maze = viz.add('tankmaze.wrl')
objects = {}
Here we define specific events which will be used in the interaction
between the client and the server. These events must be defined in the
same way on each client.
UPDATE = viznet.id('update')
CLIENT_DISCONNECTED = viznet.id('client_disconnected')
CHAT = viznet.id('chat')
Now we start the server and handle client joining and client leaving
events. The onStopClient function preforms some cleanup actions as described
in the comments.
viznet.server.start(port_in=14950,port_out=14951)
def onStartClient(e):
print('** Client joined:',e.sender)
viz.callback(viznet.CLIENT_CONNECT_EVENT,onStartClient)
def onStopClient(e):
print('** Client left:',e.sender)
viznet.server.send(CLIENT_DISCONNECTED,client=e.sender)
# Alert other clients
objects[e.sender].remove() #
Remove object from our map
del
objects[e.sender] # Delete user from array
viz.callback(viznet.CLIENT_DISCONNECT_EVENT,onStopClient)
The update function essentially updates a client's position, however
if this is the first time the client's position is updated, it also adds
the client to the map. Adding the user this way makes it so we don't have
to have another event to handle when the client has picked a character.
Further more this way we don't have to send a list of already connected
clients to all new clients.
def update(e):
try:
objects[e.sender].setPosition(e.pos)
objects[e.sender].setEuler(e.ori)
except
KeyError: # If key doesn't exist add the user's
object
objects[e.sender] = viz.add(objectList[e.object],scale=[2,2,2],pos=e.pos,euler=e.ori)
objects[e.sender].tooltip =
e.sender # Sets the tool tip for this object
viz.callback(UPDATE,update)
Here we import and define the tool tip for the server.
# Tool Tip
import viztip
tth = viztip.ToolTip()
tth.start()
tth.setDelay(0)
The following portion of the server handles the chat features. The updateChat
function updates the string to be displayed. The variable text is the
on screen messages which is limited at 5, and the variable input displays
the messages input by the server as they are input. The onChat function
is called whenever the CHAT event is called which was defined previously.
# Chat Stuff
def updateChat(msg): # Function to update the chat display
text.message('\n'.join(text.getMessage().split('\n')[1:])+'\n'+msg)
text = viz.addText('\n\n\n\n',viz.SCREEN) # Add to screen
text.alignment(viz.TEXT_LEFT_BOTTOM)
text.fontSize(25)
text.setPosition(.05,.08)
input = viz.addText('> _',viz.SCREEN) # Add to screen
input.fontSize(25);
input.setPosition(.032,.05)
input.visible(0)
# We only want visible when in type mode
def onChat(e): # Update the message display
updateChat(e.client+':
'+e.chat)
viz.callback(CHAT,onChat)
This portion handles the input of messages. To start typing a message
one must first press 'y' thus enabling talking mode. Once in talking mode
one can exit by either sending the message with the enter key, or canceling
the message by pressing escape.
Note that this example only handles the basic keyboard characters.
talking = False
def onKeyDown(key):
global
talking
if
key == 'y' and not talking: # Start talking mode
talking = True
input.message('>
_')
input.visible(1)
elif
key == viz.KEY_ESCAPE and
talking: # Exit talking mode
talking = False
input.visible(0)
return True
elif
talking and key == viz.KEY_RETURN:
# Send message
talking = False
viznet.server.send(CHAT,client='SERVER',chat=input.getMessage()[2:-1])
input.visible(0)
# Update
display with our message
updateChat('SERVER'+': '+input.getMessage()[2:-1])
elif
talking and key == viz.KEY_BACKSPACE:
# Delete a character
if len(input.getMessage()) > 3:
input.message(input.getMessage()[:-2]+'_')
elif
len(key) > 1: # Handle special keys
return False
elif
talking and 32 <= ord(key) <= 126:
# Input message
input.message(input.getMessage()[:-1]+key+'_')
viz.callback(viz.KEYDOWN_EVENT,onKeyDown,priority=-6)
Client
The client starts the same by setting up the background, adding the
map and creating an empty dictionary of objects.
import viz
import viznet
import vizinfo
import vizinput
viz.go()
viz.clearcolor(.2,.2,.4)
maze = viz.add('tankmaze.wrl')
objects = {}
Then a timer is defined which is used to send updated position and orientation
data, followed by the user defined events, which are identical to those
on the server.
UPDATE = viznet.id('update')
CLIENT_DISCONNECTED = viznet.id('client_disconnected')
CHAT = viznet.id('chat')
Here is a clean way to connect to a server. This continues to probe
the user for a server to connect to until it is connected, or the user
no longer wishes to try. If the user does not connect the application
is closed.
SERVER_NAME=vizinput.input('Enter Server Name')
while not viznet.client.connect(SERVER_NAME,port_in=14951,port_out=14950):
if
vizinput.ask('Could not connect to ' + SERVER_NAME + ' would you like to try
another?'):
SERVER_NAME=vizinput.input('Enter Server Name')
else:
viz.quit()
break
Here we create the object list, which is identical to that on the server,
and ask the user which object they would like to be.
objectList = ['duck.wrl','logo.wrl','vcc_male.cfg','vcc_female.cfg']
obj_choice=vizinput.choose('Connected.
Select your character',objectList)
The timer portion sends updated information to all clients at the specified
interval. Notice that we additionally add the client parameter so that
other clients know where the message originated. We also send which index
in the object array we selected so that the server and other clients know
which object we are.
def sendUpdate():
ori= viz.MainView.getEuler()
pos = viz.MainView.getPosition()
viznet.client.sendAll(UPDATE,client=viz.getComputerName(),object=obj_choice,pos=pos,ori=ori)
vizact.ontimer(.05,sendUpdate)
Here the client handles updates. Since the client uses a sendAll the
client receives updates about itself. Thus we must first ignore messages
that originated from itself. Then like we did with the server update function
we update the position and orientation if the object already exists otherwise,
we add the new client's object.
def update(e):
if
e.client != viz.getComputerName():
try:
objects[e.client].setPosition(e.pos)
objects[e.client].setEuler(e.ori)
except KeyError:
objects[e.client]
= viz.add(objectList[e.object],scale=[2,2,2],pos=e.pos,euler=e.ori)
objects[e.client].tooltip
= e.client
viz.callback(UPDATE,update)
The client_disconnected function performs some simple cleanup when another
client disconnects, and the onStopServer function closes the client.
def client_disconnected(e):
objects[e.client].remove()
del
objects[e.client]
viz.callback(CLIENT_DISCONNECTED,client_disconnected)
def onStopServer(e):
print('Disconnected from server')
viz.quit()
viz.callback(viznet.SERVER_DISCONNECT_EVENT,onStopServer)
The tool tip functionality is then added.
import viztip
tth = viztip.ToolTip()
tth.start()
tth.setDelay(0)
Finally the chat is defined. This is nearly identical to the server's
chat except that we use the client's sendAll function, rather than the
server's send function, and because of that the client does not need to
handle its own messages.
def updateChat(msg):
text.message('\n'.join(text.getMessage().split('\n')[1:])+'\n'+msg)
text = viz.addText('\n\n\n\n',viz.SCREEN)
text.alignment(viz.TEXT_LEFT_BOTTOM)
text.fontSize(25)
text.setPosition(.05,.08)
input = viz.addText('> _',viz.SCREEN)
input.fontSize(25)
input.setPosition(.032,.05)
input.visible(0)
def onChat(e):
updateChat(e.client+':
'+e.chat)
viz.callback(CHAT,onChat)
talking = False
def onKeyDown(key):
global
talking
if
key == 'y' and not talking:
talking = True
input.message('>
_')
input.visible(1)
elif
key == viz.KEY_ESCAPE and
talking:
talking = False
input.visible(0)
return True
elif
talking and key == viz.KEY_RETURN:
talking = False
viznet.client.sendAll(CHAT,client=viz.getComputerName(),chat=input.getMessage()[2:-1])
input.visible(0)
elif
talking and key == viz.KEY_BACKSPACE:
if len(input.getMessage()) > 3:
input.message(input.getMessage()[:-2]+'_')
elif
len(key) > 1:
return False
elif
talking and 32 <= ord(key) <= 126:
input.message(input.getMessage()[:-1]+key+'_')
viz.callback(viz.KEYDOWN_EVENT,onKeyDown,priority=-6)
See also
In this section:
Networking basics
viznet
Networking Command Table
Cluster basics
Other sections:
Tutorial: Network communication