You are on page 1of 94

1

VRML 2.0 con Java CAPÍTULO 18

La Aplicación de un Cliente
2

VRML 2.0 con Java CAPÍTULO 18

Contenido CAPÍTULO 18

• Arquitectura del sistema general


• Arquitectura Cliente
• Los Protocolos de red
• La API
• La aplicación de la API
• Diseñar el Cliente
• Aplicación del Cliente
• Todo en conjunto
• Desarrollos futuros
• El otro extremo del cable

Arquitectura del sistema global


3

VRML 2.0 con Java CAPÍTULO 18

El enfoque que va a tomar es una extensión de las ideas presentadas en el


capítulo anterior. El sistema consta de una escena, que es habitada por un
número de entidades dinámicas. Cada entidad puede residir en un cliente
diferente y puede ser pensado como una fuente de múltiples tipos de datos de
streaming. Los únicos tipos de datos que vamos a estar utilizando en nuestro
sistema de chat de texto se avatar y movimiento, pero la misma base la
arquitectura del sistema se presta bien a multipunto de voz, en tiempo real el
comportamiento de figuras articuladas, y más.

Un aspecto importante de nuestro diseño es que un cliente puede ser una


"plataforma de observación" que no crea ningún tipo de entidades. En algunas
simulaciones militares, esto se conoce como el modo del stealth. El lado del
cliente API está estructurado de tal manera que el observador de los
parámetros, incluida su virtual "cámara ubicación", son independientes de
cualquier avatares los usuarios pueden crear por sí mismos.

Cada sistema cliente se supone que tienen un único usuario (o de


"observador") sobre el mismo, y cada usuario se supone que tienen un solo
punto de vista en un momento dado.
¿Cómo funciona?

Cualquier cliente puede crear cualquier número de entidades y transmitir flujos


de datos para cada una de esas entidades. Ambas actualizaciones movimiento
y el texto será tratado de la misma manera, y debería quedar claro cómo este
sistema puede ser ampliado para incluir chat de voz y comportamiento avatar
información.

El flujo de datos se envía a través de una red multicast a todos los demás
clientes, cada uno de los cuales se presenta el flujo de datos a sus usuarios.
Cada cliente también mantiene una base de datos de entidades cuyo estado se
actualiza utilizando los datos recibidos.

En la práctica, la multidifusión cada actualización a cada cliente es


impracticable. Suficiente ancho de banda es simplemente no están disponibles,
ya que muchos de los clientes se conectarán más de 28,8 kbps-módems.
Incluso si el ancho de banda no eran un problema, la carga de procesamiento
de todos los cambios que incluso pondrán un poderoso cliente de su sistema de
rodillas. Además, la multidifusión no es ampliamente disponible. Ya que
estamos de codificación tanto de nuestros clientes y nuestro servidor en su
totalidad en Java, la multidifusión no se permite el acceso directo en esta
aplicación. Java 1.1 incluirá soporte multidifusión, pero no es ampliamente
disponible en el momento en que el software para este capítulo se está
desarrollando.
4

VRML 2.0 con Java CAPÍTULO 18

Filtrado

Modificado nuestro diseño es que cada cliente conectar hasta un filtrado de


acogida, que se encuentra (conceptual) entre el cliente y la red multicast. La
filtración selectiva de acogida relés actualizaciones de la red para el cliente y
también envía actualizaciones desde el cliente a la red. El mecanismo de
filtrado es controlada por el cliente, que envía mensajes a la filtración de
acogida que le informe sobre el actual conjunto de parámetros de filtrado.

Las actualizaciones son filtrados a través de tres criterios: la agudeza, contar


horizonte, y de región a región visibilidad.

Cada cliente tiene una agudeza ajuste, lo que indica el tamaño mínimo a través
de la distancia en la proporción necesaria para que una entidad a ser visible
para que el sistema cliente. El servidor tiene el tamaño de una entidad y la
divide por la distancia entre la entidad y la ubicación del punto de vista del
sistema cliente. Si la proporción es inferior a la agudeza para el cliente,
entonces la entidad es demasiado pequeño y / o de lejos para que el cliente
vea. En tal caso, no hay actualizaciones de la entidad que se envían al cliente.
Por supuesto, hay muchas maneras de optimizar este cálculo a fin de evitar
cálculos de la distancia real o divisiones, algunas de estas se discuten en el
capítulo 19.

El horizonte contar indica el número máximo de entidades que el cliente está


dispuesto a recibir las actualizaciones para. El filtrado de acogida suele ordenar
a las entidades por la distancia desde el cliente, de modo que contar un
horizonte de 12 significa que sólo las 12 más cercanas entidades tendrán sus
actualizaciones enviadas al cliente.

De región a región, la visibilidad de información permite al cliente para decirle


al servidor qué partes del medio ambiente son visibles para el cliente. Regiones
podría, por ejemplo, corresponden a las habitaciones de una casa. Cada
habitación tendrá un número de región, y el cliente le dirá a la acogida filtro
que las regiones son visibles desde la región de que el usuario que se
encuentra sólo en las actualizaciones para las entidades visibles las regiones
se enviarán al cliente.

Es importante tener en cuenta que todos estos mecanismos de filtrado son


dinámicos. Si la entidad y / o el punto de vista del cliente para que se mueva
más cerca entre sí, comenzará a las actualizaciones de llegar al cliente, si se
mueven más lejos aparte, las actualizaciones se detendrá. Del mismo modo, si
el observador se traslada de una habitación a otra, el conjunto de las regiones
visibles cambio. Si una entidad se mueve de una región en un non visible, el
5

VRML 2.0 con Java CAPÍTULO 18

cliente de repente empezar a recibir las actualizaciones para ella, y deja de


recibir cuando la entidad se traslada a una región non visible.

Entidad de Registro

Además de la comunicación con el filtrado de acogida, el cliente se conecta a


un registro (¡no confundir con el registro de Windows 95!), Que mantiene una
base de datos simple de las entidades en el medio ambiente. El uso de este
registro de conexión, el cliente puede obtener información acerca de cualquier
entidad en el sistema. También puede añadir entidades en el Registro y
actualizarlos cuando sea necesario.
Las interfaces entre los componentes

Figura 18-1 muestra las relaciones entre los diversos componentes del sistema.
Hay varias cosas importantes de destacar. El registro y el filtrado de acogida
pueden estar situados en el mismo sistema, e incluso pueden ser parte del
mismo proceso. Ambos residen en la red multicast, que es utilizado para
comunicarse tanto los datos de los registros y actualizaciones.

Figura 18-1 componentes básicos del sistema

Sin embargo, la "red" puede residir exclusivamente en el servidor. Si el filtrado


de acogida, el registro, y la "red" existen en la misma máquina, el sistema de
manera eficaz, tiene un servidor.
Cuestiones de aplicación

Como se mencionó anteriormente, los protocolos de red abiertos. El filtrado de


acogida, el registro de acogida, y el cliente pueden aplicarse por separado por
los diferentes desarrolladores, y que todos debemos trabajar juntos.

Todos nuestros programas, tanto para el cliente y para el servidor, se llevarán a


cabo en Java. Esta opción tiene varias ventajas: la portabilidad del código
compilado, un buen apoyo para la multitarea, y la buena gestión de memoria.
Sin embargo, una limitación de que este enfoque todo Java impone a nosotros,
además de las limitaciones de rendimiento, es el hecho de que el modelo de
seguridad utilizado en la mayoría de los navegadores limita al cliente a
comunicar sólo con la acogida que se carga desde. En otras palabras, el filtrado
de acogida y el registro deben estar en la misma máquina, y el cliente debe
6

VRML 2.0 con Java CAPÍTULO 18

cargar applet de esa máquina.


Arquitectura Cliente

El software que se ejecuta en el cliente es en realidad compuesta de diferentes


piezas y en gran medida independiente de código. Allí será el navegador de
VRML, que presenta un mundo de tres dimensiones para el usuario. Que habrá
un applet de chat de texto, que permite al usuario enviar y recibir mensajes de
texto. Por último, habrá un multiusuario applet que recibe información de la
filtración de acogida y de las actualizaciones de la escena VRML, así como el
envío de actualizaciones en nombre del usuario, sino que también los controles
de filtrado. Esta arquitectura se muestra en la Figura 18.2.

Figura 18.2 El cliente de la arquitectura

El applet es multiusuario construido en la cima de una multi-API, que se


implementa como una librería de clases, que puede ser usado para crear
cualquier número de aplicaciones en el sistema multiusuario. Del mismo modo,
la aplicación de chat de texto se basa en un texto encima de la API. Estas API
ocultar el bajo nivel de los protocolos de alambre, lo que significa que se
pueden realizar cambios a los protocolos sin que ello afecte a ninguna de las
aplicaciones cliente. Del mismo modo, la API puede ser reajustada en caso
necesario, sin cambiar el cable de protocolo.

El cliente se comunica con el applet del navegador de VRML exteriores


utilizando el API que se introdujo en el capítulo 5, "La autoría de interfaz
externa". La aplicación cliente también provee de una "API" para el applet de
chat de texto, ya que los dos deben intercambiar información; Java estándar
interapplet mecanismo de comunicación se utiliza para este propósito. Figura
18-3 muestra una vista más detallada de la forma en que estos componentes
encajan entre sí.

Figura 18.3 La interfaz entre las diferentes partes del software de cliente

Los tres de los principales módulos (navegador VRML, applet de chat de texto,
multiusuario applet) se encuentra atado por el hecho de estar en la misma
página web, como se muestra en la Figura 18.4.
7

VRML 2.0 con Java CAPÍTULO 18

Figura 18.4 El documento que contiene la Web de tres componentes


Los Protocolos de red

La frase sobre el alambre de protocolo se utiliza a veces para referirse al


formato de los mensajes de datos que son enviadas a través de una red. Por la
normalización de este protocolo, estamos en condiciones de garantizar la
interoperabilidad de los componentes de software se ejecutan en diferentes
plataformas.
TCP e IP

Vamos a comenzar por el examen de las cinco capas de TCP / IP modelo para
las comunicaciones de red, como se muestra en la Figura 18.5. La capa física
se refiere a la transmisión (cables coaxiales, fibra óptica, cables serie, y así
sucesivamente). La capa de enlace de datos se refiere a la escasa capa de
protocolo, como SLIP o PPP o Ethernet elaboración.

Figura 18.5 El TCP / IP modelo de red de comunicación

La capa de red el protocolo IP, el protocolo de Internet. Se prevé abordar, en la


actualidad en forma de un número de 32 bits asociados a cada host en la red.
Las futuras versiones de la propiedad intelectual, comúnmente conocida como
IP-NG (para "Protocolo de Internet, Next Generation") o IP6, proporcionará un
mayor espacio de direcciones.

La capa de transporte que corre por encima de la propiedad intelectual puede


ser UDP o TCP, que se describe en el Capítulo 8.
8

VRML 2.0 con Java CAPÍTULO 18

Para obtener más información acerca de TCP / IP, es la referencia definitiva


Comer, interconexión con el protocolo TCP / IP (Tomo I).
Comunicaciones entre el Cliente y el filtro de acogida

Dado que hemos decidido que vamos a ser el envío de actualizaciones de


nuestro movimiento y mensajes de texto como el streaming de datos, tiene
sentido buscar una ligera capa de aplicación del protocolo que adecuado para
el envío de datos de streaming en tiempo real. Como resulta, por ejemplo, un
protocolo que ya existe: RTP, Real-Time Protocol. RTP se describe en detalle en
el RFC 1889, que se puede encontrar en la ftp://ftp.internic.net/rfc/.

RTP se suele utilizar sobre UDP, aunque no hay nada acerca de RTP que
requiere. RTP proporciona la funcionalidad básica que suele ser requerida por
cualquier aplicación en tiempo real: los números de secuencia, tiempo, y
fuente de identificación. Vamos a utilizar todos estos en nuestra aplicación.
El encabezado de RTP

Cada paquete de RTP, que suele ser encapsulada dentro de un paquete UDP,
tiene un título de al menos 12 bytes. Este encabezado se muestra en la Figura
18.6.

Figura 18.6 La cabecera RTP

Los dos primeros bits son un número de versión, que actualmente es 2. Hay
otro pabellón bits que no va a utilizar: Relleno, la extensión y el marcador.
También hay un recuento de las fuentes de los contingentes, que no va a
utilizar. Si eres curioso acerca de lo que estos campos se utilizan para, consulte
RFC 1889. En nuestro caso, sólo tendremos que configurar todos ellos a cero.

Tipo de la carga útil es un número de 7 bits que indica cómo el contenido de los
paquetes de RTP debe interpretarse. Algunos de estos tipos de carga ya se han
asignado, en su mayoría de diversos tipos de datos de audio comprimido.
Vamos a seleccionar arbitrariamente sin utilizar dos tipos de carga útil para
nuestros propósitos: 79 de movimiento y 80 actualizaciones para los mensajes
de texto.
9

VRML 2.0 con Java CAPÍTULO 18

El número de secuencia es de 16 bits de longitud y se utiliza para detectar (y


por lo general rechazan) los datos que se reciban fuera de secuencia. También
permite al cliente para calcular el importe de la pérdida de paquetes.

El timestamp de 32 bits se utiliza para la sincronización de efectos, y para


ayudar con la interpolación de los datos que faltan. En nuestro caso, que será
utilizado para indicar el momento en que un movimiento de actualización
deben entrar en vigor (la "toTime," para utilizar el término mundos de vida).
Aunque RTP no requiere que los relojes en diferentes equipos que se van a
sincronizar, asumiremos para nuestros propósitos que son.

El último campo de 32 bits en la cabecera es la SSRC, o Sincronización Fuente


identificador. Esto se utiliza para identificar cada fuente de datos en un
determinado período de sesiones RTP. En el caso de las aplicaciones de chat de
audio, el SSRC corresponde a un participante individual. En nuestra aplicación,
que será utilizado como identificador de una entidad única.

La cabecera es seguida por la carga útil, los datos reales que se envían. Al
igual que la cabecera de los usos de la carga útil de red estándar de pedidos de
bytes (de alto orden octetos primero). Tendremos dos tipos diferentes de carga
útil: el movimiento y las actualizaciones de los mensajes de texto.
Propuesta Actualizaciones

Cuando enviar una actualización de posición para una entidad, la carga útil
tendrá el formato se muestra en la Figura 18.7. Los campos son todos bastante
sencillos. Los tres primeros de 32 bits de punto flotante son los valores x, y y z
de la entidad-en otras palabras, un SFVec3f VRML. Los próximos cuatro son el
eje y el ángulo que especifica la orientación de la entidad, que debe reconocer
como uno de VRML SFRotation.

Figura 18.7 La carga útil formato para las actualizaciones de movimiento

El siguiente es un campo de 32 bits número de región, lo que indica que la


región que se encuentra esta entidad Tenga en cuenta que una entidad puede
teóricamente en más de una región al mismo tiempo, por ejemplo, un avatar
puede ser una larga serpiente que se extiende slithery varias regiones. En tal
caso, la entidad enviará varias actualizaciones de sí mismo, una para cada
región que es in
10

VRML 2.0 con Java CAPÍTULO 18

Tras esta es una medida del tamaño de la entidad. Esto se utiliza para el
filtrado basado en la agudeza, como se describe anteriormente en este
capítulo. No es esencial que este valor se precisa en particular, algo así como
el diámetro de una esfera saltando alrededor de la entidad, o incluso la
duración de la más larga del eje de una caja, lo haría bien.

Por último, hay un conjunto de banderas. Por el momento, sólo el de bajo orden
se utiliza poco, si conjunto, indica que la entidad que envía esta actualización
es salir de la simulación.
Mensajes de texto

Al igual que las actualizaciones de movimiento, se envían mensajes de texto


utilizando RTP. Los mensajes se almacenan en la carga útil de un paquete RTP.
La fecha y la hora se pasa por alto, ya que no es particularmente relevante
para las comunicaciones de texto.

El SSRC no debe ser necesariamente el mismo que el utilizado por el SSRC


moción actualizaciones de la misma entidad. Esto es importante: Cada sesión
RTP es independiente de los demás, y los valores SSRC se gestionan
independientemente. En RTP, la relación entre los dos períodos de sesiones se
establece mediante un nombre canónico o CNAME, que identifica la fuente de
los distintos flujos de datos. Hay un SSRC para la propuesta de actualización de
RTP período de sesiones, y un texto para el período de sesiones RTP. Si se
añade el streaming de audio, que se utiliza un tercer SSRC, que podría ser
diferente de los otros dos. Sin embargo, las tres fuentes (de movimiento, texto
y audio) tendría el mismo CNAME para atar juntos.

Vamos a tratar el SSRC que llevó en la propuesta de actualización de mensajes


como "especiales", en el sentido de que también se utilizará como
identificación de la entidad (que es la clave única de la entidad en el registro
de base de datos). Hacemos esto porque siempre será el envío de
actualizaciones de movimiento, mientras que los otros tipos de flujo de datos
son opcionales.

La carga útil formato de un mensaje de texto se muestra en la Figura 18.8. El


tipo de mensaje es un valor de 16 bits que actualmente se ponen a cero y se
reservó para la futura expansión. El siguiente valor de 16 bits es la longitud del
mensaje de texto. El texto en sí se llena el resto de la carga útil.

Figura 18.8 La carga útil formato para mensajes de texto


11

VRML 2.0 con Java CAPÍTULO 18

Filtrar mensajes

El cliente debe ser capaz de informar a la filtración de acogida de los distintos


parámetros del filtro, tales como la agudeza, contar horizonte, y las regiones
visibles. Dado que esta comunicación es estrictamente entre el cliente y el
filtro de acogida y no se envían a través de la red de multidifusión, no hay
necesidad de utilizar una norma existente como RTP para que la represente. El
formato del mensaje es que el filtro se muestra en la Figura 18.9.

Figura 18.9 El filtro de formato de mensaje

La actualización del puerto local es el puerto UDP en el cliente para que las
actualizaciones de movimiento deben ser enviadas por el filtro de acogida.
Desde este puerto se asignan dinámicamente por la pila TCP / IP en el cliente,
es necesario enviar esta información para el filtro de acogida. Por ejemplo,
digamos que el cliente crea un DatagramSocket usando el paquete java.net en
la que espera recibir actualizaciones movimiento. El puerto local es la dirección
asignada dinámicamente, por lo que el filtrado de acogida debe ser informada
de este número de puerto para enviar mensajes de actualización de
movimiento a la misma.

Sólo tres de los bits de la bandera en el filtro de mensajes se utilizan


actualmente. La orden de baja poco (0x0001) indica que el cliente no desea
recibir actualizaciones desde el filtro de acogida, en efecto, "se apaga el grifo"
para detener el flujo de datos. El siguiente bit (0x0002) pide que el filtro de
acogida reenviar el movimiento más reciente actualización que recibió de cada
entidad, lo que es útil si el cliente ha sido ocupado por un período de tiempo y,
por tanto, puede haber perdido actualizaciones. El tercer bit (0x0004) indica
que el cliente está a punto de desconectar.

