A través de este tutorial, crearemos un trabajo cliente/servidor para usar en el motor de juego. Serás introducido a bajo nivel en la red de trabajo Python a través del uso de sockets. Todo será cubierto paso a paso e ilustrado, y el tutorial completo está disponible para descargar.
Parte 1: Trabajo de red y escritura.
Tomemos un minuto para explicar algunas cosas sobre el trabajo en red. Podría escribir una enciclopedia entera sobre el trabajo de red en ordenador, pero no creo que necesites saber más que lo básico de lo que el paquete trae. No necesitas comprender completamente lo siguiente, pero esta incluido para la seguridad de los que lo necesitan. Cuando un computador necesita enviar datos a otro computador, envía un paquete de información a través de un medio físico para él receptor final. Este paquete está encapsulado con información de cabecera qué explica donde debe ir el paquete. Cuando el paquete es enviado y recibido, fluye a través de un conocimiento estándar acerca el modelo OSI (y el modelo TCP/IP) como funcionan. Los paquetes son enviados a través de un servicio que está asociada a un puerto. Un puerto es un simple número que TCP usa para enviar y recibir información. Ahora qué he resumido seis meses de escuela en un paragrafo, echa un vistazo a este diagrama:
Esta es una simple carta demostrando como ajustaremos nuestro servidor y cliente. El cliente conectara al servidor y establecerá una conexión de socket. El cliente enviara un hilo de datos al servidor. El servidor entonces lera los datos y notificara para qué este cierre la conexión. Eso es. Este modelo es conocido como un servidor Echo, debido a qué su función única es repetir el dato de vuelta al cliente. Primero, enciende Blender. Para evitar la molestia de ir paso a paso a través de crear un menú de juego, me tome la libertad de crear uno para propósitos demostrativos. Puedes descargar el archivo desde el link al final del artículo.
Echemos un vistazo a lo que tenemos aquí. En una nuez hay una cámara, tres entradas (servidor IP, puerto, y mensaje), junto a un botón de conexión. También hay un campo para la respuesta del servidor y un vacío que usaremos como interfaz controladora. Empecemos uniendo todo. Para tener algo de interacción con el juego, necesitaremos un cursor visible.
Necesitamos un script de Python que lo haga. Abre la ventana de Text Editor y crea un nuevo archivo llamado onGameLoad, py. Esto me dará una buena oportunidad para introducirte a la escritura Python, así el resto del tutorial no será tan intimidante. Con Python, podemos agregar casi ilimitadas funcionalidades a nuestros juegos. Los bloques lógicos son una agradable manera de agregar simple funcionabilidad sin conocer el código, pero puede volverse muy complejo cuando tratas de agregar una función que requiere más de veinte bloques de conexión. Usando Python podemos evadir duras labores a través de una red de conexiones de bloques, y entender más allá de limitaciones que los bloques lógicos sostienen.
Sin embargo, sin bloques lógicos no tendremos ninguna manera de llamar nuestros scripts en Python.
Volvamos al script onGameLoad, py. Queremos hacer un script que permita al cursor ser visto en el juego. El modulo Python que maneja esto es Rasterzer.
Para acceder a las funciones de Rastezer, necesitaremos primero importar el módulo en nuestro script. Después tendrás acceso a todas sus funciones. Puedes importar el script usando import (module). Para salvar el tipeado, usaremos la opción as(name). Esto ayuda tremendamente cuando creamos scripts muy largos que requieren tipear el nombre del modelo un montón.
Código:
import Rasterizer as r
.
Para mostrar el mouse en la pantalla, usaremos la función showMousse() así:
.
Nota qué para llamar showMouse() necesitamos hacer referencia al modulo primero. Para llamar cualquier función contenida en un modulo, te referirás a ellas en orden module, function(), subfunction().
Otra cosa para tomar nota es el atributo de función. En este caso, estoy usando 1 para referenciar un valor verdadero. Falso será representado con 0. De hecho, cualquier valor que no sea cero será Verdadero. También podríamos haber usado r, showMouse(True) y habría funcionado igual. Esto es asunto de elección personal, pero yo creo que tipear 1 o 0 es mucho más fácil que tipear Verdadero o Falso.
Ahora qué nuestro script está hecho, necesitamos crear algunos bloques lógicos para llamarlos. Los asignaremos a nuestro espacio vacío, llamado GameController. Selecciona el vacío y póngalo en la ventana de bloques lógicos F4. Agrega un sensor Always y desactiva pulsaciones de nivel Verdadero. Entonces agrega un controlador de Python. Aquí es donde entramos el name de el script a ser cargado. Conecta los puntos, y prueba el juego. Deberías poder ver tu cursor en la vista del juego.
Eso debería darte un buen entendimiento de cómo los scriptres Python son manejados en el motor de juego. Ahora salgamos del modo principiante y nos fijaremos en códigos más complejos. De aquí en adelante, no explicare todo como clica esto o apunta aquí, etc. Si estas seriamente buscando agregar trabajo de red en tus juegos, deberías tener un buen manejo de lo básico a estas alturas.
Parte 2: Ajustando el servidor Python.
Empecemos a correr nuestro servidor TCP de Python. Me gusta usar el editor de texto de Blender para editar mis scripts, mayormente porque me carga ir y volver entre aplicaciones. Ctrl+W salvará el archivo.blend y Alt+S salvará el archivo exterior de.blend. El siguiente script debe ser salvado fuera de.blend, pues será nuestra aplicación servidora. Intenté encontrar otra forma de correr el servidor fácilmente con el mismo juego, pero no pude encontrar ninguna. Correrlo fuera de Blender lo hará incluso más rápido en respuesta y manejo que las conexiones de cliente.
Echemos un vistazo al corazón de la red de trabajo, el servidor. Como mencione antes, vamos a usar sockets Python para hacer las conexiones entre cliente y servidor. Te mostrare el servidot TCP Python que usaremos, y junto al código lo dividiré en piezas fáciles de entender usando comentarios.
# Vamos a importar el socket modulo
Import socket.
# Asignar un número de puerto a una variable. Asignar valores como este hará más fácil cambiar, en lugar de tener que editar cada referencia por script en el puerto. Nota, usar un valor de puerto sobre 1024. Servicios populares, como un ftp, http, etc. Usa puertos de números bajos y esto ayudará a evitar conflictos.
.
# Aquí es donde crearemos nuestro socket. Asignaremos el socket a S llamando el módulo socket y socket función (). El socket de atributos() le dirá al socket para crear un tipo de socket de corriente desde el INET de familia de socket.
Código:
s = socket, socket(socket. AF_INET, socket. SOCK_STREAM)
.
# Luego, unimos el puerto al nombre del huesped. Esto le dirá al servidor que queremos usar.
# para usar el puerto especifico para escuchar.
Código:
s, bind((socket, gethostname(), port))
.
# Ahora digamosle al servidor que empiece a escuchar conexiones de clientes.
# Para propósitos de prueba, ajustaremos el servidor para manejar una conexión # simultanea. Puedes cambiar esto más tarde si quieres.
.
# Debido a qué este scriptr estará corriendo fuera del juego, como una ventana terminal más
# podemos usar imprimir() para mostrar que el servidor esta corriendo. Yo inclui # el valor de puerto en el comando para mostrar el puerto de número.
Código:
print Game server is running on port, port
.
# Este es nuestro loop principal para iniciar conexiones entrantes con el cliente.
# Las conexiones son aceptadas y el mensaje es guardado en una variable.
# La información de conexión esta escrita() en la ventana de terminal y el mensaje # es enviado de vuelta al cliente. Entonces la conexión se cierra.
Código:
while 1:
conn, addr = s, accept()
data = conn, recv(1024)
# display client connection info
print connected., addr, data
conn, send(data)
conn, close()
.
Salva este archivo como server, py. Si creaste el archivo dentro de Blender, usa ALT+S para salvarlo fuera del.blend. Entonces abre una ventana terminal, navega hasta el directorio donde salvaste el script del servidor, y exribe Python server, py para correr el servidor. Deberías ver algo así:
Código:
$ Python server, py
El servidor del juego corre en el puerto 2020.
Para matar propiamente el proceso, usa el comando top y busca el comando listado bajo command Python. Recuerda el PID junto a el y presiona la tecla qué para salir de top. Entonces usa el comando kill [pid] remplazando [pid] con el PID de el comando Python de arriba. Después de ser ejecutado, deberías notar un comando Terminated al final de tu servidor.
Parte 3: creando el cliente.
Ahora qué tenemos un servidor corriendo no hará mucho si no creamos un cliente. Usando el archivo.blend del artículo previo, ajustaremos unas cuantas cosas antes de crear el código del cliente. Lo primero, si miras al menú, notaras tres entradas: Server IP (entrada uno), Puerto (entrada dos), y mensaje (entrada tres) son usadas para crear las conexiones con el servidor. Actualmente no hay una forma fácil de entrar los datos para múltiples objetos de texto, así que, tendremos que escribirlos.
Antes de hacerlo, selecciona cada entrada y crea dos propiedades. La primera será llamada Text y la segunda llamada edit. Ajusta la primera entrada a Verdadero, y la otra a Falso. Esto nos dará un inicio. Voy a hacer un sumario del script, en lugar de llevarte paso a paso. Crearemos un gatillo que revise si un sensor (un sensor de teclado) es positivo. Si lo es, entonces crearemos una lista Python de los tres objetos entrantes. Entonces correremos un loop if/elif loop para ajustar las propiedades de edit de las entradas apropiadas de acuerdo a qué condiciones de estado sea Verdadero. Bastante simple.
Aquí está el script. Salvalo como: tabController, py en el editor de texto de Blender.
Código:
import GameLogic as gl
cont = gl, getCurrentController()
trigger = cont, getSensor(tabcheck)
if trigger, isPositive():
# get objects
input1 = gl, getCurrentScene(), getObjectList()[OBinput1] # server ip
input2 = gl, getCurrentScene(), getObjectList()[OBinput2] # port
number
input3 = gl, getCurrentScene(), getObjectList()[OBinput3] # message
proplist = [input1, input2, input3]
# server => port
if (proplist[0].edit == 1):
proplist[0].edit = 0
proplist[1].edit = 1
proplist[2].edit = 0
# port => message
elif (proplist[1].edit == 1):
proplist[0].edit = 0
proplist[1].edit = 0
proplist[2].edit = 1
# message => server
elif (proplist[2].edit == 1):
proplist[0].edit = 1
proplist[1].edit = 0
proplist[2].edit = 0
.
Selecciona el GameController vacío y ajusta el sensor de teclado. Para este ejercicio, estaré usando la tecla Return, simplemente porque usar la tecla Tab produce un @ lo cual arruina el texto. Puedes usar Tab si así lo quieres, pero deberás editar el script para remover el signo @ en el texto de objeto. Conecta el sensor de teclado a un controlador Python, y usa el archivo tabControllers, py.
Aquí es donde hay algunos problemas con el diseño de bloques lógicos. Lo que necesitamos hacer es crear dos sensores de mouse (sobre el mouse y el botón izquierdo), y cuando ambos sean positivos, un script Python se iniciara. Al principio pensaras bien, conéctalos ambos a un controlador Python. Esto no funcionara pues cada sensor de mouse actúa de manera independiente como si fueran una condición OR, y el script correra cada vez que el botón izquierdo del mouse sea presionado y cada vez que pases sobre el mouse sobre el botón. Necesitamos conectarlos a un controlador AND, pero ahora se nos presenta el problema de no poder correr el script directamente. Debido a qué no hay actuador Python, tendremos que usar el actuador Property para ajustar una propiedad bol, el cual gatillara un sensor de propieda el cual iniciara el script. Revisa la imagen debajopara ver cómo trabaja:
Sobre el script. Similar al scriptd el servidor, te mostrare el script que estamos usando, y luego usa comentarios en el script para explicar que está sucediendo.
# Debido a qué usamos las funciones del motor de juego de Blender, # necesitamos importar el módulo de lógica de juego.
Código:
import socket
import GameLogic as gl
.
# Este es otro problema con los bloques lógicos. Podemos inicializar los scripts usando el controlador Python, pero no podemos detenerlos cuando su sensor no es verdadero.
#Si nosotros no revisamos las propiedads, el script se correra dos veces (al cliquear y al soltar).
# Crearemos un loop para probar si la propiedad es verdadera, de esta manera solo correra una vez.
Código:
conprop = gl, getCurrentController(), getOwner()
if (conprop, connect == True):
.
# Ahora obtengamos el valor del texto de propiedades en el objeto de entrada, y asignemoslos a sus respectivas variables. Notese que tenemos que usar la función int() para los ajustes de puerto. De lo contrario, la variable es asignada como una cuerda y obtendrás un error cuando encuadernes el puerto porque no es un valor integro.
Código:
hostinput = gl, getCurrentScene(), getObjectList()[OBinput1]
host = hostinput. Text
portinput = gl, getCurrentScene(), getObjectList()[OBinput2]
portstring = portinput. Text
port = int(portstring)
.
# Al igual que con el servidor, crearemos un socket.
Código:
s = socket, socket(socket. AF_INET, socket. SOCK_STREAM)
.
# En lugar de escuchar por conexiones, las conectaremos al servidor.
Código:
s, connect((host, port))
.
# Cogeremos el mensaje de la entrada 3 y usaremos la función enviar() para enviarsela al servidor.
Código:
message = gl, getCurrentScene(), getObjectList()[OBinput3]
sendme = message. Text
s, send(sendme)
.
# Asigna la respuesta del servidor a una variable, para usarla más tarde y poder cerrar la conexión.
Código:
data = s, recv(1024)
s, close()
.
# Ahora podemos mostrar el eco de la respuesta del servidor como objeto de texto asignandoselo a su propiedad de texto.
Código:
resp = gl, getCurrentScene(), getObjectList()[OBresponseValue]
resp. Text = data
.
# Y finalmente, reseteamos la propiedad conectada de vuelta a falso.
Código:
conprop, connect = 0 Summary
.
Los probamos todos, enciende el servidor, y entonces juega el juego. Tipea en la IP del servidor, el número del puerto en que el servidor esta corriendo, y el mensaje que quieras enviar. Presiona conectar, y si todo salió bien, el servidor responderá con el eco del mensaje que fue mostrado en el área respuesta. Puedes ver las conexiones en la ventana terminal. Con suerte, esto te dará un conocimiento de cuan simples son las conexiones con socket, cómo puedes obtener una red de trabajo para tus juegos.