La x, y y z son los valores de punto flotante números que dan la posición del
punto de vista. Tenga en cuenta que estos no tienen que corresponder a la
ubicación de cualquiera de las entidades, es decir, estos x, y, z y los valores
son independientes de los utilizados en la propuesta de actualización de
mensajes. Sin embargo, para el avatar del usuario, estos valores suelen ser los
mismos que en la propuesta de actualización de mensajes.

La agudeza visual en el horizonte sobre el terreno y contar con campos se


12

VRML 2.0 con Java CAPÍTULO 18

utilizan como se describe anteriormente en este capítulo.

Estos campos son seguidos por un recuento del número de regiones que son
visibles al cliente y, a continuación, por una serie de los números de la región.
Si el cliente puede ver las regiones 4, 18, 92, y 104, entonces la región se
cuentan 4 y será seguido por esos cuatro valores. Sólo mensajes de
actualización de las entidades en estas regiones se enviarán al cliente.
Recordemos que la actualización de cada mensaje contiene el número de
región de la entidad que lo envió.

La parte final del mensaje es un filtro de la cuenta del número de puertos


adicionales que el cliente está recibiendo los mensajes de. Cada puerto se
utiliza para un tipo diferente de datos, en la actual aplicación, el puerto cuenta
siempre es 1, y el único puerto especificado es el que el cliente de chat de
texto a la espera de recibir mensajes de texto. Si se añade chat de audio, el
conteo sería 2, y la segunda sería la entrada del puerto en el que el cliente
espera para recibir los paquetes de datos de audio.

Tenga en cuenta que el filtro de mensajes también actúan como "mantener


alives" o "los latidos del corazón." Si el filtro de acogida no recibe un mensaje
de filtro por lo menos una vez cada 10 segundos, se detendrá el envío de
actualizaciones para el cliente.
Puerto de uso

Claramente, nuestro sistema utilizará una serie de puertos. Propuesta de


actualización de mensajes desde el cliente son enviados a un puerto en
particular en el filtro de acogida. Texto actualizaciones se envían a un puerto
diferente en el filtro de acogida. Filtro de mensajes de control se envían a un
puerto de un tercer filtro en los de acogida.

Si el filtro de acogida es compatible con múltiples clientes, que pueden


compartir todos los mismos puertos, ya que el filtro de acogida puede decir
cuando un mensaje llegó por medio de la SSRC (es decir, entidad ID) en el
propio mensaje. Si un filtro de acogida había una docena de clientes, todos
ellos enviar datagramas a su actualización en el mismo puerto en el filtro de
acogida. El filtro de acogida sólo tendrán que recibir los paquetes en tres
puertos diferentes: uno para las actualizaciones de movimiento, uno de los
mensajes de texto, y un filtro de mensajes de control, independiente del
número de clientes que pueden compartir ese filtro de acogida.

El filtro de acogida enviará moción cambios a un puerto en particular en cada


uno de los clientes (que se especifica en el filtro de mensajes de cliente a que
el filtro de acogida). Que enviar mensajes de texto a un puerto en el cliente
13

VRML 2.0 con Java CAPÍTULO 18

(que se especifica como el primero de los puertos adicionales al final del filtro
de mensajes, como se ha descrito anteriormente).

En aras de la simplicidad, vamos a hacer algunas suposiciones acerca de las


relaciones entre los distintos puertos. Vamos a seleccionar un filtro de puerto al
que deben enviarse los mensajes y el uso que el número de puerto más 2
como la propuesta de actualización del puerto. Del mismo modo, vamos a
utilizar el filtro de mensajes puerto más 4 como el mensaje de texto puerto. Por
ejemplo, si el filtro de mensajes se envían al puerto 3000, entonces la moción
actualizaciones serán enviados al puerto 3002 y los mensajes de texto en el
puerto 3004. Figura 18.10 muestra las relaciones entre los distintos puertos.

Figura 18.10 en el uso del puerto sistema multiusuario


La comunicación entre el Cliente y el Registro

Hasta el momento hemos examinado el uso de la UDP la comunicación entre el


cliente y el filtro de acogida. Es hora de mirar la relación entre el cliente y la
entidad de registro.
Base de datos de las entradas

La información almacenada para cada entidad en el Registro se compone de


los siguientes:

• entidad ID: Identificador único de esta entidad


• CNAME: nombre canónico a un único nombre para la persistencia de la
entidad
• momento de la inscripción: la hora de que la entidad que apareció por
primera vez
• última actualización tiempo: el tiempo que la entidad fue de oído
• persistencia bandera: si está establecido, la entidad no necesita ser
refrescado periódicamente para mantenerse en la simulación
• avatar bandera: si está establecido, la entidad que corresponde a un
usuario humano
• URL: VRML hace referencia a un archivo que contiene una descripción de
la entidad
• Propietario: identifica el propietario de esta entidad
• la información pública: arbitraria cliente-cadenas de texto se especifica
14

VRML 2.0 con Java CAPÍTULO 18

Es importante comprender las diferencias entre estos ámbitos. La entidad es


una ID de 32-bit Identificador numérico, que se puede considerar como la
"clave" en la entidad de base de datos. El CNAME es una cadena que identifica
de forma única la entidad, y es destinado a ser única en el mundo. Si una
entidad es destruida y, a continuación, volver a crear, puede ser asignado una
nueva entidad de identificación, el CNAME, sin embargo, muestran que es la
misma entidad que había antes.

El propietario es el nombre del usuario que ha creado una entidad. Tenga en


cuenta que las entidades que son propiedad de los usuarios, no clientes,
incluso si un usuario cambia a un sistema cliente, que seguirá siendo
propietaria de todos las entidades que han creado en otros lugares.
Comunicación

El cliente abre una conexión TCP en el registro y uso de una línea orientada a
ASCII protocolo similar al utilizado por SMTP o NNTP. El cliente envía un
comando al servidor y obtener una respuesta de vuelta.

El cliente puede solicitar información sobre cualquier entidad, obtener una lista
de entidades, o para obtener una lista de entidades que han actualizado
recientemente sus entradas de registro.

Además, el usuario en el cliente puede elegir para identificar a él oa ella con un


nombre de usuario y contraseña. El cliente puede crear una nueva entrada en
la entidad de base de datos y proporcionar los datos de esa entidad.

Un usuario puede crear más de una entidad. Un usuario también puede


eliminar cualquier entidad que posean, así como una actualización de datos de
la entidad, siempre que ellos elijan. También pueden a su vez propiedad de una
entidad a otro usuario que se identifica por su nombre de usuario.

Entidades que se eliminan de la base de datos a menos que se actualizan


periódicamente. Este tiempo de espera puede ser apagado por el cliente
mediante el establecimiento de la "persistente" bandera en una entidad.

Si el registro está conectado a una red multicast, puede utilizar RTCP (parte de
la RTP protocolo descrito anteriormente) para enviar la información de registro
a otros anfitriones en la forma de SDES (Fuente descripción) mensajes. Esta
funcionalidad no está implementada en esta versión del software de servidor,
para obtener más información acerca de RTCP, consulte el documento RFC
1889.

El protocolo en sí es bastante simple. Los comandos y las acciones


15

VRML 2.0 con Java CAPÍTULO 18

correspondientes por el registro se detallan en las secciones siguientes.


El Comando HELLO

HOLA el comando comienza la interacción entre el cliente y el Registro. Se


tiene el siguiente formato:

HELLO optional_client_version_number optional_additional_information

El registro responde con

BIENVENIDOS optional_registry_version_number optional_banner_string

REHUSÓ reason_for_refusal

Por ejemplo, si el servidor sólo está dispuesta a aceptar hasta 25 clientes, que
podría responder con un mensaje a cualquier REHUSÓ adicionales clientes
potenciales.
El Comando GOODBYE

GOODBYE El comando se utiliza para permitir que el servidor sabe que el


cliente se desconecte. Se tiene el siguiente formato:

GOODBYE

El registro responde por el cierre de la conexión.


El Comando GETENTITY

El GETENTITY comando se utiliza para recuperar la información del Registro


para una entidad. El formato del comando es como sigue:

GETENTITY entid

Por ejemplo, si el cliente recibe un mensaje de actualización a una entidad


cuya entidad ID es 2357, y el cliente no tiene la información del Registro de esa
entidad, entonces el comando sería emitido

GETENTITY 2357

El registro responde a un comando GETENTITY con cuatro líneas de


información:

CNAME url propietario avatar persistentes creation_time


16

VRML 2.0 con Java CAPÍTULO 18

alias
SSRC_values

El CNAME es el nombre que utiliza cuando la entidad fue creada (es decir, el
nombre que se especifica en el comando ALLOC, descrito más adelante). El
campo url es la URL del archivo VRML que describe esta entidad. El propietario
de campo es el nombre de usuario de la IDENT que estaba en efecto cuando el
ALLOC de esta entidad se ha hecho. Tenga en cuenta que la respuesta se dé
por concluido con un punto (.) En una línea por sí mismo, lo que nos da la
flexibilidad, si decide regresar más información en las futuras versiones del
protocolo.

El avatar de campo es la palabra, ya sea verdadera o falsa la palabra (que por


defecto a true). Si es cierto, esta entidad es un avatar controlado por un ser
humano. Del mismo modo, la persistencia de campo es la palabra, ya sea
verdadera o falsa la palabra (que por defecto a false), indicando si esta entidad
seguirá siendo en el mundo después de su propietario ha desconectado.
Creation_time El campo es el mundo al tiempo que el comando ALLOC de la
entidad se llevó a cabo.

El apodo es el apodo del usuario, tal como se especifica con el apodo, o


"Invitado", si no se le dio apodo.

SSRC_values el campo es una lista de 32-bit los valores, expresados en


decimales y separados por espacios, que corresponden a los utilizados por
SSRCs los distintos tipos de datos para esta entidad. Sólo una se utiliza
actualmente: SSRC el valor de esta entidad de mensajes de texto, que también
nos referimos como el "texto de identificación".
El Comando getinfo

El getinfo comando se utiliza para recuperar más información especificada por


el usuario para una entidad. Se tiene el siguiente formato:

Getinfo entid

El registro responde con la información de los usuarios de la entidad, envió una


serie de líneas que terminan con una línea que contenga sólo un punto.
La lista de comandos

El comando LISTA produce una lista de identificadores de entidad (entids) para


las diversas entidades en el mundo. Su formato es muy simple:

LISTA
17

VRML 2.0 con Java CAPÍTULO 18

El registro responde con una lista de entids, uno por línea, terminando con una
línea que contenga sólo un punto.
El Comando LISTNEW

El LISTNEW comando es similar a la lista de comandos; la diferencia es que se


toma un parámetro que indica el número de milisegundos desde la última
solicitud se hizo. El registro sólo lista las entidades cuyos datos de registro ha
cambiado desde entonces. El formato es

LISTNEW millisecs

El Comando IDENT

IDENT el comando se utiliza para proporcionar un nombre de usuario y


contraseña que se utilizan en la creación y actualización de las entidades en el
Registro. Cada entidad es propiedad de un usuario en particular, y sólo el
usuario que es capaz de actualizar la entidad. El formato del comando es
IDENT

IDENT pasar nombre de usuario

El registro responde con

OKAY

REHUSÓ optional_reason_for_refusal

Típicos motivos de la denegación son "no usuarios" y "contraseña incorrecta".

Tenga en cuenta que varios IDENT comandos pueden ser emitidas en el mismo
período de sesiones, la más reciente está siempre en vigor.
El Comando ALLOC

El ALLOC comando se utiliza para asignar una entrada en el Registro. Se tarda


un CNAME como parámetro, este nombre debe ser único. Singularidad puede
basarse en el usuario la dirección de correo electrónico, por ejemplo, un
usuario llamado JimSmith@somewhere.com podría crear entidades con
CNAMES de JimSmith1@somewhere.com, JimSmith2@somewhere.com, y así
sucesivamente.

El formato del comando es ALLOC


18

VRML 2.0 con Java CAPÍTULO 18

ALLOC CNAME

El registro crea una entrada para la nueva entidad y se asigna una ID única
entidad para la misma. La respuesta al cliente es la entidad o el

REHUSÓ optional_reason_for_refusal

Típicos motivos de la denegación son "CNAME ya en uso" y "usuario no


identificado utilizando IDENT".
El Comando LIBERACIÓN

LIBERACIÓN El comando es el opuesto de ALLOC, en la medida en que elimina


un registro de la entidad. Sólo puede ser expedido por las entidades que son
propiedad del usuario, es decir, el nombre de usuario de la más reciente IDENT
comando debe coincidir con el titular de la entidad. El formato del comando es
LIBERACIÓN

LIBERACIÓN entid

La entidad con la entidad ID se elimina de la base de datos del registro, y el


servidor responde con

OKAY

REHUSÓ optional_reason_for_refusal

La razón más común para la denegación es que el usuario no posee la entidad.


La URL, NICKNAME, PERSISTENTES, AVATAR, y TextID Comandos

Estos cinco comandos se utilizan para actualizar la información para una


entidad. El usuario debe poseer la entidad en cuestión, al igual que para la
liberación de mando. El formato de estos comandos es la siguiente:

URL url entid


NICKNAME apodo entid
PERSISTENTES entid truefalse
AVATAR entid truefalse
TextID entid TextID

El truefalse campos son la verdadera palabra o la palabra falsa. El apodo de


campo se extiende hasta el final de la línea y puede contener espacios. En
todos los casos, el registro responde con
19

VRML 2.0 con Java CAPÍTULO 18

OKAY

REHUSÓ optional_reason_for_refusal

Las razones son las mismas que para la liberación de mando.


El Comando Chown

Chown El comando se utiliza para transferir la propiedad de una entidad a otro


usuario y puede ser emitida únicamente por el usuario que posee actualmente
la entidad. A partir de ese punto en adelante, se especifica el usuario es titular
de la recepción de la entidad, y el usuario que ha emitido la Chown comando
ya no puede actualizar o liberación de la entidad. El formato de la Chown es la
siguiente:

Chown entid usuario

La respuesta del servidor es el mismo que para la liberación de mando. Otro


motivo de denegación es que el nombre de usuario no es reconocido, lo que es
impedir que las entidades se conviertan en "huérfanos".
El Comando INFO

INFO El comando se utiliza para proporcionar información de los usuarios de


una entidad. El formato es el siguiente:

INFO entid

El servidor responde con

OKAY

REHUSÓ optional_reason_for_refusal

Si la respuesta es bien, el cliente puede enviar una serie de líneas de texto al


servidor que se almacenan como la información de los usuarios de esa entidad.
Después de la última línea del texto, el cliente debe enviar una línea que
contenga sólo un punto.
La API
20

VRML 2.0 con Java CAPÍTULO 18

Como se muestra en la Figura 18.3, hay una serie de interfaces de


programación de aplicaciones en el sistema. Autoría de la interfaz externa, lo
que permite la aplicación multiusuario para comunicarse con el navegador de
VRML, se ha descrito en detalle en el capítulo 5. Hay otras tres API: una entre el
applet multiusuario y de la red, entre el texto de un applet de chat y de la red,
y una charla entre el texto y el applet applet multiusuario. Estas API se
describen aquí. Todos ellos se implementan como conjuntos de clases Java.
La API de Multiusuario

El multiusuario se ejecuta utilizando la API de tres clases: Mundo, la entidad, y


LocalEntity. El mundo representa la clase de mundo virtual, y proporciona
métodos para el filtrado de la aplicación de la vista de ese mundo. La Entidad
de clase describe una entidad bajo el control remoto de algunos procesos.
LocalEntity la clase describe una entidad creada en la máquina local. En Vida
Mundos terminología, una entidad es un "zumbido", y un LocalEntity es un
"piloto".
El mundo de Clase

Un objeto de clase Mundial proporciona la principal conexión entre el applet y


los dos hosts (filtro y registro). A nivel de la API, la separación entre los dos
hosts se oculta. Cuando la aplicación crea un nuevo mundo de objetos, todas
las conexiones necesarias para el filtro de acogida y se creó el Registro, y un
número de hilos se inician con el fin de mantener la información de la copia
local de la entidad de base de datos hasta la fecha.

El constructor para la clase de mundo tiene un nombre y un número de puerto.


Debido a las restricciones de seguridad de Java descrito anteriormente, las
aplicaciones en ejecución dentro de los navegadores Netscape o similares sólo
podrán comunicarse con el host que el applet se carga desde.

El número de puerto se utiliza para dos fines. Es el puerto UDP en el filtro de


acogida a los que el filtro de control de los mensajes deben ser enviados, y
también es el puerto TCP en el registro de acogida. Esto es posible porque los
puertos UDP y TCP los puertos son independientes uno de otro.

El mundo de clase mantiene una base de datos de entidades locales, que es un


subconjunto del conjunto completo de las entidades mantienen en el servidor.
El acceso a la base de datos que es provista por el getEntities () método:

Enumeración World.getEntities ();

Los elementos de la enumeración debería ser devuelto emitidos para ser de la


clase de entidad. En efecto, esta llamada devuelve una enumeración de todas
las entidades locales en la copia de la base de datos.
21

VRML 2.0 con Java CAPÍTULO 18

No hay un método similar para el retorno de una enumeración de entidades


que se han creado en el cliente:

Enumeración getLocalEntities ();

Cada elemento de la enumeración es un objeto de clase LocalEntity.

También hay métodos para la búsqueda de una entidad dada su entidad o el ID


de su texto (es decir, SSRC texto):

Entidad getEntity (int entid);


Entidad getEntityByTextId (int TextID);

Este último método se utiliza principalmente para permitir que el applet de


chat de texto a la entidad que encontrar un mensaje de texto viene. ID es el
texto en el mensaje y será diferente de la entidad ID.

El mundo de clase proporciona algunos métodos de utilidad para acceder a la


información sobre la conexión con el entorno virtual compartido:

GetBanner cadena ();


largo getConnectedTime ();
int getPacketCount ();

El getBanner () devuelve el método pancarta mensaje de que el Registro de


acogida vuelve con su mensaje de bienvenida. El getConnectedTime () método
devuelve el número de milisegundos que han transcurrido desde que el cliente
conecta al servidor. El getPacketCount () método devuelve el número de
paquetes que se han recibido desde el cliente conectado al servidor.

El filtrado se puede configurar los parámetros utilizando una serie de métodos


de la clase:

setViewpoint vacío (Vec3 loc);


vacío setVisualAcuity (float acu);
vacío setVisualHorizonCount (int n);
vacío setVisibleRegions (int [] regiones);

El punto de vista es la ubicación en la que la cámara virtual se encuentra.


Tenga en cuenta que el usuario puede o no han creado un avatar, que puede
ser simplemente la observación del mundo. La clase Vec3 utilidad se utiliza
para almacenar la x, y, z y los valores de un punto en el espacio y se describe
más adelante en este capítulo.
22

VRML 2.0 con Java CAPÍTULO 18

También hay un método para la suspensión de unsuspending y el flujo de


cambios, y otro para solicitar la acogida de filtro para volver a enviar las
actualizaciones más recientes para cada entidad:

suspender vacío (boolean bandera);


vacío refresh ();

Hay métodos para obtener y definir el puerto en el que el cliente recibirá


mensajes de texto:

int getTextPort ();


vacío setTextPort (int puerto);

Y, por último, hay un método para establecer el nombre de usuario y


contraseña. Estos valores se utilizan en la creación de nuevas entidades en el
servidor, por lo que este método debe ser llamado antes de que un nuevo
LocalEntity se crea:

vacío de identidad (String usuario, String password);

Como puede ver, la clase de mundo es bastante mínima. Esto se debe a que
existe principalmente para mantener la base de datos de entidades, muchas
de las funcionalidades que usted puede ser que esperan encontrar aquí es
realmente en la clase de entidad.
La Entidad de clase

La clase de entidad mantiene toda la información para una entidad en el


entorno virtual. Habrá una Entidad de objeto para cada entidad para que las
actualizaciones se han recibido.

Cada entidad tiene una única entidad de 32 bits de identificación que se asigna
por el Registro. Este es el mismo que el valor SSRC en la propuesta de
actualización de mensajes, y que sirve de base para la identificación de las
entradas en la base de datos de la entidad. Un método es siempre para
encontrar el ID de una entidad:

int getId ();

Además de la entidad de identificación, la entidad también tiene un ID de


texto, también conocido como el texto SSRC (SSRC la utilizada en los mensajes
de texto enviados por la entidad). Tiene que haber alguna manera a esta
pregunta, y es la siguiente:
23

VRML 2.0 con Java CAPÍTULO 18

int getTextId ();

Además, el nombre canónico de la entidad se pueden recuperar:

Cadena getName ();

Este es el nombre que se utilizó cuando la entidad fue añadido a la entidad de


base de datos del Registro.

Como se ha descrito anteriormente, las entidades pueden entrar y salir


asincrónicamente. Ellos pueden moverse dentro y fuera de rango, pueden ser
oscurecidos por las características fijadas en el medio ambiente (es decir, que
puede entrar en un nonvisible región), y pueden salir de la simulación por
completo. Cada entidad tiene un estado que puede tener cualquier de los
siguientes valores:

• OKAY: la entidad está presente en la simulación


• STALE: no se han recibido las actualizaciones de la entidad por un tiempo
• abandono: la entidad ha dejado a la simulación
• MUERTOS: el cliente de applet ha eliminado la entidad

Cuando una entidad que primero llega a la simulación, su estado es OKAY. La


entidad se marca como acabó cuando su titular anuncia que ha salido de la
simulación. Si no se han recibido actualizaciones desde hace algún tiempo, la
entidad estatal de cambios a STALE. Hay varias razones por las que esto puede
ocurrir: la entidad puede tener vagaba fuera de rango y se quedó allí, tal vez
haya salido de la simulación, pero el mensaje que envió anunciando su
diappearance se perdió, o el cliente que crea que puede tener estrelló.

Una vez que el applet multiusuario avisos de que una entidad es STALE o
desaparecido, se puede eliminar de la entidad en tres dimensiones de la
representación de VRML mundo y, a continuación, establecer el estado de la
entidad a MUERTOS. En este punto, la API es libre de eliminar la entidad,
siempre es conveniente hacerlo.

El estado actual de una entidad puede ser consultado y se utilizan los


siguientes métodos:

int GetState ();


vacío setState (int estado);

Cuando una entidad que aparece por primera vez en la simulación, todos los
24

VRML 2.0 con Java CAPÍTULO 18

clientes que se sabe acerca de la actualización de la información de su


mensaje. Todos los demás datos, como el seudónimo, la dirección URL de
archivo VRML describirla, varios pabellones, etc, deben ser recuperados en el
registro de acogida. Un método se proporciona para averiguar si una entidad
todavía tiene que recuperar esta información:

boolean needsInfo ();

La ubicación de una entidad, la orientación, y otras propiedades pueden estar


cambiando constantemente. Hay métodos para obtener los valores actuales
para cada una de estas dinámicas cantidades:

Vec3 getLocation ();


Rotación getOrientation ();
largo getTimestamp ();
int getRegion ();
flotador getSize ();

Usted reconocerá como los campos de mensajes de actualización de la


propuesta descrita anteriormente en este capítulo. Los tres primeros métodos
de valores de retorno correspondiente a la "toLocation", "toOrientation" y
"toTime" campos de Vida Mundos. Tenga en cuenta que a pesar de una entidad
puede enviar varios mensajes de actualización de sí mismo, una para cada
región en la que, sólo una región es realmente disponibles a partir de una
entidad.

Aviso de nuevo el uso de la utilidad Vec3 clase. Observe también que tenemos
una clase de utilidad para las orientaciones, llamado de rotación. Estas clases
se describen más adelante en este capítulo.

También puede conocer el número de milisegundos que ha sido desde la última


vez que recibió una actualización de una entidad:

largo getLastUpdate ();

Cada entidad tiene también una bandera que indica si se ha actualizado a


partir de información de la red. La bandera es fijado por la API y se elimina por
la aplicación una vez que los datos se han extraído y utilizado para actualizar el
escenario VRML:

boolean hasChanged ();


vacío markUnchanged ();

Una entidad puede ser "silenciado" (lo que significa que los mensajes de texto
25

VRML 2.0 con Java CAPÍTULO 18

no debe ser recibido de ella) y / o "oculto" (lo que significa que no es visible).
Estas propiedades se pueden fijar y se preguntó a través de un conjunto de
métodos:

Ocultar vacío (boolean bandera);


boolean isHidden ();
Silencio vacío (boolean bandera);
boolean isMuted ();

La información del Registro de la entidad también se puede obtener, mediante


un conjunto de métodos para ello:

Cadena getURL ();


GetNickName cadena ();
largo getCreationTime ();
boolean isPersistent ();
boolean isAvatar ();
GetOwner cadena ();
String [] getinfo ();

También hay métodos para averiguar si la información ha cambiado de registro


y control para ver si una nueva URL ha sido fijado por la entidad:

boolean registryHasChanged ();


boolean hasNewURL ();

Una vez que el applet se ha acelerado la nueva URL u otros datos de los
registros, es necesario borrar la bandera que dice que ha cambiado los datos:

vacío markRegistryUnchanged ();


vacío unmarkNewURL ();

Por último, tenemos alguna manera de asociar a una entidad con una
representación visual en el mundo VRML. Cada entidad almacena una
referencia a un java.lang.Object (la clase base para todos los objetos en Java)
que hace referencia a los datos de VRML para la representación visual de la
entidad. Esta referencia se puede leer y escribir con los siguientes métodos:

Objeto getRepresentation ();


vacío setRepresentation (Objeto obj);

La representación se lee desde la dirección URL devuelta por el getURL ()


método. Una vez que el VRML se construyen estructuras de datos, una
referencia a ellos se asocian con la entidad mediante el setRepresenation ()
26

VRML 2.0 con Java CAPÍTULO 18

método. Tenga en cuenta que no hay nada acerca de esta API que es en
realidad VRML específicos, la representación puede ser cualquier cosa, y la
dirección URL se puede referir a cualquier tipo de datos.

Tenga en cuenta que aunque una entidad tiene un constructor, nunca es


llamado por la aplicación. Sólo son entidades creadas por la API, en respuesta a
los mensajes recibidos de la red.
La clase LocalEntity

Un LocalEntity es la "otra mitad" de una entidad. Una entidad es la


representación de una entidad en la base de datos, como se ha visto por la
observación del cliente. Un LocalEntity es el objeto que es "el control" de la
entidad.

A diferencia de una entidad, una LocalEntity se crea en el cliente local. El


constructor de una LocalEntity parecido a éste:

público LocalEntity (Mundo mundo, string name)

El nombre es el CNAME para la entidad, tal como se describe anteriormente en


este capítulo. La referencia al mundo es necesario, ya que una LocalEntity debe
ser capaz de comunicarse con el mundo con el fin de anunciar su existencia y
mantener al mundo informado de su estado siempre cambiante.

Algunos de los métodos de un LocalEntity corresponden a las de una entidad,


aunque, por supuesto, su funcionamiento interno es totalmente diferente ya
que la información no es procedente de la red.

ay maneras de obtener el ID y la entidad canónica nombre de una entidad:

int getId ();


Cadena getName ();

También hay métodos para obtener toda la información del Registro:

Cadena getURL ();


GetNickName cadena ();
String [] getinfo ();
boolean isAvatar ();
boolean isPersistent ();
int getTextId ();

Sin embargo, en este caso, también existen métodos para la fijación de la


27

VRML 2.0 con Java CAPÍTULO 18

información:

vacío setURL (String s);


vacío setNickName (String nombre);
vacío SetInfo (String [] inf);
vacío setAvatar (boolean bandera);
setPersistent vacío (por boolean);
vacío setTextId (int id);

Después de uno o más de estos seis métodos se llama, el método

vacío updateRegistry ();

debería llamarse en realidad a actualizar el registro a través de la red.

Por último, existen métodos para la fijación y la lectura de nuevo toda la


información que se envía en mensajes de actualización:

Vec3 getLocation ();


setLocation vacío (Vec3 loc);
Rotación getOrientation ();
vacío setOrientation (Rotación de origen);
largo getTimestamp ();
setTimestamp vacío (mucho tiempo);
int getRegion ();
vacío setRegion (int r);
flotador getSize ();
vacío setSize (float SIZ);

Después de cualquiera de estas dinámicas se han establecido las cantidades,


el método

vacío sendUpdate ();

debería llamarse en realidad a enviar una propuesta de actualización en


nombre de la entidad.
El API de Texto

Una API es siempre para el envío y recepción de mensajes de texto. Se


compone de tres clases: texto, TextSender, y TextReceiver.
El texto de clase

Un texto objeto corresponde muy de cerca a la baja a nivel de los mensajes


enviados a través del cable. El constructor de la clase toma una matriz de
28

VRML 2.0 con Java CAPÍTULO 18

bytes, que, generalmente, es obtenido a partir de un paquete de datos que


llegan:

Mensaje público (byte [] buffer);

La clase existe simplemente para ocultar la representación interna de los


mensajes enviados a través del cable. El texto prevé la clase de acceso a
métodos de obtener los valores de los distintos ámbitos:

int getTextId ();


corto GetType ();
Cadena de gettext ();

Tenga en cuenta que el TextID aquí es en realidad el SSRC de la RTP paquete


que contiene el mensaje en su campo de carga útil.
La clase TextReceiver

El TextReceiver clase es sólo una muy delgada capa en todo el bajo nivel de
formato de mensaje de texto. El constructor no tiene argumentos y
simplemente crea un socket para recibir mensajes de texto en:

público TextReceiver ();

La clase TextReceiver ofrece dos métodos:

int getPort ();


Texto getMessage ();

El getPort () método devuelve el número de puerto local en la que el texto del


receptor está a la escucha de las actualizaciones. El getMessage () devuelve un
método de texto de la toma de corriente. Bloquea-es decir, que no vuelve
hasta que llega un paquete, por lo general, el subprograma debería llamarlo
desde un hilo.
La clase TextSender

TextSender la clase se encarga de la tarea de formato de una cadena de texto


en un paquete de RTP y que se transmite. El constructor toma el nombre de
host del filtro de acogida y el puerto en el que acogen a los que los mensajes
de texto deben ser enviados:

público TextSender (String host, int puerto);

Hay métodos para establecer y obtener el ID de texto (es decir, el SSRC de los
paquetes RTP utiliza para enviar mensajes de texto):
29

VRML 2.0 con Java CAPÍTULO 18

int getTextId ();


vacío setTextId (int id);

Y, por supuesto, hay un método para enviar el texto:

vacío sendText (String texto);

No hay ningún método para fijar el tipo de mensaje, ya que la actual versión
del software siempre se fija el tipo de mensaje a cero.
El Cliente-a-cliente de la API

Habrá al menos dos aplicaciones de cliente en nuestro sistema, lo que nos


referimos como el multi-cliente y el cliente de texto. El multi-cliente es el
componente que se comunica con el filtro de acogida y el registro, mientras
que el texto se ocupa de los clientes de mensajería de texto. Puede haber otros
componentes que proporcionan apoyo a cosas tales como el streaming de
audio.

Debido a que el cliente multiusuario applet está en contacto con el servidor y


mantiene los objetos correspondientes al mundo y el avatar del usuario, tiene
que haber alguna manera para el resto de los applets para acceder a dicha
información. Existen dos métodos que el cliente multiusuario se espera poner a
disposición:

mundo público getWorld ();


público LocalEntity getAvatar ();

La primera simplemente devuelve una referencia al mundo, y la segunda


devuelve una referencia para el usuario del avatar. El applet de chat de texto,
por ejemplo, obtener una referencia para el mundo con el fin de establecer el
texto del puerto que se utilizará en el filtro de mensajes. Asimismo, obtener
una referencia para el avatar, a fin de establecer el texto del avatar ID (es
decir, su texto SSRC), que es generado por la aplicación de chat de texto.
La aplicación de la API

Ahora que hemos visto cómo la API aparecerán en el exterior, es el momento


de la apertura de las distintas clases y ver cómo funcionan. Tenga en cuenta
que esta aplicación interna se pueden cambiar fácilmente, manteniendo
intacta la propia API.
El mundo de Clase

La clase de mundo es la principal interfaz para el sistema multiusuario. Se


mantiene un registro de información básica acerca de las conexiones de red,
30

VRML 2.0 con Java CAPÍTULO 18

incluido el socket TCP para la comunicación con el Registro de acogida y de la


toma de datagrama moción en la que los mensajes de actualización que se
reciben. También se incluyen el momento en que la conexión a los servidores y
se estableció un recuento del número total de mensajes recibidos moción
actualización.

/ / Un mundo, tal y como aparece en el API de cliente final


/ / Escrito por Bernie Roehl, diciembre de 1996
paquete multianual;

importación java.util .*;


importación java.net .*;
importación java.io. *;

público de clase mundial (


Socket registry_socket; / / tcp zócalo de matrícula
DataInputStream registry_input / / flujo de entrada para ese zócalo
PrintStream registry_output / / flujo de salida para que la toma de
Cadena de bandera, / / de

DatagramSocket incoming_socket; / / utilizado para la recepción de


actualizaciones
int packets_received = 0; / / número total de paquetes recibidos

largo connected_time / / momento en que conectamos

Toda la información que se envió en cada uno de los filtros de mensajes


también se almacena en el mundo de objetos:

Vec3 ubicación Vec3 = new (); / / ubicación de nuestro punto de vista


flotador agudeza = 1.0F; / / tamaño de más de distancia
horizonte int = 0; / / número máximo de entidades visibles
int [] visRegions; / / lista de las regiones visibles
boolean suspendido = false; / / fiel a reprimir las actualizaciones
boolean request_refresh = false; / / queremos que todas las nuevas
actualizaciones
boolean ido = false; / / cliente abandona el mundo
int text_port = 0; / / puerto en el que la escucha del cliente para el texto

El mundo también almacena una simple "base de datos" de las entidades en la


simulación, en la forma de un vector de objetos de la entidad. Hay también un
vector de todos los creados a nivel local LocalEntity objetos:
31

VRML 2.0 con Java CAPÍTULO 18

Entidades vector = new Vector ();


Local_entities vector = new Vector ();

Y, por último, hay referencias a todos los hilos que el mundo crea objeto:

FilterSender filter_sender; / / filtro remitente hilo


UpdateReceiver update_receiver; / / actualización receptor hilo
Reaper Reaper / / segador hilo
RegistryUpdater registry_updater / / Registro actualizador hilo
ChangeMonitor change_monitor; / / cambio supervisar hilo

Todos estos hilos se explica un poco más tarde.

El mundo de clase ofrece una variedad de métodos de acceso que permiten la


aplicación para recuperar y modificar muchos de estos campos, sino que es
todo muy sencillo y no se enumeran aquí, en el fin de ahorrar espacio. El
código fuente completo para todos los software multiusuario, cliente y servidor,
se encuentra en el CD-ROM que viene con este libro.

Sin embargo, vale la pena señalar que los métodos que establecen los
parámetros de filtro que el filter_sender hilo que despertar y enviar un nuevo
mensaje para el filtro de acogida. Por ejemplo, aquí está lo que el setViewpoint
método tiene el siguiente aspecto:

público sincronizado setViewpoint vacío (Vec3 loc) (


location = loc;
sincronizadas (filter_sender) (filter_sender.notify ();)
)

Otros dos métodos son dignos de mención, ya que proporcionan acceso a las
"bases de datos" de las entidades locales y entidades:

Enumeración getEntities público () (


volver entities.elements ();
)

Enumeración getLocalEntities público () (


volver local_entities.elements ();
)

El constructor para la clase de mundo es muy pequeño:

mundo público (String host, int puerto)


lanza IOException, ConnectionRefusedException
32

VRML 2.0 con Java CAPÍTULO 18

(
/ / Conectar hasta registro de acogida
InetAddress = InetAddress.getByName anfitrión (host);
registry_socket = new Socket (host, puerto, true);
registry_input = new
DataInputStream (registry_socket.getInputStream ());
registry_output = new
PrintStream (registry_socket.getOutputStream (), true);
registry_output.println ( "hola");
Cadena de respuesta registry_input.readLine = ();
if (response.length ()> 7)
bandera = response.substring (8);
algo más
bandera = "";
if (response.startsWith ( "REHUSÓ"))
lanzar nuevos ConnectionRefusedException (bandera);
connected_time = System.currentTimeMillis ();
incoming_socket = new DatagramSocket ();
LocalEntity.setHost (host, puerto 2) / / puerto de datos
/ / Iniciar hilos
update_receiver = new UpdateReceiver (este);
filter_sender = new FilterSender (este, host, puerto);
segador = new Reaper (este);
registry_updater = new RegistryUpdater (este);
change_monitor = new ChangeMonitor (este);
)

El nombre de host se resuelve en una dirección IP, un socket TCP, se crea para
comunicarse con el Registro de acogida, y un par de arroyos se crean para
hablar con el Registro. HOLA el mensaje es enviado, la respuesta está marcada,
y, en caso necesario, se lanza una ConnectionRefusedException.

Si la conexión se ha establecido, la hora actual se registra de manera que el


tiempo de conexión más tarde puede ser calculado. La toma de datagrama que
recibe mensajes de actualización de movimiento se crea. El registro del rótulo
de acogida también es leído y almacenado.

Como se describe anteriormente en este capítulo, el puerto al que enviamos


nuestra propuesta de actualización de mensajes para las entidades locales es
el puerto para filtrar los mensajes que se envían, más 2.

El constructor termina por crear una serie de hilos para tratar diferentes
aspectos del sistema multiusuario. Estos hilos se describen por separado a
continuación. El hilo se pasa filter_sender la dirección y el puerto al que filtrar
33

VRML 2.0 con Java CAPÍTULO 18

los mensajes deben ser enviados.

El mundo también tiene una clase de finalizar () el método, que es llamado


automáticamente cuando un objeto se destruye mundo:

public void finalizar () (


/ / Dicen que el filtro de acogida para detener el envío de actualizaciones de
nosotros
ido = true;
sincronizadas (filter_sender) (filter_sender.notify ();)
/ / Cerrar los diferentes hilos
change_monitor.stop ();
registry_updater.stop ();
reaper.stop ();
/ / El registro de acogida decir que somos de aquí
sincronizadas (registry_socket) (
registry_output.println ( "Adiós");
try (registry_socket.close ();) catch (IOException e) ()
)
registry_socket.close ();
update_receiver.stop ();
filter_sender.stop ();
)

Este método fija el pasado las fuerzas de la bandera y el hilo filter_sender para
enviar un nuevo mensaje al filtro de acogida. A continuación, se detiene los
hilos que se han creado en el constructor, dice adiós a la secretaría de acogida,
y cierra la conexión.

Hay algunos otros métodos de la clase en el mundo. Hay una identidad () el


método, que identifica al usuario en el registro de acogida para que las nuevas
entidades locales se pueden crear.

public void identidad (String usuario, String password)


lanza IOException, BadIdentityException (
Cadena de respuesta;
sincronizadas (registry_socket) (
registry_output.println (
"IDENT" nombre de usuario "" contraseña);
try (respuesta = registry_input.readLine ();)
catch (IOException e) (
System.out.println (
"error al leer la respuesta a la identidad:" e);
lanzar nuevos BadIdentityException (
34

VRML 2.0 con Java CAPÍTULO 18

"mala respuesta a la identidad");


)
)
if (response.startsWith ( "OK"))
identified_to_registry = true;
algo más
lanzar nuevos BadIdentityException (
"no usuario o contraseña incorrecta");
)

Y, por último, existen métodos para añadir la búsqueda de entidades y ellos


sobre la base de su entidad o de texto, números de identificación ID:

Entidad pública addEntity (int entid) (


Entidad e = new Entidad (entid);
int i;
for (i = 0; i <entities.size (); i)
if (entities.elementAt (i) == null) (
entities.setElementAt (e, i);
retorno e;
)
entities.addElement (e);
retorno e;
)

Entidad pública getEntity (int entid) (


int i;
for (i = 0; i <entities.size (), i) (
Entidad e = (Entidad) entities.elementAt (i);
if (e.getId () == entid)
retorno e;
)
return null;
)

Entidad pública getEntityByTextId (int tssrc) (


int i;
for (i = 0; i <entities.size (), i) (
Entidad e = (Entidad) entities.elementAt (i);
if (e.getTextSSRC () == tssrc)
retorno e;
)
return null;
)
35

VRML 2.0 con Java CAPÍTULO 18

Esa es la clase de mundo, ahora echemos un vistazo a los hilos que se genera.

El Hilo FilterSender

Un objeto de la clase FilterSender se ejecuta como un hilo que periódicamente envía un


mensaje al filtro de acogida que le informe de los actuales parámetros de filtrado. Este
mensaje se envió periódicamente, incluso si los parámetros no han cambiado, ya que los
mensajes también sirven como "mantener alives" o "latidos".

/ / Hilo que envía periódicamente el filtro de mensajes


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.net .*;


importación java.io. *;

clase extiende FilterSender Hilo (


Mundo mundo;
InetAddress dirección;
int puerto;

FilterSender público (WRLD mundo, InetAddress addr, int prt) (


= WRLD mundo;
address = addr;
port = prt;
start ();
)

público sincronizado vacío ejecutar () (


DatagramSocket out_socket;
try (out_socket = new DatagramSocket ();)
catch (IOException e) (
System.out.println ( "No es posible crear el socket de datagrama");
retorno;
)
ByteArrayOutputStream combate ByteArrayOutputStream = new ();
DataOutputStream out = nuevo DataOutputStream (combate);
while (true) (
bout.reset ();
try (
corto banderas = 0;
if (world.suspended)
banderas | = 0x0001;
if (world.request_refresh) (
banderas | = 0x0002;
36

VRML 2.0 con Java CAPÍTULO 18

world.request_refresh = false;
)
if (world.gone)
banderas | = 0x0004;
out.writeShort (world.incoming_socket.getLocalPort ());
out.writeShort (banderas);
out.writeFloat (world.location.getX ());
out.writeFloat (world.location.getY ());
out.writeFloat (world.location.getZ ());
out.writeFloat (world.acuity);
out.writeInt (world.horizon);
out.writeShort (0); / / no las regiones
if (world.text_port == 0)
out.writeShort (0); / / sin puertos
else (
out.writeShort (1) / / un puerto adicional
out.writeShort (world.text_port); / / el texto del puerto
)
out.writeShort (0);
)
catch (IOException e) (
System.out.println (
"Error al edificio filtro mensaje:" + e);
retorno;
)
DatagramPacket paquete =
nuevo DatagramPacket (bout.toByteArray (), bout.size (),
dirección, puerto);
try (out_socket.send (paquete);)
catch (IOException e) (
System.out.println ( "No se pudo enviar paquetes:" + e);
retorno;
)
try (espera (5000);)
de capturas (InterruptedException e) ()
)
)
)

El constructor sólo mantiene una referencia al mundo, así como a la acogida y la dirección
del filtro de acogida. La carrera () crea un método de toma de datagrama, luego se sienta en
un bucle activamente la construcción de filtrar los mensajes y enviarlos. Duerme durante
cinco segundos (es decir, 5000 milisegundos) entre las actualizaciones, o que si menos de
notificar () es llamado cuando el hilo.
El Hilo UpdateReceiver

UpdateReceiver el hilo se crea para recibir los mensajes de actualización del filtro de
acogida.
37

VRML 2.0 con Java CAPÍTULO 18

/ / Hilo que recibe mensajes de actualización y las actualizaciones de las entidades


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.net .*;


importación java.io. *;
clase extiende UpdateReceiver Hilo (

Mundo mundo;

público UpdateReceiver (Mundo WRLD) (


= WRLD mundo;
start ();
)

public void run () (


byte [] buffer = new byte [1024];
DatagramPacket paquete = new DatagramPacket (
búfer, buffer.length);
while (true) (
try (world.incoming_socket.receive (paquete);)
catch (IOException e) (
System.out.println (
"Error al leer el mensaje de actualización:" + e);
retorno;
)
+ + world.packets_received;
UpdateMessage mensaje;
try (mensaje = new UpdateMessage (buffer);)
catch (IOException e) (
/ / Ignorar mensajes mal formados
continuar;
)
int entid = message.getEntityId ();
Entidad e = world.getEntity (entid);
if (e == null)
e = world.addEntity (entid);
e.update (mensaje);
)
)
)

El código es muy sencillo. La carrera () hace que el método de hilo para bloquear los
paquetes en espera. Cuando uno llega, que se convirtió en un nuevo UpdateMessage. La
entidad de identificación de la entidad que envía el mensaje se extrae, y la entidad se
observa en la base de datos local. Si no es encontrado, entonces no hemos escuchado de
38

VRML 2.0 con Java CAPÍTULO 18

esta entidad antes, por lo que añadirlo a la base de datos. Por último, dijo que la entidad de
que se actualice el mensaje.
El Hilo UpdateMessage

El UpdateMessage clase sólo se ocupa de analizar los detalles de un mensaje entrante


actualización:

/ / Un mensaje de actualización para el protocolo multiusuario


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.io. *;

clase UpdateMessage (
int seqnum;
int entid;
largo de tiempo;
Vec3 ubicación;
Orientación de la rotación;
int región;
flotador tamaño;
booleano pasado;

/ / Métodos de acceso:
público int getEntityId () (return entid;)
público Vec3 getLocation () (return location;)
Rotación getOrientation público () (return orientación;)
pública a largo getTimestamp () (return hora;)
público int getRegion () (return zona;)
flotador público getSize () (return tamaño;)
boolean isGone () (return pasado;)
int getSequenceNumber () (return seqnum;)

público UpdateMessage (byte [] buffer) throws IOException (


ByteArrayInputStream bin = new ByteArrayInputStream (buffer);
DataInputStream en = new DataInputStream (bin);
in.readByte (); / / ignore versión, el acolchado, la extensión, la CSRC contar
if (in.readByte ()! = 79) / / carga útil de tipo de marcador es cero
lanzar nuevos IOException ( "tipo de carga útil mal!");
int siguientes in.readShort = ();
if (siguientes <seqnum)
retorno;
= seqnum siguientes;
timestamp = in.readInt () <<16;
entid = in.readInt ();
location.read (en);
orientation.read (en);
39

VRML 2.0 con Java CAPÍTULO 18

región in.readInt = ();


size = in.readFloat ();
corto banderas = in.readShort ();
ido = ((banderas y 0x0001) == 0x0001)? verdadero: falso;
)

Después de una comprobación rápida para asegurarse de que el tipo de carga útil es
correcta, los campos son recogidos y almacenados. Tenga en cuenta que dejamos de lado
los paquetes que llegan fuera de secuencia.

El Hilo ChangeMonitor

El hilo ChangeMonitor periódicamente con los controles en el Registro de


acogida para ver si las nuevas entidades se han añadido o entidades de edad
se han cambiado.

/ / Hilo que busca nueva actualización entidades


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.io. *;
importación java.util .*;

clase extiende ChangeMonitor Hilo (

Mundo mundo;

static final int PERIODO = 10000 / / cada 10 segundos

público ChangeMonitor (Mundo WRLD) (


= WRLD mundo;
start ();
)

public void run () (


while (true) (
try (dormir (TIEMPO);)
de capturas (InterruptedException e2) ()
sincronizadas (world.registry_socket) (
world.registry_output.println ( "LISTNEW" + PERIOD);
40

VRML 2.0 con Java CAPÍTULO 18

while (true) (
Cadena de respuesta;
try (
respuesta = world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a NUEVA LISTA:"
+ Ex);
break;
)
if (response.startsWith ("."))
break;
int entid = Integer.parseInt (respuesta);
Entidad e = world.getEntity (entid);
if (e == null)
e = world.addEntity (entid);
algo más
e.needsAllInfo ();
)
)
)
)
)

Una vez más, nada demasiado sorprendente aquí. El hilo se despierta cada 10
segundos, y pide el registro de una lista de las entidades que han cambiado
durante ese período. Para cada entidad, se efectúe una prueba para ver si ya
existe, si no es así, que ha creado. En cualquier caso, la entidad se marca como
la necesidad de tener todas sus información (apodo, dirección URL, etc)
actualizado.
El Hilo RegistryUpdater

El RegistryUpdater hilo pasa a través de la lista de entidades de control para


ver si alguna de ellas necesitan ser actualizadas.

/ / Hilo que se actualiza en las entidades, según sea necesario


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.io. *;
importación java.util .*;
41

VRML 2.0 con Java CAPÍTULO 18

clase extiende RegistryUpdater Hilo (

Mundo mundo;

público RegistryUpdater (Mundo WRLD) (


= WRLD mundo;
setPriority (NORM_PRIORITY-1);
start ();
)

public void run () (


while (true) (
try (sleep (1);)
de capturas (InterruptedException ex) ()
for (int i = 0; i <world.entities.size (); + + i) (
Entidad e = (Entidad) world.entities.elementAt (i);
if (e! = null) (
if (e.needsInfo ()) (
sincronizadas (world.registry_socket) (
Cadena de respuesta;
StringTokenizer fichas;
world.registry_output.println (
"GETENTITY" e.getId + ());
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a GETENTITY:"
+ Ex);
retorno;
)
e.markRegistryChanged ();
tokens = new StringTokenizer (respuesta);
e.setName (tokens.nextToken ());
NewURL cadena = tokens.nextToken ();
if (e.getURL () == null
| |! NewURL.equals (e.getURL ())) (
e.setURL (newURL);
e.markNewURL ();
)
e.setURL (tokens.nextToken ());
e.setOwner (tokens.nextToken ());
e.setAvatar (tokens.nextToken ()
42

VRML 2.0 con Java CAPÍTULO 18

. iguales ( "true"));
e.setPersistent (tokens.nextToken ()
. iguales ( "true"));
e.setCreationTime (Long.parseLong (
tokens.nextToken ()));
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a GETENTITY:"
+ Ex);
retorno;
)
e.setNickName (respuesta);
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a GETENTITY:"
+ Ex);
retorno;
)
tokens = new StringTokenizer (respuesta);
e.setTextSSRC (Integer.parseInt (
tokens.nextToken ()));
do (
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a
GETENTITY: "
+ Ex);
retorno;
)
) While (! Response.equals ("."));
world.registry_output.println (
"Getinfo" e.getId + ());
String [] info = new String [20];
int ind = 0;
while (true) (
43

VRML 2.0 con Java CAPÍTULO 18

try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a
Getinfo: "
+ E);
break;
)
if (response.startsWith ("."))
break;
if (ind <info.length)
info [ind + +] = new
String (respuesta);
)
e.setInfo (información);
e.gotAllInfo ();
)
)
)
)
)
)
)

Este mensaje se ejecuta a través de los vectores de las entidades, en busca de


aquellos que han sido marcado como la necesidad de una actualización. Para
cada uno que hace, el comando GETENTITY la expedición, y la respuesta se
analiza y se utiliza para actualizar la entidad. Tenga en cuenta que nos marca
la entidad para mostrar que su registro de datos ha cambiado.
El Hilo Reaper

Si una entidad va fuera del alcance del cliente o en una región que
actualmente no es visible para el cliente, o si se deja por completo la
simulación, los datos de esa entidad debe ser removido de la copia local de la
base de datos:

/ / Hilo que pasa a través de entidades y elimina


/ / Que no hemos escuchado de los últimos
/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;
44

VRML 2.0 con Java CAPÍTULO 18

importación java.util .*;

clase extiende Reaper (Hilo

Mundo mundo;

público Reaper (Mundo WRLD) (


= WRLD mundo;
start ();
)

public void run () (


while (true) (
try (sueño (5000);) / / cinco segundos
de capturas (InterruptedException e) ()
for (int i = 0; i <world.entities.size (), i) (
Entidad e = (Entidad) world.entities.elementAt (i);
if (e! = null) (
if (e.getState () == Entity.DEAD)
world.entities.setElementAt (null, i);
algo más
e.age ();
)
)
)
)
)

Como se ha descrito anteriormente, la entidad puede estar en cualquiera de


varios estados. El estado terminal, si usted perdón el juego de palabras, es
Entity.DEAD. Las entidades que están en ese estado se retiran de la base de
datos por este "Grim Reaper" hilo. Este mensaje también causa las entidades a
"edad"; una vez que obtener la edad suficiente, que están marcados como
Entity.STALE. Cuando el applet toma nota de que la entidad es Entity.STALE,
como banderas que Entity.DEAD, y la entidad se limpia por fuera de este hilo.
La Entidad de clase

La Entidad de clase se utiliza para representar a las entidades en la simulación.


Cada entidad tiene alguna información básica asociada a ella:

/ / Definición de una entidad para el cliente de multiusuario


/ / Escrito por Bernie Roehl, diciembre de 1996
45

VRML 2.0 con Java CAPÍTULO 18

paquete multianual;

importación java.util .*;

Entidad pública clase (


int id; / / identificador de la entidad, también, para SSRC RTP principal período
de sesiones
int estado = OK; / / OKAY, STALE, muerto, ido
boolean needInfo = true; / / tenemos que actualizar nuestra información de
registro

boolean oculto = false; / / usuario no desea ver este avatar


silenciado boolean = false; / / usuario no desea escuchar este avatar

/ / Actualización de los mensajes:


boolean cambiado = false; / / true si ha cambiado recientemente
largo lastHeardFrom / / fecha y hora local de la última actualización
Vec3 ubicación; / / X, Y, Z
Orientación de la rotación; / / eje de rotación y el ángulo
largo de tiempo; / / hora para la ubicación y orientación
int región = 0; / / región que se encuentra en esta entidad
flotador size = 0; / / tamaño aproximado (por la agudeza)
int seqnum = 0; / / siguientes el número de la última actualización

/ / Registro de acogida:
CNAME cadena / / único nombre canónico (como por RTP)
Cadena url / / URL del archivo de descripción de la entidad
Cadena apodo / / apodo de la entidad
largo creationTime / / timestamp mundo de la creación de la entidad
boolean persistente = false; / / entidad persiste después propietario hojas
boolean avatar = false; / / entidad está dirigida por un ser humano
Cadena propietario / / nombre de usuario de la entidad titular
String [] info / / adicionales (al estilo SDES) Información de entidad
boolean registry_changed; / / establecer si los datos del registro ha cambiado

boolean has_new_url; / / nueva URL ha sido fijado


int text_SSRC / / SSRC de datos de texto

Objeto de representación; / / referencia al nodo de VRML

Obviamente, la entidad debe almacenar su ID de la entidad, así como su


estado actual, como se ha discutido anteriormente. Hay una bandera que
indica que es necesario la actualización, véase el debate anterior en este
capítulo de la Secretaría-y ChangeMonitor Updater hilos.
46

VRML 2.0 con Java CAPÍTULO 18

Hay banderas para indicar que el usuario quiere esta entidad a ser ocultado o
silenciado. Estas son utilizadas únicamente por el applet y no se envían a
través de la red, una discusión completa de ellos se encuentra más adelante en
este capítulo en la sección relativa a la aplicación cliente.

La información de la más reciente propuesta de actualización de mensajes se


almacena en la Entidad, junto con una bandera que indica que los datos ha
cambiado. Esta bandera se fija por el software multiusuario y autorizado por el
applet. El momento en que la actualización más reciente se llegó también
almacenados.

Toda la información acerca de la entidad que la obtenida del registro de


acogida también se mantienen aquí, junto con una bandera que indica que se
ha cambiado y una bandera que indica que la URL es diferente de lo que era
antes. Esto es importante, ya que significa que el applet se tiene que
reemplazar el VRML que corresponde a la entidad.

También mantendrá en torno a un objeto de referencia. Esto suele ser una


referencia a la representación visual del objeto, en el caso de la interfaz
externa de VRML, este campo que hace referencia a un nodo de la carga desde
el archivo indicado en la URL.

Hay una serie de métodos de acceso, que se omiten aquí por brevedad. El
constructor establece el tiempo de la última actualización a la hora actual e
inicializa la ubicación y orientación de la entidad:

Entidad (int entid) (


id = entid;
lastHeardFrom = System.currentTimeMillis ();
ubicación = new Vec3 (0, 0, 0);
= nueva orientación de rotación (0, 1, 0, 0);
)

El otro método de una entidad es la que tiene un UpdateMessage y utiliza los


datos que contiene para actualizar la Entidad:

público nula actualización sincronizada (UpdateMessage actualización) (


if (update.getEntityId ()! = id)
/ / Id desajuste; ignorarlo
retorno;
if (update.getSequenceNumber () <seqnum)
/ / Paquete es fuera de la secuencia; ignorarlo
retorno;
47

VRML 2.0 con Java CAPÍTULO 18

cambiado = true;
location = update.getLocation ();
orientación update.getOrientation = ();
timestamp = update.getTimestamp ();
región update.getRegion = ();
size = update.getSize ();
if (update.isGone ())
= estado han ido;
algo más
estado = OK;
lastHeardFrom = System.currentTimeMillis ();
)

La edad () el método se ha mencionado anteriormente en este capítulo. Lo


único que hace es comprobar cuánto tiempo ha sido desde la última vez que
escuchó a partir de esta entidad, y, si es que ha sido durante más de 10
segundos, la bandera de la entidad como Entity.STALE.

público sincronizado vacío edad () (


if ((lastHeardFrom - System.currentTimeMillis ())> 10000)
estado = STALE; / / 10 segundos desde la última actualización
)

Eso es todo para la clase de entidad, ahora echemos un vistazo en su


equivalente local.

La clase LocalEntity

El LocalEntity clase representa entidades que se han creado en la máquina local. Estas
entidades son responsables de enviar las actualizaciones de movimiento por sí mismos para
el filtro de acogida. La dirección IP y el número de puerto que estas actualizaciones
deberán enviarse a la se almacenan como variables de clase, y hay una clase de método de
fijación de ellos. También hay una toma de datagrama que se utiliza para enviar las
actualizaciones, junto con un ByteArrayOutputStream y un DataOutputStream que se
utilizan para la construcción del mensaje:

/ / Una entidad local


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.io. *;
importación java.net .*;
48

VRML 2.0 con Java CAPÍTULO 18

clase pública LocalEntity (

InetAddress dirección estática;


static int puerto;

estática ByteArrayOutputStream combate = new ByteArrayOutputStream ();


DataOutputStream dout estático = new DataOutputStream (combate);
estática DatagramSocket zócalo;

static void setHost (InetAddress addr, int prt)


lanza SocketException (
address = addr;
port = prt;
socket = DatagramSocket nuevo ();
)

Por ejemplo, la información es similar a la que se encuentra en una Entidad:

int id;
Cadena CNAME;
Url cadena;
String [] info;
boolean is_avatar = true;
boolean is_persistent = false;
int text_id;
Cadena apodo;

int seqnum = 0;
Vec3 ubicación Vec3 = new ();
Nueva orientación de la rotación Rotación = ();
largo timestamp = System.currentTimeMillis ();
int región = 0;
size = 2 septies flotador;
boolean ido = false;

Hay de nuevo una serie de métodos de acceso, que no vamos a enumerar aquí para ahorrar
espacio.

El constructor solicita el registro para asignar una ID de la entidad especificada CNAME y


lanza una excepción si es rechazada. Si todo está bien, entonces la nueva entidad se añade a
la lucha de las entidades locales que se mantienen en el mundo:

LocalEntity público (WRLD mundo, string name)


lanza PermissionDeniedException (
= WRLD mundo;
CNAME = new String (nombre);
sincronizadas (world.registry_socket) (
Cadena de respuesta;
49

VRML 2.0 con Java CAPÍTULO 18

world.registry_output.println ( "ALLOC" CNAME +);


try (respuesta = world.registry_input.readLine ();)
catch (IOException ex) (
lanzar nuevos PermissionDeniedException ( "mala respuesta");
)
if (response.startsWith ( "REHUSÓ"))
lanzar nuevos
PermissionDeniedException (response.substring (8));
id = Integer.parseInt (respuesta);
)
world.local_entities.addElement (este);
)

El finalizar () sólo le indica al método de registro para deshacerse de la entidad local


(suponiendo que no se marcará como persistente):

public void finalizar () (


ido = true;
try (sendUpdate ();) catch (IOException ex) ()
if (is_persistent == false) (
sincronizadas (world.registry_socket) (
world.registry_output.println ( "LIBERACIÓN" + id);
try (world.registry_input.readLine ();)
catch (IOException ex) ()
)
)
)

El sendUpdate () el método se basa el paquete y lo envía al filtro de acogida. También se


ocupa de incrementar el número de secuencia para esta entidad:

público sincronizado vacío sendUpdate () throws IOException (


sincronizadas (combate) (
bout.reset ();
/ / Version = 2, relleno = 0, la extensión = 0, CSRC count = 0:
dout.writeByte (0x80);
dout.writeByte (79) / / = 0 marca, tipo de carga útil = 79
dout.writeShort (seqnum);
dout.writeInt ((int) (fecha y hora>> 16));
dout.writeInt (id);
dout.writeFloat (location.getX ());
dout.writeFloat (location.getY ());
dout.writeFloat (location.getZ ());
dout.writeFloat (orientation.getX ());
dout.writeFloat (orientation.getY ());
dout.writeFloat (orientation.getZ ());
dout.writeFloat (orientation.getAngle ());
dout.writeInt (región);
50

VRML 2.0 con Java CAPÍTULO 18

dout.writeFloat (tamaño);
dout.writeShort (ido? 0x0001: 0x0000); / / banderas
DatagramPacket paquete = new
DatagramPacket (bout.toByteArray (),
bout.size (), la dirección, puerto);
+ + seqnum;
sincronizada (sockets) (
socket.send (paquete);
)
)
)

El último método LocalEntity en la clase se encarga de la actualización del registro con la


información más reciente acerca de la entidad. Debe ser llamado después de la aplicación
de cualquier modificación del registro de información relacionada con:

público sincronizado updateRegistry vacío ()


lanza IOException, PermissionDeniedException (
sincronizadas (world.registry_socket) (
world.registry_output.println ( "URL"
+ Id + ""
+ ((Url == null)? "No_url": url));
validate_response ();
world.registry_output.println ( "persistente"
+ Id + "" + is_persistent);
validate_response ();
world.registry_output.println ( "Avatar"
+ Id + "" + is_avatar);
validate_response ();
world.registry_output.println ( "TextID" + id + "" + text_id);
validate_response ();
world.registry_output.println ( "apodo"
+ Id + ""
+ ((Alias == null)? "Invitado": apodo));
validate_response ();
if (info! = null) (
world.registry_output.println ( "INFO" + id);
validate_response ();
for (int i = 0; i <info.length; + + i)
if (info [i]! = null)
world.registry_output.println (info [i]);
world.registry_output.println (".");
)
)
)
51

VRML 2.0 con Java CAPÍTULO 18

Todos los campos se actualizan cada vez que alguno de ellos los cambios. Este
no es el diseño más eficiente, pero el ancho de banda disponible en el
"ascendente" de lado (desde el cliente en el registro) es abundante en relación
con la "bajada" hacia el tráfico, así que no es demasiado malo.

El método anterior llama al método privado validate_response () para


comprobar si había algún problema con la actualización del Registro:

private void validate_response ()


lanza IOException, PermissionDeniedException (
Cadena de respuesta world.registry_input.readLine = ();
Cadena msg = "";
if (response.length ()> 7)
msg = response.substring (8);
if (response.startsWith ( "REHUSÓ"))
lanzar nuevos PermissionDeniedException (msg);
)

Eso es todo lo que hay que LocalEntity la clase. Como la mayoría de los demás,
es muy simple.
La clase TextSender

TextSender la clase se encarga de enviar mensajes de texto para el texto del


cliente:

/ / Un objeto que envía mensajes de texto


/ / Escrito por Bernie Roehl, enero de 1997

paquete multianual;

importación java.net .*;


importación java.io. *;

clase pública TextSender (

InetAddress de acogida;
int puerto;
int text_ssrc;
int seqnum = 0;

DatagramSocket zócalo;
DatagramPacket paquete;
52

VRML 2.0 con Java CAPÍTULO 18

ByteArrayOutputStream combate;
DataOutputStream dout;

público int getTextId sincronizado () (return text_ssrc;)


público sincronizado vacío setTextId (int id) (text_ssrc = id;)

público TextSender (String hostname, int prt) lanza SocketException (


try (= InetAddress.getByName anfitrión (host);)
de capturas (UnknownHostException ex) (
System.out.println ( "Host desconocido \" "hostname" \ "");
retorno;
)
port = prt;
combate ByteArrayOutputStream = new ();
dout = new DataOutputStream (combate);
socket = DatagramSocket nuevo ();
)

público sincronizado vacío sendText (String texto) throws IOException (


int msglen = text.length ();
byte [] mensaje = new byte [msglen];
text.getBytes (0, msglen, mensaje, 0);
bout.reset ();
/ / Version = 2, relleno = 0, la extensión = 0, CSRC count = 0
dout.writeByte (0x80);
dout.writeByte (80) / / = 0 marca, tipo de carga útil = 80
dout.writeShort (seqnum); / / número de secuencia
dout.writeInt (0); / / hora
dout.writeInt (text_ssrc); / / entid
dout.writeShort (0); / / Tipo de
dout.writeShort ((corto) msglen);
dout.write (mensaje, 0, msglen);
dout.flush ();
paquete = new DatagramPacket (bout.toByteArray (),
bout.size (), host, puerto);
socket.send (paquete);
)

El constructor se resuelve el nombre de host y crea un datagrama zócalo, junto


con una base de datos de salida para la construcción de los paquetes.

El sendText () el método se basa y envía el paquete. Tenga en cuenta que el


53

VRML 2.0 con Java CAPÍTULO 18

TextSender no se ejecuta en su propio hilo, y sobre el envío de bloques de


texto, por lo que debería ser llamado dentro de un hilo. Aviso de que se trata
de un método sincronizado, lo que asegura que sólo una instancia de la clase
utiliza la clase de las variables compartidas en un momento dado, y que
ninguna de las variables de instancia, mientras que el cambio sendText () es la
composición de su paquete.
La clase TextReceiver

El TextReceiver clase se utiliza para recibir los mensajes de texto. El constructor


construye un paquete de amortiguación y un datagrama zócalo. El zócalo del
puerto se puede obtener utilizando el getPort () método:

/ / Objeto que recibe mensajes de texto


/ / Escrito por Bernie Roehl, de enero de 1997 (mi primer código de'97!)

paquete multianual;

importación java.net .*;


importación java.io. *;

clase pública TextReceiver (

DatagramSocket zócalo;
DatagramPacket paquete;
byte [] buffer;

público TextReceiver () (lanza SocketException


socket = DatagramSocket nuevo ();
= new byte buffer [1024];
paquete = new DatagramPacket (buffer, buffer.length);
)

público int getPort () (return socket.getLocalPort ();)

público texto sincronizado getMessage () throws IOException (


socket.receive (paquete);
regresar nuevo texto (buffer);
)

El getMessage () lee un método de paquetes de mensajes de texto desde el


zócalo, el bloqueo hasta que está disponible. A continuación, construye un
texto desde el paquete y lo devuelve.
54

VRML 2.0 con Java CAPÍTULO 18

El texto de clase

El texto sólo tiene una clase de paquetes de datos y lo analiza. Métodos de


acceso puede ser utilizado para leer el tipo de mensaje, el texto de
identificación, y el propio texto:

/ / Un mensaje de texto en el sistema multiusuario


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.io. *;

clase pública texto (


de tipo corto; / / siempre cero, por ahora.
int ssrc / / sincronización de la fuente
Cadena de mensaje, / / el propio mensaje

pública a corto GetType () (return tipo;)


público int getTextId () (return ssrc;)
public String gettext () (return mensajes;)

Mensaje público (byte [] buffer) throws IOException (


ByteArrayInputStream bin = new ByteArrayInputStream (buffer);
DataInputStream en = new DataInputStream (bin);
in.readByte (); / / versión, el acolchado, la extensión, la CSRC contar
if (in.readByte ()! = 80) / / Tipo de carga útil, marcador es cero
lanzar nuevos IOException ( "mal de carga útil de tipo texto");
in.readShort (); / / número de secuencia
in.readInt (); / / hora
ssrc in.readInt = (); / / SSRC texto
type = in.readShort ();
int len = in.readShort ();
byte [] textbuffer = new byte [len];
in.readFully (textbuffer);
mensaje = new String (textbuffer, 0, 0, len);
)

El tipo de campo es siempre cero, pero esto no es marcada. El tipo de carga útil
se verifica, sin embargo.
La Utilidad de las clases
55

VRML 2.0 con Java CAPÍTULO 18

Hay un par de clases pequeñas que proporcionan apoyo a los vectores en tres
dimensiones (similar a un SFVec3f VRML) y del eje de rotación de ángulo
(similar a un SFRotation VRML). El Vec3f clase tiene este aspecto:

/ / Una simple clase de vectores 3D


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;

importación java.io. *;

clase pública Vec3 (


protegidas flotar x = 0, y = 0, z = 0;

público Vec3 () ()

público Vec3 (float xval, yval flotador, flotador zval) (


x = xval;
y = yval;
z = zval;
)

flotador público getX () (return x;)


flotador público Gety () (return y;)
flotador público Getz () (return z;)

public void leer (en DataInputStream) throws IOException (


x = in.readFloat ();
y = in.readFloat ();
z = in.readFloat ();
)

public String toString () (return x "" y "" z;)

La rotación y la clase tiene este aspecto:

/ / Una simple rotación de clase


/ / Escrito por Bernie Roehl, diciembre de 1996

paquete multianual;
56

VRML 2.0 con Java CAPÍTULO 18

importación java.io. *;

clase pública Rotación (


protegidas flotar x = 0, y = 0, z = 1, a = 0;

flotador público getX () (return x;)


flotador público Gety () (return y;)
flotador público Getz () (return z;)
flotador público getAngle () (return a;)

público de rotación () (x = y = a = 0, z = 1;)

público de rotación (xv flotador, flotador YV, ZV flotador, flotador av) (


xv = x;
y = YV;
z = ZV;
a = av;
)

public void leer (en DataInputStream) throws IOException (


x = in.readFloat ();
y = in.readFloat ();
z = in.readFloat ();
a = in.readFloat ();
)

public String toString () (return x "" y "" z "" a;)

Clases de excepción

Hay también, por supuesto, una serie de excepciones que se arrojaron bajo
diversas circunstancias:

• BadIdentityException: La entidad ID no existe.


• ConnectionRefusedException: El servidor se negó a aceptar este cliente.
• PermissionDeniedException: Se hizo un intento de modificar o eliminar
una entidad que el usuario no posee.

Del manejo de excepciones de Java simplifica enormemente el código de


cliente.
El diseño de los clientes
57

VRML 2.0 con Java CAPÍTULO 18

En general, la interfaz que el usuario considera que se divide en tres partes: el


navegador de VRML, el cliente multi-applet, y el texto de chat applet. El
navegador de VRML la interfaz de usuario no es nuestra preocupación y variará
de un navegador de VRML a otro. Vamos a centrar nuestra atención en el
diseño del panel de control para la aplicación multiusuario cliente y el cliente
de applet texto.
El Multiusuario Cliente Applet

El diseño para el cliente de applet es bastante complejo. Hay una gran


cantidad de información para el usuario de acceso, a fin de diseñar una interfaz
de usuario que es eficaz y fácil de usar representa un reto significativo. La
información que se presenta al usuario, y las funciones que queremos poner a
su disposición, se organizará en seis categorías:

• Red de información (host, puerto, tiempo de conexión, etc)


• El usuario de información (lista de usuarios, firmado el)
• Avatar de selección (la elección de un cuerpo)
• Perfil de la información (establecimiento de alias, dirección de correo
electrónico, y así sucesivamente)
• Filtrado de control
• Información General del Programa

Estas seis categorías serán colocados en seis cartas en un awt.CardLayout


diseño, con un conjunto de seis botones para seleccionar qué tarjeta esté
activa.

También vamos a añadir una barra de estado que permanece en la pantalla, sin
importar que el panel se muestra. Esto será usado para mostrar información
crítica para el usuario.

El diseño resultante se muestra en la Figura 18.11. Cada una de las seis


tarjetas realiza un conjunto específico de funciones, y el usuario puede cambiar
entre las tarjetas haciendo clic en los botones de radio a un lado.

Figura 18.11 La presentación de los applet del panel de control multiusuario


Información de la Tarjeta

Información de la tarjeta se muestra cuando el usuario se firmó el primero y


puede ser re-mostradas en cualquier momento haciendo clic en el botón de
58

VRML 2.0 con Java CAPÍTULO 18

Información. La presentación de la tarjeta de información se muestra en la


Figura 18.12. No hay nada para el usuario para establecer el grupo es
puramente informativo, como un "Acerca de" cuadro de diálogo.

Figura 18.12 La información de tarjeta


La Tarjeta de Personas

La población presenta una tarjeta de la lista de usuarios conectados


actualmente. Cualquier entidad que se marca como un avatar que se considera
un "usuario". El diseño de la tarjeta de la gente se muestra en la Figura 18.13.

Figura 18.13 El Pueblo tarjeta

El usuario se presenta con tres botones y un cuadro de lista de desplazamiento


que muestra los apodos de los usuarios. El usuario puede elegir a cualquier
persona haciendo clic en su nombre en el cuadro de lista.

Al hacer clic en Ocultar, se activará el "oculto" de la condición de usuario.


Oculta los usuarios no serán visibles en 3-D de la ventana de la vista y están
marcados con una H en la lista de usuarios. Del mismo modo, el usuario puede
hacer clic en Mute para alternar el "silenciado" de el usuario especificado.
Silenciado los usuarios no tengan su texto que se muestra en la ventana de
chat y tendrá una M al lado de su nombre en el cuadro de lista.

Al hacer clic en Consulta mostrará un cuadro de diálogo que contenga toda la


información disponible sobre el usuario, incluyendo toda la información que
hayas elegido para configurar por sí mismos.
El filtro de la tarjeta

El filtro de la tarjeta permite al usuario ajustar los parámetros de filtrado


directamente. La presentación de la tarjeta del filtro se muestra en la Figura
18.14. El usuario puede ajustar dos deslizadores, uno para su agudeza visual y
la otra para contar su horizonte. También pueden decirle al filtro de acogida
59

VRML 2.0 con Java CAPÍTULO 18

para suspender todas las futuras actualizaciones de control de la casilla de


verificación Suspender.

Figura 18.14 La tarjeta Filtro


Avatar de la Tarjeta

Avatar de la tarjeta permite al usuario seleccionar un nuevo órgano. Una lista


de los avatares de desplazamiento se presenta junto con un botón Seleccionar,
como se muestra en la Figura 18.15. El usuario puede hacer clic en cualquiera
de los avatares que figuran, a continuación, haga clic en Seleccionar para
hacer que el avatar de su actual cuerpo.

Figura 18.15 El Avatar tarjeta

La lista de avatares se lee a partir de un conjunto de parámetros dentro de la


<applet> ... </ APPLET> etiquetas en el archivo HTML que carga la aplicación
multiusuario. Cada avatar tiene dos partes: el nombre que se muestra en la
lista, y la URL del archivo VRML describiendo el avatar. Dos parámetros se
utilizan para cada avatar: la avatarDesc y la avatarUrl. El primer avatar
avatarDesc0 uso y avatarUrl0, el próximo utilizará avatarDesc1 y avatarUrl0, y
así sucesivamente, como se muestra a continuación:

<param name=avatarDesc0 value="Bearded man">


<param name=avatarUrl0 value=http://someplace.com/bearded.wrl>
<param name=avatarDesc1 value="Kid con lollipop">
<param name=avatarUrl1 value=http://elsewhere.com/lollipop.wrl>
<param name=avatarDesc2 value="Punk rocker">
<param name=avatarUrl2 value=http://thirdplace.com/punker.wrl>

Tenga en cuenta que estos ejemplos son sólo eso: ejemplos. Ninguno de los
URL corresponderá a los archivos.
La tarjeta de red
60

VRML 2.0 con Java CAPÍTULO 18

La tarjeta de red muestra el nombre de host, filtro de direcciones de los


puertos, tiempo de conexión, el número de paquetes recibidos, y el registro del
rótulo de mensaje de acogida (si procede). Esta tarjeta se muestra en la Figura
18.16. La tarjeta es estrictamente informativa, y ninguno de los campos
pueden ser modificados por el usuario.

Figura 18.16 La Tarjeta de red

El perfil de la tarjeta

La última tarjeta permite al usuario introducir su alias, dirección de correo


electrónico, URL de la página web, y cualquier otra información personal que
desee proporcionar. Se muestra en la Figura 18.17. La información se actualiza
de inmediato, pero puede tardar varios segundos para propagar a todos los
clientes conectados.

Figura 18.17 El perfil de la tarjeta


El cuadro de diálogo Iniciar sesión

Además de las diversas tarjetas, hay un cuadro de diálogo que aparece cuando
el usuario lanza la primera aplicación multiusuario. La caja se muestra en la
Figura 18.18. El usuario que se espera de entrar en su nombre de usuario y
contraseña, luego pulsa el botón Conectar. La contraseña no se muestra como
se escribió.

Figura 18.18 El cuadro de diálogo Iniciar sesión


Chat de texto Cliente Applet
61

VRML 2.0 con Java CAPÍTULO 18

El cliente de chat applet texto es muy simple. Se trata de una zona de salida
(que puede ser desplazado si es necesario) y un área de entrada. Ellos se
muestran en la Figura 18.19. Cada usuario de texto va precedido de su apodo
si ha configurado una, o por su ID de texto si no lo han hecho.

Figura 18.19 El applet de chat de texto


La aplicación del Cliente

Ahora que hemos establecido lo que el usuario ve, es el momento de iniciar la


codificación del cliente. Vamos a examinar la aplicación multi-cliente, el applet
de chat de texto, y la comunicación entre ellos, la interfaz para el navegador
de VRML, y cómo encaja todo junto en una sola página Web.
El Cliente Multiusuario

El multi-cliente de applet es bastante compleja otra pieza de código. Se ha de


proporcionar la interfaz de usuario se describe anteriormente, así como la
interfaz con el navegador VRML y añadido de la API de multiusuario. Vamos a
bucear en derecho:

/ / El cliente de applet Multiusuario


/ / Escrito por Bernie Roehl, enero de 1997

importación java.applet .*;


importación java.util .*;
importación java.awt .*;
importación java.io. *;
netscape.javascript.JSObject de importación;
vrml.external.Browser de importación;
vrml.external.Node de importación;
importación vrml.external.field .*;
importación vrml.external.exception .*;
de importación de múltiples .*;

clase pública MultiUserClient


extiende Applet
implementa ejecutable, EventOutObserver (

MyThread hilo;
62

VRML 2.0 con Java CAPÍTULO 18

boolean corriendo = true;

public static final Cadena version = "0.1";

Mundo Mundo = null; / / el mundo en el que está conectado a

LocalEntity myEntity; / / nuestro avatar

Cadena de nombre de usuario = "";


String password = "";
Cadena hostname;
int puerto;

Cadena bandera = "none";

String [] info = new String [10] / / información pública

Además de la ampliación de la clase Applet, el MultiUserClient implementa el


ejecutable desde la interfaz que se ejecuta como un hilo separado. También
pone en práctica la interfaz EventOutObserver que se define en la interfaz
externa de VRML.

Una variable no pierde de vista el hilo que está ejecutando, y hay una
referencia al mundo que está conectado a LocalEntity y el correspondiente al
usuario del avatar. Hay otras variables de instancia para el nombre del host y el
puerto, así como el nombre de usuario, contraseña y la información pública.

También hay una serie de variables que se utilizan para la interfaz con el
navegador de VRML, pero será un poco más adelante se describen en este
capítulo.

Hay varios elementos de la interfaz de usuario que deben ser definidas. Existe
la tarjeta del panel, así como una serie de controles que se colocan sobre las
diversas tarjetas de crédito:

Grupo cardPanel; / / multi-panel de la página


/ / Algunos de los controles
HideButton botón, botón muteButton, queryButton;
Botón selectButton;
Casilla de verificación infoCheckbox, peopleCheckbox, filterCheckbox;
Casilla de verificación avatarCheckbox, networkCheckbox, profileCheckbox;
SuspendCheckbox casilla de verificación;
Lista avatarList;
63

VRML 2.0 con Java CAPÍTULO 18

Lista peopleList;
static final int SCROLLMAX = 50;
Scrollbar visualAcuitySlider, visualHorizonSlider;
NicknameField de texto, nameField, emailField, webpageField;
GeographyField de texto, phoneField, faxField;
PacketsField de texto, connectedField, bannerField, statusField;

Todos los botones, casillas de verificación, las listas de desplazamiento,


deslizadores, y los campos de entrada de texto que hemos visto en nuestros
debates de diseño de la interfaz de usuario se definen aquí. También están los
botones de radio (un conjunto de seis casillas de verificación) que selecciona a
los paneles, así como la producción de sólo algunos campos (como el
statusField, bannerField, y connectedField).

También se definen dos vectores: uno para las URL de los avatares que
tenemos a nuestra disposición, y otra para la lista de personas que se firmó en:

Avatares vector = new Vector (); / / URL de avatares


Personas vector = new Vector (); / / entidades en el mundo

Vamos a proporcionar un getAppletInfo () método, así como un


getParameterInfo () método. Estas aplicaciones permitirán a los demás (o el
navegador) para averiguar acerca de nuestra aplicación:

getAppletInfo public String () (


retorno "multi-usuario de clientes" + versión
+ ", Escrito por Bernie Roehl";
)

public String [] [] getParameterInfo () (


String [] [] info = (
( "Anfitrión", "cadena",
"el nombre o la dirección IP de la filtración de acogida"),
( "Puerto", "entero",
"La propiedad intelectual puerto de filtrado de acogida")
);
información de retorno;
)

Comunicación Inter-Applet

Ofrecemos dos métodos para la aplicación de chat de texto (o cualquier otra


aplicación, para el caso) a utilizar para obtener una referencia a la carga o el
64

VRML 2.0 con Java CAPÍTULO 18

mundo a nuestro avatar:

mundo público getWorld () (return mundo;)


público LocalEntity getAvatar () (return myEntity;)

Init () Método

El inicializador MultiUserClient para la clase se encarga de hacer una buena


cantidad de configuración, en su mayoría de la interfaz de usuario. Las cosas
empiezan simplemente suficiente:

public void init () (


/ / Obtener los parámetros del applet
hostname = new String (getParameter ( "host"));
port = Integer.parseInt (getParameter ( "puerto"));
if (host == null)
hostname = getDocumentBase (). getHost ();

El nombre de host y el puerto será leído del <param> etiquetas en el


documento HTML. Si no se especifica el nombre de host, el equipo desde el que
se cargó el documento se utiliza. Esto es útil, ya que significa que todo el
mundo puede ser trasladado a un host diferente, simplemente copiando los
archivos.

El siguiente paso es la creación de los diversos controles. Esto es tedioso, pero sencillo:

/ / Crear los controles

= connectedField nuevo texto (6) / / tiempo de conexión


connectedField.setEditable (false); / / mostrar sólo
= packetsField nuevo texto (6) / / # de paquetes recibidos
packetsField.setEditable (false); / / mostrar sólo
= bannerField nuevo texto (12) / / la bandera de acogida
bannerField.setEditable (false); / / mostrar sólo
= statusField nuevo texto (34) / / situación actual
statusField.setEditable (false); / / mostrar sólo

= muteButton nuevo botón ( "Silenciar / Activar audio"); / / silenciar a un usuario


= hideButton nuevo botón ( "Ocultar / Mostrar"); / / ocultar un usuario
= queryButton nuevo botón ( "Consulta"); / / Información de consulta pública
= selectButton nuevo botón ( "Seleccionar"); / / select avatar

/ / Campos de información pública


65

VRML 2.0 con Java CAPÍTULO 18

= nicknameField nuevo texto (8);


= nameField nuevo texto (8);
= emailField nuevo texto (8);
= webpageField nuevo texto (8);
= geographyField nuevo texto (8);
= phoneField nuevo texto (8);
= faxField nuevo texto (8);

/ / Botones para seleccionar las páginas


CheckboxGroup selector = new CheckboxGroup ();
infoCheckbox = new Casilla ( "Información", selector, true);
peopleCheckbox = new Casilla ( "Personas",
Selector, false);
filterCheckbox = new Casilla ( "Filtro",
Selector, false);
avatarCheckbox = new Casilla ( "Avatar",
Selector, false);
networkCheckbox = new Casilla ( "Red",
Selector, false);
profileCheckbox = new Casilla ( "Perfil",
Selector, false);

/ / Deslizadores para el filtrado de información


visualAcuitySlider = new scrollbar (Scrollbar.HORIZONTAL,
0, 0, 0, SCROLLMAX);
visualHorizonSlider = new scrollbar (Scrollbar.HORIZONTAL,
0, 0, 0, SCROLLMAX);

/ / Suspender las actualizaciones de filtro de acogida


suspendCheckbox = new Casilla ();
peopleList = new lista (10, false); / / lista de usuarios
peopleList.setFont (nuevo Font ( "Courier", Font.PLAIN, 12));
avatarList = new lista (10, false); / / lista de avatares
avatarList.setFont (nuevo Font ( "Courier", Font.PLAIN, 12));

Ahora que la avatarList ha sido creado, podemos leer los parámetros que dan las
descripciones y las URL de los diversos avatares:

/ / Poblar la lista avatar


for (int i = 0; getParameter ( "avatarDesc" i)! = null; i) (
Cadena desc = getParameter ( "avatarDesc" i);
Cadena url = getParameter ( "avatarUrl" i);
if (url! = null) (
avatarList.addItem (new String (desc));
avatars.addElement (new String (url));
)
)
avatarList.select (0);
66

VRML 2.0 con Java CAPÍTULO 18

avatarList.makeVisible (0);

A continuación crear el principal grupo de controles, incluidos los botones de radio y el


mensaje de estado:

MainControls panel = new Panel ();


mainControls.setLayout (nuevo FlowLayout ());
mainControls.setBackground (Color.lightGray);
StatusPanel panel = new Panel ();
statusPanel.setLayout (nuevo FlowLayout ());
statusPanel.setBackground (Color.lightGray);
statusPanel.setForeground (Color.blue);
statusPanel.add (nueva etiqueta ( "Situación :"));
statusField.setForeground (Color.white);
statusPanel.add (statusField);

/ / Panel de botones para seleccionar las páginas


SelectorPanel panel = new Panel ();
selectorPanel.setLayout (nuevo GridLayout (6, 1));
selectorPanel.setBackground (Color.cyan);
selectorPanel.add (infoCheckbox);
selectorPanel.add (peopleCheckbox);
selectorPanel.add (filterCheckbox);
selectorPanel.add (avatarCheckbox);
selectorPanel.add (networkCheckbox);
selectorPanel.add (profileCheckbox);

El siguiente paso es establecer todas las diferentes tarjetas. Esto es tedioso, por lo que no se
moleste lista aquí todo. Nos limitamos a usar el panel de personas como un ejemplo:

GridBagConstraints gbc = new GridBagConstraints ();


PeoplePanel panel = new Panel ();
GridBagLayout GBL = new GridBagLayout ();
peoplePanel.setLayout (GBL);
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbc.weightx = 100; gbc.weighty = 100;
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1;
gbc.gridheight = 3;
gbl.setConstraints (peopleList, gbc);
peoplePanel.add (peopleList);
gbc.anchor = GridBagConstraints.SOUTH;
gbc.gridy = 0; gbc.gridx = 1;
gbc.gridheight = 1;
gbl.setConstraints (hideButton, gbc);
peoplePanel.add (hideButton);
gbc.gridy = 1;
67

VRML 2.0 con Java CAPÍTULO 18

gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (muteButton, gbc);
peoplePanel.add (muteButton);
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.NORTH;
gbl.setConstraints (queryButton, GBC)
peoplePanel.add (queryButton);

Los otros grupos son similares.

El "grupo de grupos"-la-cardPanel se crea como sigue:

cardPanel = new Grupo ();


cardPanel.setLayout (nuevo CardLayout ());
cardPanel.add ( "Información", infoPanel);
cardPanel.add ( "Personas", peoplePanel);
cardPanel.add ( "Filtro", filterPanel);
cardPanel.add ( "Avatar", avatarPanel);
cardPanel.add ( "Red", networkPanel);
cardPanel.add ( "Perfil", profilePanel);

Por último, establecer el panel principal:

setLayout (nuevo BorderLayout ());


añadir ( "Sur", statusPanel);
añadir ( "Oriente", selectorPanel);
agregar ( "Centro", cardPanel);

Eso es todo por el inicio () método.


El inicio (), stop (), y ejecutar () Métodos

El inicio () y stop () métodos son triviales:

public void start () (


if (myThread == null)
myThread = new Hilo (este);
myThread.start ();
if (mundo! = null)
world.suspend (suspendCheckbox.getState ());
)

public void stop () (


if (mundo! = null)
world.suspend (true);
if (myThread! = null)
myThread.stop ();
)
68

VRML 2.0 con Java CAPÍTULO 18

La carrera () método es corto pero interesante. Se aparece el inicio de sesión de diálogo que
se ha descrito anteriormente, entonces se crea la interfaz del navegador y el mundo de
interfaz (es decir, el API para la multi-tecnología). También se inicia un par de hilos, una
para mantener el tiempo de conexión al día y uno para actualizar la lista de usuarios cada
pocos segundos:

public void run () (


LoginDialog nuevo (este);
browserSetup (); / / conectar con el navegador de VRML
worldSetup (); / / conectar con el mundo
TimeUpdater nuevo (este); / / mantiene la hora actualizada
PeopleReader nuevo (este) / / guarda la lista de los usuarios hasta la fecha
mientras que (con) (
if (mundo! = null) (
Enumeración en world.getEntities = ();
/ / Ir a través de entidades
while (en.hasMoreElements ())
updateEntity ((Entidad) en.nextElement ());
)
Thread.yield ();
)
)

Una vez que todo esté configurado, el run () método sólo se encuentra en un bucle que
actualiza las entidades, según sea necesario:

El Método updateEntity

Este método es llamado una vez para cada una de las entidades locales en nuestra base de
datos cada vez que a través del lazo.

private void updateEntity (Entidad e) (


int estado = e.getState ();
if (state == Entity.STALE | | estado == Entity.GONE) (
/ / Hacia la izquierda o la entidad ha caducado
e.setState (Entity.DEAD);
e.setRepresentation (null);
retorno; / / hacer con esta entidad
)
/ / Asegúrese de que el objeto tiene una representación
Rep = cosa (Thing) e.getRepresentation ();
if (re == null | | e.hasNewURL ()) (
/ / Necesidad de crear una representación
Url = e.getURL cadena ();
if (url == null)
retorno;
69

VRML 2.0 con Java CAPÍTULO 18

try (
rep = new Cosa (navegador,
addAvatars, removeAvatars, url);)
de capturas (InvalidVrmlException ex) (
System.out.println ( "no puede agregar objeto:" + ex);
retorno;
)
de capturas (InvalidEventInException ex) (
System.out.println ( "no puede agregar objeto:" + ex);
retorno;
)
e.setRepresentation (REP);
e.unmarkNewURL ();
)
if (e.hasChanged ()) (
/ / Rep actualización con la localización y orientación
rep.setLocation (e.getLocation ());
rep.setOrientation (e.getOrientation ());
rep.setVisibility (! e.isHidden ());
e.markUnchanged ();
)
)

Si la entidad estatal es STALE o desaparecido, significa que el sistema multiusuario ha


decidido que esta entidad ya no debe ser parte de la base de datos. Es aquí que el applet lo
reconoce mediante el establecimiento del estado a los puntos muertos, lo que hace que el
hilo Reaper a disponer del mismo tiempo. Asimismo, establecer la representación sobre el
terreno de la entidad a nulo, lo que hace que el finalizar () el método de las Cosas que se
llama (la clase de cosa se describe más adelante en este capítulo). El finalizar () método de
la clase Cosa elimina la representación de la entidad del mundo VRML.

Si la entidad no tiene ninguna representación, o una nueva URL ha sido fijado por el
sistema multiusuario, es crear algo nuevo y configurarlo como la representación de esta
entidad. Reconocemos que hemos aceptado la nueva URL llamando la entidad
unmarkNewURL () método.

Si la entidad ha cambiado, tenemos su ubicación, orientación, información y visibilidad y lo


utilizará para fijar los valores correspondientes en la representación de VRML. Tenga en
cuenta que el isHidden () método devuelve verdadero si la entidad es invisible, por lo que
tapa el valor al llamar setVisiblity ().
El handleEvent () Método

Los eventos para el MultiUserClient es competente para la tramitación de cambios a


cualquiera de los controles:

boolean público handleEvent (Event evt) (


CardLayout diseño = (CardLayout) cardPanel.getLayout ();
if (evt.id == Event.ACTION_EVENT) (
70

VRML 2.0 con Java CAPÍTULO 18

if (evt.target instanceof Casilla) (


if (evt.target == infoCheckbox)
layout.show (cardPanel, "Información");
else if (evt.target == peopleCheckbox)
layout.show (cardPanel, "Personas");
else if (evt.target == filterCheckbox)
layout.show (cardPanel, "Filtro");
else if (evt.target == avatarCheckbox)
layout.show (cardPanel, "Avatar");
else if (evt.target == networkCheckbox)
layout.show (cardPanel, "Red");
else if (evt.target == profileCheckbox)
layout.show (cardPanel, "Perfil");

Este se encarga de la conmutación de una tarjeta a otra. El resto de los procesos método
sólo los diversos controles, tales como la "suspensión" en:

else if (evt.target == suspendCheckbox & & mundo! = null)


world.suspend (((Boolean) evt.arg). booleanValue ());
return true;
)

Usamos el world.suspend () método de la API de multiusuario para controlar el flujo de


actualizaciones.

Los diferentes campos de texto que se utilizan para configurar el alias del usuario y la
información pública son manejados idéntica:

else if (evt.target instanceof texto) (


if (evt.target == nicknameField & & myEntity! = null)
myEntity.setNickName ((String) evt.arg);
else if (evt.target == nameField)
info [0] = (String) evt.arg;
else if (evt.target == emailField)
info [1] = (String) evt.arg;
else if (evt.target == webpageField)
info [2] = (String) evt.arg;
else if (evt.target == geographyField)
info [3] = (String) evt.arg;
else if (evt.target == phoneField)
info [4] = (String) evt.arg;
else if (evt.target == faxField)
info [5] = (String) evt.arg;
if (myEntity! = null) (
myEntity.setInfo (información);
try (myEntity.updateRegistry ();)
catch (IOException ex) ()
de capturas (PermissionDeniedException ex) ()
71

VRML 2.0 con Java CAPÍTULO 18

)
return true;
)

Tenga en cuenta que nosotros llamamos la updateRegistry () método de la LocalEntity que


corresponde a nuestro avatar, con el fin de enviar estos cambios en el Registro de acogida.
Algo similar se realiza cuando el usuario selecciona un nuevo avatar:

else if (evt.target instanceof Botón) (


if (evt.target == selectButton
& & MyEntity! = Null) (
myEntity.setURL (
(String) avatars.elementAt (
avatarList.getSelectedIndex ()));
try (myEntity.updateRegistry ();)
catch (IOException ex) ()
de capturas (PermissionDeniedException ex) ()
)

Ocultar botones Silenciar y sencillo:

else if (evt.target == hideButton) (


int n = peopleList.getSelectedIndex ();
Entidad e = (Entidad) people.elementAt (n);
e.Hide (! e.isHidden ());
peopleList.replaceItem (peopleFormat (e), n);
peopleList.select (n);
)

else if (evt.target == muteButton) (


int n = peopleList.getSelectedIndex ();
Entidad e = (Entidad) people.elementAt (n);
e.Mute (! e.isMuted ());
peopleList.replaceItem (peopleFormat (e), n);
peopleList.select (n);
)

Y el botón de consulta aparece un cuadro de diálogo:

else if (evt.target == queryButton)


nuevo QueryDialog ((Entidad)
people.elementAt (
peopleList.getSelectedIndex ()));
return true;
)
72

VRML 2.0 con Java CAPÍTULO 18

Los deslizadores de la agudeza y el horizonte se manejan contar llamando a los métodos


apropiados en el mundo:

else if (evt.target instanceof scrollbar


& & Mundo! = Null) (
switch (evt.id) (
Event.SCROLL_LINE_UP caso:
Event.SCROLL_LINE_DOWN caso:
Event.SCROLL_PAGE_UP caso:
Event.SCROLL_PAGE_DOWN caso:
Event.SCROLL_ABSOLUTE caso:
if (evt.target == visualAcuitySlider)
world.setVisualAcuity (
visualAcuitySlider.getValue ()
/ (Float) SCROLLMAX);
else if (evt.target == visualHorizonSlider)
world.setVisualHorizonCount (
visualHorizonSlider.getValue ());
return true;
por defecto: return false;
)
)

Hay una utilidad que proporciona el método de formato para la lista de usuarios:

public static Cadena peopleFormat (Entidad e)


String name = e.getNickName ();
if (nombre == null)
name = "usuario";
return (e.isHidden ()? "H": "")
(e.isMuted ()? "M": "")
"" Nombre;
)

El Método worldSetup

El último método en la clase MultiUserClient es worldSetup (), que llama al constructor de


mundo para establecer una conexión con el Registro de acogida y configurarse para enviar
y recibir mensajes de filtro de actualizaciones. Se muestra un mensaje de error de color rojo
brillante en la ventana de estado si no hay ningún problema. De lo contrario, lee el mensaje
de la bandera del registro de acogida:

private void worldSetup () (

/ / Conectar con el mundo

try (
73

VRML 2.0 con Java CAPÍTULO 18

= mundo nuevo mundo (el nombre de host, puerto);


statusField.setText ( "Conectado");
statusField.setForeground (Color.yellow);
)
catch (Exception ex) (
statusField.setText (ex.toString ());
statusField.setForeground (Color.red);
)

if (mundo! = null) (
/ / Establecer el banner
bandera = world.getBanner ();
if (bandera == null)
bandera = "";
bannerField.setText (bandera);
/ / Crear nuestro avatar
try (world.identity (nombre de usuario, contraseña);)
catch (IOException ex) (
System.out.println ( "no podía IDENT:" ex);
)
de capturas (BadIdentityException ex) (
System.out.println ( "no podía IDENT:" ex);
)
try (
/ / Usamos el nombre de usuario como CNAME
/ / Para el avatar del usuario:
myEntity = new LocalEntity (Mundo, el nombre de usuario);
/ / También lo utilizan como apodo inicial:
myEntity.setNickName (nombre de usuario);
/ / Usar un avatar por defecto:
myEntity.setURL ((String) avatars.elementAt (0));
myEntity.updateRegistry ();
)
de capturas (PermissionDeniedException ex) (
System.out.println ( "no podía crear avatar:" ex);
)
catch (IOException ex) (
System.out.println ( "no podía crear avatar:" ex);
)
)
)

Lo último que worldSetup () hace es crear un avatar para el usuario. El nombre de usuario y
la contraseña de inicio de sesión de diálogo se utilizan para identificar al usuario en el
registro de acogida, el LocalEntity se crea utilizando el nombre de usuario como nombre
canónico. El nombre de usuario también se utiliza como el nombre inicial, aunque el
usuario puede cambiar esto en cualquier momento desde la tarjeta de perfil. El primer
74

VRML 2.0 con Java CAPÍTULO 18

avatar en la lista es seleccionado como el avatar por defecto, aunque el usuario también
puede cambiar esta opción más adelante si así lo desean (Avatar de la tarjeta).

El QueryDialog y LoginDialog Clases

Estos son sólo los cuadros de diálogo estándar. QueryDialog es usado para mostrar la
información detallada sobre un usuario que se produce cuando el botón de la Consulta
Popular se hace clic en la tarjeta. LoginDialog símbolo se utiliza para el usuario por su
nombre de usuario y contraseña cuando se inicia por primera vez la aplicación de:

clase de diálogo se extiende QueryDialog (

Botón protegidas dismissButton = nuevo botón ( "Cerrar");

QueryDialog público (Entidad entidad) (


super (nuevo marco (), "Consulta de Usuario", true); / / cuadro de diálogo modal
GridBagLayout GBL = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
setLayout (GBL);
Etiqueta etiqueta;
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 100; gbc.weighty = 100;
gbc.insets = new Insets (10, 0, 10, 20);
gbc.anchor = GridBagConstraints.EAST;
gbc.fill = GridBagConstraints.NONE;
gbc.gridy = 0; etiqueta = new Etiqueta ( "Primer / Apellido:");
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 1; etiqueta = new Etiqueta ( "dirección de correo electrónico:");
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 2; etiqueta = new Etiqueta ( "página Web:");
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 3; etiqueta = new Etiqueta ( "Ubicación geográfica:");
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 4; etiqueta = new Etiqueta ( "Teléfono:");
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 5; etiqueta = new Etiqueta ( "Número de fax:");
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST;
String [] info = entity.getInfo ();
gbc.gridy = 0;
etiqueta = new Etiqueta (info [0] == null? "<desconocido>": info [0]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 1;
etiqueta = new Etiqueta (info [1] == null? "<desconocido>": info [1]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
75

VRML 2.0 con Java CAPÍTULO 18

gbc.gridy = 2;
etiqueta = new Etiqueta (info [2] == null? "<desconocido>": info [2]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 3;
etiqueta = new Etiqueta (info [3] == null? "<desconocido>": info [3]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 4;
etiqueta = new Etiqueta (info [4] == null? "<desconocido>": info [4]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 5;
etiqueta = new Etiqueta (info [5] == null? "<desconocido>": info [5]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridx = 0; gbc.gridy = 6;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.CENTER;
gbc.insets = new Insets (10, 10, 10, 10);
gbc.gridy = 6;
gbl.setConstraints (dismissButton, gbc); añadir (dismissButton);
pack ();
show ();
)

boolean acción pública (evt evento, objeto whichAction) (


if (evt.target == dismissButton)
ocultar ();
return true;
)

clase de diálogo se extiende LoginDialog (

Botón protegidas connectButton = nuevo botón ( "Connect");


protegidas usernameField de texto, passwordField;

LoginDialog público (MultiUserClient muclient) (


super (nuevo marco (), "Login", true); / / cuadro de diálogo modal
GridBagLayout GBL = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
setLayout (GBL);
Etiqueta etiqueta;
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 100; gbc.weighty = 100;
gbc.insets = new Insets (10, 0, 10, 20);
gbc.anchor = GridBagConstraints.EAST;
gbc.fill = GridBagConstraints.NONE;
etiqueta = new Etiqueta ( "Userid:");
76

VRML 2.0 con Java CAPÍTULO 18

gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);


gbc.gridy = 1;
etiqueta = new Etiqueta ( "Contraseña:");
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.anchor = GridBagConstraints.WEST;
gbc.gridx = 1; gbc.gridy = 0;
= usernameField nuevo texto (16);
gbl.setConstraints (usernameField, gbc);
añadir (usernameField);
gbc.gridy = 1;
= passwordField nuevo texto (16);
passwordField.setEchoCharacter ('*');
gbl.setConstraints (passwordField, gbc);
añadir (passwordField);
gbc.gridy = 2;
gbl.setConstraints (connectButton, gbc);
añadir (connectButton);
pack ();
show ();
muclient.username = usernameField.getText ();
muclient.password = passwordField.getText ();
)

boolean acción pública (evt evento, objeto whichAction) (


if (evt.target == connectButton)
ocultar ();
return true;
)

El Hilo TimeUpdater

El hilo TimeUpdater dice la hora actual, resta de ella el momento en que el cliente
conectado al Registro de acogida y de filtro de acogida, y convierte el resultado de horas y
minutos:

clase extiende TimeUpdater Hilo (

MultiUserClient cliente;

TimeUpdater público (MultiUserClient cli) (


client = clima;
setPriority (NORM_PRIORITY-1);
start ();
)

public void run () (


77

VRML 2.0 con Java CAPÍTULO 18

while (true) (
largo secondsConnected =
(System.currentTimeMillis ()
- Client.world.getConnectedTime ()) / 1000;
largo minutesConnected = secondsConnected/60;
largas horas minutesConnected = / 60;
largo minutesConnected minutos = 60%;
client.connectedField.setText (
horas + ":" + ((minutos <10)? "0": "")
+ Minutos);
client.packetsField.setText (
String.valueOf (client.world.getPacketCount ()));
intenta dormir (1000); de capturas (InterruptedException ex) ()
)
)
)

La hora se establece entonces el valor de la llamada connectedField texto, que aparece en la


tarjeta de red. El número de paquetes recibidos también se muestra en la misma página de
la misma manera. El hilo se despierta una vez cada segundo.

El Hilo PeopleReader

El hilo PeopleReader reconstruye el peopleList y el vector de las entidades


correspondientes a las personas.

clase extiende PeopleReader Hilo (

MultiUserClient cliente;

PeopleReader público (MultiUserClient cli) (


client = clima;
setPriority (NORM_PRIORITY-1);
start ();
)

public void run () (


while (true) (
/ / Borrar la lista para que podamos volver a llenar:
client.peopleList.delItems (0,
client.peopleList.countItems () -1);
client.people = new Vector ();
Enumeración en client.world.getEntities = ();
while (en.hasMoreElements ()) (
78

VRML 2.0 con Java CAPÍTULO 18

Entidad e = (Entidad) en.nextElement ();


if (e.isAvatar () == false) / / no hacer caso omiso de los avatares
continuar;
if (e.needsInfo ()) / / no todos llegamos
continuar;
client.people.addElement (e);
client.peopleList.addItem (client.peopleFormat (e));
)
client.peopleList.select (0);
intentar Thread.sleep (10000) / / actualiza cada diez segundos
de capturas (InterruptedException ex) ()
)
)
)

El usa el hilo isAvatar () el método en cada entidad para determinar si


corresponde a un usuario humano. Ignora las entidades para las que la
información detallada (como su apodo) no está actualmente disponible. El hilo
se ejecuta una vez cada diez segundos.
La interfaz de VRML

El multiusuario applet debe ser capaz de actualizar la escena VRML en


respuesta a los mensajes de la red. También hay pista de la ubicación del
usuario, ya que navegar por el mundo, tanto para informar a la red de que el
usuario del avatar y es para decirle al filtro de acogida donde se encuentra el
punto de vista.
Variables de Instancia

Varias variables de instancia se utilizan:

Navegador navegador / / el navegador de VRML


Nodo raíz, / / nodo raíz de la carga mundial
Nodo proxSensor; / / primero de ellos es un ProximitySensor
Nodo entityGroup; / / segundo de un grupo para la celebración de avatares
EventOutSFVec3f position_changed / / eventOuts de ProximitySensor
EventOutSFRotation orientation_changed;
EventInMFNode addAvatars = null; / / eventIns de ObjGroup
EventInMFNode removeAvatars = null;

El navegador variable puntos en el navegador en sí. El nodo raíz se refiere a un


Grupo de alto nivel en el nodo de VRML mundo; estaremos agregando dos
nodos al mismo, una y otra ProximitySensor Grupo nodo. El Nodo proxSensor se
refiere a la ProximitySensor que añadir al mundo con el fin de rastrear la
ubicación del usuario y la orientación. El Nodo entityGroup que añadir que los
79

VRML 2.0 con Java CAPÍTULO 18

demás niños de la raíz de nodo se utiliza para contener las entidades que se
incorporen al mundo, algo así como una "Zona" nodo de Vida Mundos.

También mantener las referencias a la position_changed y orientation_changed


eventOuts de la ProximitySensor nodo que hemos añadido, así como
referencias a la AddChildren y removeChildren eventIns sobre la entityGroup
nodo. Tenga en cuenta que eventOuts en los nodos son eventIns a nuestro
script, y viceversa.

El browserSetup () Método

Como vimos anteriormente, el run () el método de la clase MultiUserClient


llama a la browserSetup () para establecer el acceso al navegador VRML y
añadido de la escena que contiene. Esto es lo que browserSetup () tiene el
siguiente aspecto:

private void browserSetup () (


/ / Encontrar el navegador de VRML
= getBrowser navegador ();
if (navegador == null)
System.out.println ( "No se pudo encontrar el navegador de VRML!");

/ / Encontrar el nodo raíz de la carga mundial


root = null;
try (
mientras que (root == null)
root = browser.getNode ( "Root");

)
de capturas (InvalidNodeException e) (
System.out.println ( "No se pudo encontrar el nombre de nodo \" Root \
"");
retorno;
)

EventInMFNode AddChildren = null;


try (
AddChildren = (EventInMFNode)
root.getEventIn ( "AddChildren");)
de capturas (InvalidEventInException e) (
System.out.println (
80

VRML 2.0 con Java CAPÍTULO 18

"No es posible acceder en AddChildren eventIn nodo raíz:"


+ E);
)

Nodo [] ournodes = browser.createVrmlFromString (


"ProximitySensor (tamaño 1e30 1e30 1e30) \ n" +
"Grupo de () \ n"
);
addChildren.setValue (ournodes);
proxSensor = ournodes [0];
entityGroup = ournodes [1];

/ / Encontrar el entityGroup el nodo y la AddChildren


/ / RemoveChildren eventIns
try (
addAvatars = (EventInMFNode)
entityGroup.getEventIn ( "AddChildren");
removeAvatars = (EventInMFNode)
entityGroup.getEventIn ( "removeChildren");

)
de capturas (InvalidEventInException e) (
System.out.println (
"No es posible acceder en eventIn nodo ObjGroup:"
+ E);
)

/ / La captura del position_changed y proxSensor


/ / Orientation_changed eventIns
position_changed = (EventOutSFVec3f)
proxSensor.getEventOut ( "position_changed");
position_changed.advise (este, null);
orientation_changed =
(EventOutSFRotation)
proxSensor.getEventOut ( "orientation_changed");
orientation_changed.advise (este, null);

Después de obtener una referencia al navegador de VRML, tratamos de


encontrar el nodo llamado "principal" en el mundo. Si el mundo VRML es
grande o compleja, es posible que el applet de multiusuario puede haber
cargado antes de que el mundo estaba listo, por lo que espera en el bucle de
nodo. Si el nodo no se encuentra, sólo un mensaje de error a la consola de Java
81

VRML 2.0 con Java CAPÍTULO 18

y se rinda.

Una vez que el nodo raíz se ha cargado, tenemos una referencia a su


AddChildren eventIn. Vamos a estar usando esta referencia, a fin de sumar
nuestra ProximitySensor y el Grupo de los nodos. A continuación, crear los dos
nodos; tenga en cuenta que el createVrmlFromString () método devuelve un
array de nodos, el primer elemento de los cuales es el ProximitySensor y el
segundo de los cuales es el Grupo. Una vez que hemos creado los nodos,
usamos AddChildren para añadirlos al mundo.

Tenemos la AddChildren removeChildren eventIns y para el grupo nodo hemos


añadido y guárdelas en addAvatars y removeAvatars, respectivamente. Estos
son los eventos que usaremos para añadir entidades en el mundo y,
posteriormente, eliminarlas.

Por último, tenemos las referencias a la position_changed y


orientation_changed eventOuts de la ProximitySensor. Usamos el
asesoramiento () método para informar al navegador de VRML que queremos
que estos eventos que se enviará a nuestra llamada () método.
La devolución de llamada () Método

La devolución de llamada () método recibe un evento cuando el


ProximitySensor envía position_changed un evento o un acontecimiento
orientation_changed:

public void llamada (EventOut caso, el doble de tiempo, objeto UserData) (


if (event.getType () == FieldTypes.SFVEC3F) (
EventOutSFVec3f pos = (EventOutSFVec3f) evento;
flotado [] = valores pos.getValue ();
Vec3 loc = new Vec3 (valores [0], valores [1], valores [2]);
if (mundo! = null)
world.setViewpoint (loc);
if (myEntity! = null) (
myEntity.setLocation (loc);
try (myEntity.sendUpdate ();)
catch (IOException e) ()
)
)
else if (event.getType () == FieldTypes.SFROTATION) (
if (myEntity! = null) (
Putrefacción EventOutSFRotation = (EventOutSFRotation) evento;
flotado [] = valores rot.getValue ();
if (myEntity! = null) (
myEntity.setOrientation (
82

VRML 2.0 con Java CAPÍTULO 18

nueva rotación (
valores [0], valores [1],
valores [2], valores [3]));
try (myEntity.sendUpdate ();)
catch (IOException e) ()
)
)
)
)

El tipo de evento está marcada para ver si se trata de una traducción o de la


rotación. En cualquier caso, recuperamos los valores (tres carrozas para un
SFVec3f o cuatro para un SFRotation). Pedimos la setLocation () o el método
setOrientation () para cambiar la ubicación o la orientación de nuestro avatar,
que es representada por la LocalEntity llamada myEntity. El LocalEntity del
sendUpdate () método se llama en realidad a enviar una actualización a través
de la red.

El getBrowser () Método

El getBrowser () método que browserSetup () llama es sorprendentemente


complejo. Las futuras versiones de la interfaz externa definirá una manera
sencilla de obtener una referencia al navegador de VRML, pero por ahora
tenemos que utilizar este lugar en cuestión y el navegador Web procedimiento
específico:

Navegador getBrowser privado () (


Navegador navegador Browser.getBrowser = ();
if (navegador == null) (
JSObject ganar = JSObject.getWindow (este);
if (ganar! = null) (
JSObject doc = (JSObject) win.getMember ( "documento");
JSObject incrusta = (JSObject) doc.getMember ( "incrusta");
Applets JSObject = (JSObject) doc.getMember ( "applets");
JSObject marcos = (JSObject) doc.getMember ( "frames");
for (int i = 0; i <10; i + +) (
= navegador (Browser) embeds.getSlot (0);
if (navegador! = null)
break;
try (Thread.sleep (500);)
de capturas (InterruptedException e) ()
)
)
83

VRML 2.0 con Java CAPÍTULO 18

)
retorno del navegador;
)

Tenga en cuenta que este código asume que el primer objeto incrustado
(incrusta. GetSlot (0)) es el navegador de VRML. Tratamos de hasta cinco
segundos para encontrar el navegador incrustado.
La cosa de clase

Una cosa es una representación de una entidad en un mundo VRML. Cada Cosa
en VRML está representada por algo como lo siguiente:

Transformar (
los niños [
Switch (
whichChoice 0
elección [
... Las formas o inlines ...
]
)
]
)

El nodo de transformación se utiliza para mover y rotar la entidad. El nodo


Switch es usado para ocultar la entidad:

/ / Una "cosa" - una envoltura alrededor de un objeto VRML


/ / Escrito por Bernie Roehl, enero de 1996
vrml.external.Browser de importación;
vrml.external.Node de importación;
importación vrml.external.field .*;
importación vrml.external.exception .*;
de importación de múltiples .*;

Cosa pública clase (


Nodo transformNode protegidas;
Nodo switchNode protegidas;
EventInSFVec3f set_translation protegidas;
EventInSFRotation set_rotation protegidas;
EventInSFInt32 set_switch protegidas;
protegidas EventInMFNode eliminar;

El transformNode y switchNode las variables apuntan a la transformación y


Switch nodos del VRML mundo. El set_translation y set_rotation eventos se
84

VRML 2.0 con Java CAPÍTULO 18

usan para fijar la traducción y la rotación de los campos de la transformación


nodo. El set_switch evento se utiliza para establecer que el ámbito de la
Elección Switch nodo.

Eliminar la variable de puntos a la removeChildren método de la matriz de este


nodo, y se utiliza cuando es el momento de la Cosa, debe suprimirse.

El setLocation () y setOrientation () se utilizan métodos para hacer el trabajo de


movimiento y rotación de la Cosa, y el setVisibility () establece el método de
Switch:

public void setLocation (Vec3 loc) (


flotado [] = new flotado valor [3];
valor [0] = loc.getX ();
valor [1] = loc.getY ();
valor [2] = loc.getZ ();
set_translation.setValue (valor);
)

public void setOrientation (Rotación de origen) (


flotado [] = new flotado valor [4];
valor [0] = ori.getX ();
valor [1] = ori.getY ();
valor [2] = ori.getZ ();
valor [3] = ori.getAngle ();
set_rotation.setValue (valor);
)

public void setVisibility (boolean visible) (


set_switch.setValue (visible? 0: -1);
)

El constructor de una cosa se basa la transformación y Switch nodos como se


describe más arriba y hace que el interruptor de un niño de la transformación.
También añade una línea que contiene la URL del archivo VRML real para la
entidad. Por último, se las referencias a cada uno de los acontecimientos
descritos más arriba:

Cosa pública (navegador navegador, EventInMFNode añadir,


EventInMFNode eliminar, Cadena url)
lanza InvalidVrmlException, InvalidEventInException (
Nodo [] nodos;
EventInMFNode nodeIn;
/ / Crear nodo de transformación
85

VRML 2.0 con Java CAPÍTULO 18

nodos = browser.createVrmlFromString ( "Transformar ()");


transformNode nodos = [0];
add.setValue (nodos);
/ / Crear nodo Switch
nodos browser.createVrmlFromString = (
"Cambiar whichChoice (0)");
switchNode nodos = [0];
/ / Ponga Switch de transformación en
nodeIn = (EventInMFNode) transformNode.getEventIn (
"AddChildren");
nodeIn.setValue (nodos);
/ / Crear nodo en línea
nodos browser.createVrmlFromString = (
"Forma ((url geometría en línea \" "+ url +" \ "))");
/ / Ponga en línea Switch
nodeIn = (EventInMFNode) switchNode.getEventIn ( "elección");
nodeIn.setValue (nodos);
/ / Obtener EventIns
set_translation = (EventInSFVec3f)
transformNode.getEventIn ( "traducción");
set_rotation = (EventInSFRotation)
transformNode.getEventIn ( "rotación");
set_switch = (EventInSFInt32)
switchNode.getEventIn ( "whichChoice");
)

El último método en la clase de cosa es terminar (), que se llama cuando la


cosa se destruye:

public void finalizar () (


Nodo [] = new Nodo nodos [1];
nodos [0] = transformNode;
remove.setValue (nodos);
)
)

Eliminar el evento se utiliza para eliminar la transformación del Grupo de nodo


que lo contiene.
El texto de Chat Applet

El applet de chat de texto es mucho, mucho más simple que el Applet


multiusuario. Así es como empieza:

/ / El texto de Chat Cliente applet


86

VRML 2.0 con Java CAPÍTULO 18

/ / Escrito por Bernie Roehl, enero de 1997

importación java.applet .*;


importación java.awt .*;
importación java.net .*;
importación java.io. *;
importación java.util .*;
de importación de múltiples .*;

clase pública se extiende TextClient Applet (

public static final Cadena version = "0.1";

InputField de texto;
Textarea outputArea;

TextReceiver textReceiver;
TextSender textSender;

Mundo Mundo = null

getAppletInfo public String () (


retorno "multi-usuario de Chat de texto de clientes" + versión
+ ", Escrito por Bernie Roehl";
)

public String [] [] getParameterInfo () (


String [] [] info = (
( "Anfitrión", "cadena",
"el nombre o la dirección IP de la filtración de acogida"),
( "Puerto", "entero",
"La propiedad intelectual al puerto al que enviar mensajes de texto")

);
información de retorno;
)

El inputField es donde el usuario escribe en sus mensajes, y los resultados


putArea es donde el texto de todos los usuarios se muestra. Un TextSender
objeto se utiliza para enviar los mensajes de texto, y TextReceiver un objeto se
utiliza para recibirlos. Todo muy sencillo.
87

VRML 2.0 con Java CAPÍTULO 18

Init () Método

Init () lee el método de acogida del puerto y los parámetros de la <param>


etiquetas en el archivo HTML, de nuevo utilizando la acogida que el documento
ha sido cargado de acogida como el predeterminado. La interfaz de usuario se
construye, como lo fue para el applet multiusuario:

public void init () (

/ / Leer los parámetros de aplicación

Cadena hostname = getParameter ( "host");


int port = Integer.parseInt (getParameter ( "puerto"));
if (host == null)
hostname = getDocumentBase (). getHost ();

/ / Crear los campos de texto, y añadir a nuestra disposición

= inputField nuevo texto (100);


= outputArea nuevo texto (10, 100);
outputArea.setEditable (false); / / salida
outputArea.setBackground (Color.yellow);

setLayout (nuevo BorderLayout ());

agregar ( "Centro", outputArea);


añadir ( "Sur", inputField);

El siguiente paso es crear la TextSender y TextReceiver objetos:

/ / Crear el TextSender y objetos TextReceiver

try (textSender = new TextSender (nombre de host, puerto);)


catch (Exception ex) (
addLine ( "No se pudo crear remitente texto!");
addLine ( "Exposición de motivos:" + ex);
retorno;
)

try (textReceiver = new TextReceiver ();)


catch (Exception ex) (
addLine ( "No se pudo crear el receptor de texto!");
addLine ( "Exposición de motivos:" + ex);
retorno;
88

VRML 2.0 con Java CAPÍTULO 18

Tenga en cuenta que los mensajes de error son enviados a la outputArea


usando el complemento de línea () método.

Generamos una aleatoria número de 32 bits para usar como ID de nuestro


texto, sólo para demostrar que no tiene por qué ser el mismo que nuestra
entidad ID. Hay una muy, muy, muy pequeña posibilidad de que dos usuarios
del viento hasta que tengan el mismo ID de texto. Nosotros no te preocupes
para nuestra aplicación, pero la especificación de RTP (RFC 1889) que describe
el modo de garantizar que estos se generan aleatoriamente SSRC valores son
únicos dentro de un período de sesiones:

/ / Generar y establecer un control aleatorio de 32 bits de texto ID

textSender.setTextId ((nuevo aleatoria ()). nextInt ());

Y, por último, de empezar un hilo para recibir mensajes de texto y lo muestra


en la outputArea:

nuevo ReceiveText (textReceiver, este);

El inicio () Método

El inicio () utiliza el método getApplet () para encontrar el cliente de


multiusuario. Es posible que el TextClient, siendo menor que el MultiUserClient,
recibirá cargada en primer lugar; por lo tanto, el uso de un tiempo () bucle que
esperar hasta que el applet se puede encontrar. El nombre de "control" se
especifica mediante una "name =" en la etiqueta para el <applet>
multiusuario cliente applet en el archivo HTML:

public void start () (


MultiUserClient muClient = null;
while (muClient == null)
muClient = (MultiUserClient)
getAppletContext (). getApplet ( "control");
while (Mundo == null)
Mundo = muClient.getWorld ();
world.setTextPort (textReceiver.getPort ());
LocalEntity avatar = null;
while (avatar == null)
avatar muClient.getAvatar = ();
avatar.setTextId (textSender.getTextId ());
try (avatar.updateRegistry ();)
89

VRML 2.0 con Java CAPÍTULO 18

catch (IOException ex) (


System.out.println (
"no puede establecer el texto de identificación para mi avatar:"
+ Ex);
)
de capturas (PermissionDeniedException ex) (
System.out.println (
"no puede establecer el texto de identificación para mi avatar:"
+ Ex);
)
)

Una vez que el applet multiusuario cliente se encuentra, que recuperar el


puntero del mundo. Es posible que no se han establecido todavía, así que
espere hasta que esté. El mismo procedimiento se utiliza para encontrar el
LocalEntity corresponing para el usuario del avatar. Una vez que el avatar se
encuentra, le informamos de que el texto ID seremos usando; que la
información será enviada al Registro para que otros TextClients ejecutando en
otros hosts será capaz de identificar nuestro avatar en su texto de
identificación.

Una pequeña utilidad es de rutina, siempre que simplemente agrega una línea
de texto a la outputArea:

vacío addLine (String s) (/ / añadir una línea a la outputArea


outputArea.appendText (s + "\ n");
)

La acción () Método

La acción () TextClient método de la clase simplemente responde a las líneas


de texto al usuario por decir la TextSender objeto de enviar a:

boolean acción pública (evt evento, objeto arg) (


if (evt.target instanceof texto) (
if (evt.target == inputField) (
try (textSender.sendText ((String) arg);)
catch (IOException ex) (
addLine ( "Error al enviar el texto:" + ex);
)
inputField.setText (""); / / despejar el campo de entrada
)
return true;
)
90

VRML 2.0 con Java CAPÍTULO 18

return false;
)
)

No hay necesidad de hacerse eco del texto del usuario a la outputArea; esto se
hará por el filtro de acogida, que reenvía el mensaje a todos los clientes,
incluyendo el que lo envió. Hay un beneficio adicional para hacer esto: Se le da
al usuario una indicación de lo lento o rápido es la red.
El Hilo ReceiveText

El hilo que recibe mensajes de texto va por separado del cuerpo principal de la
TextClient applet. Tiene su sede en un bucle, a la espera de entrar en los
mensajes de la getMessage () TextReceiver método de la clase. Cuando un
mensaje llega, el texto se extrae de ID y espera que en el mundo local de la
base de datos de entidades. Si es encontrado, el apodo por objeto que se
recupera y se utiliza como el nombre de usuario para el mensaje de texto, de lo
contrario, el texto se utiliza en lugar ID:

clase extiende ReceiveText Hilo (

protegidas TextReceiver receptor;


protegidas TextClient cliente;

ReceiveText público (TextReceiver rcvr, TextClient cli) (


rcvr = receptor;
client = clima;
start ();
)

public void run () (


Mensaje msg;
while (true) (
try (msg = receiver.getMessage ();)
catch (IOException ex) (
client.addLine ( "Error al recibir el mensaje:" + ex);
continuar;
)
int TextID msg.getTextId = ();
String name = null;
if (client.world! = null) (
Entidad ent = client.world.getEntityByTextId (TextID);
if (ent! = null) (
name = ent.getNickName ();
)
91

VRML 2.0 con Java CAPÍTULO 18

)
if (nombre == null)
name = "entidad #" + TextID;
client.addLine (nombre + ":" + msg.getText ());
)
)
)

Del cliente addLine () el método es usado para mostrar el mensaje en el


outputArea, al comienzo con el nombre del usuario que lo envió.

Juntando Todo

El navegador de VRML, el multi-cliente, y el cliente de chat de texto son atadas


por el hecho de estar en el mismo documento HTML. El documento en sí mismo
tiene este aspecto:

<html> <head> <title> multi-usuario de cliente </ title> </ head>

<body>
<embed src="empty.wrl" border=1 height=300 width=500>

<applet name = control height = 300 width = 300


code = MultiUserClient.class mayscript>

<param name=host value=ece.uwaterloo.ca>


<param name=port value=3000>

<! - Nota: ninguno de estos avatares realmente existe! ->

<param name=avatarDesc0 value="Simple azul box">


<param name=avatarUrl0 value=box.wrl>

<param name=avatarDesc1 value="Simple verde cone">


<param name=avatarUrl1 value=cone.wrl>

<param name=avatarDesc2 value="Punk rocker">


<param name=avatarUrl2 value=http://somewhere.com/av2.wrl>

<param name=avatarDesc3 value="Kumquat salesman">


<param name=avatarUrl3 value=http://elsewhere.gov/av3.wrl>

<... Etc ...>


92

VRML 2.0 con Java CAPÍTULO 18

</ applet>

<applet name = textChat height = 200 width = 500


code = TextClient.class mayscript>
<param name=host value=ece.uwaterloo.ca>
<param name=port value=3004>
</ applet>
</ body>
</ html>

El VRML empty.wrl mundo está incrustado en la página, con un tamaño bien


definido. El applet es multiusuario cliente incluyó el uso de un <applet> ... </
APPLET> par de etiquetas. Es asignado el nombre de "control", que puede
recordar es el nombre que el texto de chat applet busca al intentar ponerse en
contacto con el applet de multiusuario.

Varios parámetros se especifican usando la etiqueta <param>. El nombre de la


máquina y el número de puerto para que el cliente debe conectarse se
especifican. El avatarDesc y avatarUrl parámetros vienen en pares, tal como se
describe anteriormente en este capítulo en la sección de Avatar de la tarjeta.

El componente final es el cliente de chat de texto, que también tiene un


nombre y número de puerto. En este caso, el número de puerto es el de que los
mensajes de texto deben ser enviados.

El archivo VRML que se carga al principio es muy simple:

# VRML V2.0 utf8


DEF principal Grupo ()

El Grupo de nodo único es lo que la interfaz del navegador de VRML


multiusuario en el applet utiliza para agregar nodos a la escena. El archivo
puede, por supuesto, contienen un mundo real y VRML, el único requisito es
que el Grupo de nodo llamado "Raíces" estará presente.
Desarrollos futuros

El cliente se presenta aquí es bastante rudimentaria, y hay mucho espacio para


mejorar. Por ejemplo, es posible realizar el seguimiento de paquetes de
pérdidas utilizando la secuencia de números en cada movimiento y
actualización de paquetes de mensajes de texto, pero por el momento no
existe ninguna disposición para ello en el cliente.
Filtrado por Regiones
93

VRML 2.0 con Java CAPÍTULO 18

Soporte para el filtrado de las regiones sería una característica muy útil para
agregar. El espacio particionado código presentado en el capítulo 21, que
podría integrarse con el cliente, y la lista de regiones visibles pueden ser
enviados al filtro de acogida.
Los paquetes más pequeños

Hay espacio para optimizar el uso del ancho de banda de red disponible. Por
ejemplo, el 12-byte encima de la cabecera RTP es bastante derroche, un mejor
diseño podría ser la de utilizar un formato más pequeño de paquetes entre el
cliente y el filtro de acogida y de convertir a partir de RTP y en el filtro de
acogida en el envío y recepción de paquetes más de la red multicast. La
entidad de 32-bit ID podría ser asignada a un solo byte, ya que es poco
probable que el cliente desea manejar más de 256 entidades simultánea. La
posición y la información de la rotación podría probablemente ser comprimido
y, por ejemplo, la distancia puede ser expresado como 16-bits de
compensaciones fijas "hitos".

La sobrecarga adicional de las cabeceras UDP e IP es difícil de evitar, pero es


una solución parcial para empacar múltiples mensajes de actualización en un
solo paquete UDP desde el filtro de acogida al cliente.
Apoyo a la Vida Mundos

La Vida Mundos propuesta aún está en evolución, pero una vez que se
estabiliza debemos ser capaces de añadir soporte para que esta multi-cliente.
El servidor de código, por supuesto, no deberían tener que cambiar en
absoluto.

Un obstáculo a la aplicación de los mundos de vida es que ninguno de los


navegadores disponibles VRML proporciona una implementación completa de
VRML 2.0 todavía, sin embargo, que se puede esperar que el cambio en el
momento de leer esto.
Sonido

Este sistema ha sido diseñado desde cero para apoyar diversos tipos de
streaming de datos, incluyendo sonido. Obviamente, el procesamiento de audio
en tiempo real es poco práctico en Java por razones de rendimiento. Sin
embargo, utilizando métodos nativos de Java con una envoltura alrededor de
ellas haría posible que un applet de audio para hablar con el multi-cliente.

Una vez que esto es posible, la aplicación de audio puede recoger el audio de
su SSRC paquetes RTP y buscar SSRC que en la entidad local de base de datos
exactamente como el texto de chat applet no se ve hasta cuando las entidades
a buscar sus apodos. Habida cuenta de la entidad de referencia, la aplicación
de audio puede encontrar la ubicación y orientación de la fuente de sonido a
94

VRML 2.0 con Java CAPÍTULO 18

fin de proporcionar spatialized de audio. El mecanismo de filtrado podría


ampliarse para permitir el filtrado de sonido, por regiones, la distancia, y
cuenta horizonte.
El otro extremo del cable

En el siguiente capítulo, Justin nos mostrará lo que ocurre con todos nuestros
mensajes una vez que lleguen al servidor. Él nos mostrará cómo el filtro de
acogida y de registro de acogida y cómo se aplican los mecanismos de filtrado
de trabajo.

You might also like