Hi, i create a small example of a multiplayer game, a player is a cube and it’s can be move.
i use the python socket lib, I would like to know if it was the right way to do what I want or not
the client code:
from direct.showbase.ShowBase import ShowBase
from panda3d.core import TextureStage, TexGenAttrib
import socket
import threading
# A class to handle the client side of the network communication
class Client:
def __init__(self):
# The IP address and port of the server
self.host = "127.0.0.1"
self.port = 9999
# The socket object to communicate with the server
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# A flag to indicate if the connection is established
self.connected = False
# A thread to receive messages from the server
self.recv_thread = None
# A method to connect to the server
def connect(self):
try:
# Connect to the server
self.sock.connect((self.host, self.port))
print("Connected to server")
# Set the connected flag to True
self.connected = True
# Start the receive thread
self.recv_thread = threading.Thread(target=self.receive)
self.recv_thread.start()
except Exception as e:
# Print any error message and close the socket
print(e)
self.sock.close()
# A method to send a message to the server
def send(self, msg):
if self.connected:
try:
# Encode the message as bytes and send it through the socket
data = msg.encode()
self.sock.send(data)
except Exception as e:
# Print any error message and close the socket and connection
print(e)
self.sock.close()
self.connected = False
# A method to receive messages from the server
def receive(self):
while self.connected:
try:
# Receive data from the server as bytes and decode it as a string
data = self.sock.recv(1024)
msg = data.decode()
# Split the message by commas and convert it to floats
x, y, z = map(float, msg.split(","))
# Set the position of the cube according to the message
app.cube.setPos(x, y, z)
except Exception as e:
# Print any error message and close the socket and connection
print(e)
self.sock.close()
self.connected = False
# A class to handle keyboard input and move an object
class KeyboardMovement:
def __init__(self, obj):
# The object to move
self.obj = obj
# The speed of movement and rotation
self.move_speed = 0.1
self.rotate_speed = 5
# The keys to press for each direction
self.keys = {
"forward": "w",
"backward": "s",
"left": "a",
"right": "d",
"up": "q",
"down": "e"
}
# A dictionary to store the state of each key
self.key_state = {}
for key in self.keys.values():
self.key_state[key] = False
# Accept the events for key press and release
for key in self.keys.values():
base.accept(key, self.setKeyState, [key, True])
base.accept(key + "-up", self.setKeyState, [key, False])
# A method to set the state of a key
def setKeyState(self, key, state):
# Set the state of the key in the dictionary
self.key_state[key] = state
# Send a message to the server with the key and state
msg = f"{key},{state}"
app.client.send(msg)
# A method to update the movement of the object
def update(self):
# Get the current position and orientation of the object
x, y, z = self.obj.getPos()
h, p, r = self.obj.getHpr()
# Move and rotate the object according to the key state
if self.key_state[self.keys["forward"]]:
y += self.move_speed
if self.key_state[self.keys["backward"]]:
y -= self.move_speed
if self.key_state[self.keys["left"]]:
x -= self.move_speed
if self.key_state[self.keys["right"]]:
x += self.move_speed
if self.key_state[self.keys["up"]]:
z += self.move_speed
if self.key_state[self.keys["down"]]:
z -= self.move_speed
# Set the new position and orientation of the object
self.obj.setPos(x, y, z)
# A class to handle the main application logic
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# Create a cube and load a cube map texture on it
self.cube = loader.loadModel("models/box")
tex = loader.loadCubeMap("models/skybox_#.png")
self.cube.setTexture(tex)
self.cube.reparentTo(render)
# Enable texture coordinate generation for the cube
ts = TextureStage.getDefault()
self.cube.setTexGen(ts, TexGenAttrib.MWorldCubeMap)
# Create a keyboard movement object for the cube
self.keyboard = KeyboardMovement(self.cube)
# Create a client object and connect to the server
self.client = Client()
self.client.connect()
# Add a task to update the keyboard movement every frame
self.taskMgr.add(self.keyboard.update, "keyboard_update")
# Create an instance of the application and run it
app = MyApp()
app.run()
the server code:
import socket
import select
# The IP address and port of the server
host = "127.0.0.1"
port = 9999
# The socket object to listen for incoming connections
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the address and port and start listening
server_sock.bind((host, port))
server_sock.listen()
print("Server listening on", host, port)
# A list of sockets to monitor for events
sockets_list = [server_sock]
# A dictionary to store the position of each client
clients_pos = {}
# A loop to handle network events
while True:
# Use select to get the list of sockets that are ready for read, write or error
read_sockets, write_sockets, error_sockets = select.select(sockets_list, [], sockets_list)
# Iterate over the read sockets
for sock in read_sockets:
# If the socket is the server socket, it means a new connection is requested
if sock == server_sock:
# Accept the connection and get the client socket and address
client_sock, client_addr = server_sock.accept()
# Add the client socket to the list of sockets to monitor
sockets_list.append(client_sock)
# Print a message to indicate a new connection
print("New connection from", client_addr)
# Initialize the position of the client to (0, 0, 0)
clients_pos[client_sock] = (0.0, 0.0, 0.0)
# Otherwise, it means an existing client has sent some data
else:
try:
# Receive data from the client as bytes and decode it as a string
data = sock.recv(1024)
msg = data.decode()
# Split the message by commas and convert it to floats or booleans
key, state = msg.split(",")
state = state == "True"
# Get the current position of the client from the dictionary
x, y ,z = clients_pos[sock]
# Update the position of the client according to
# the key and state
if key == "w" and state:
y += 0.1
if key == "s" and state:
y -= 0.1
if key == "a" and state:
x -= 0.1
if key == "d" and state:
x += 0.1
if key == "q" and state:
z += 0.1
if key == "e" and state:
z -= 0.1
# Set the new position of the client in the dictionary
clients_pos[sock] = (x, y, z)
# Format the position as a string with commas
pos_str = f"{x},{y},{z}"
# Encode the position as bytes and send it to the client
data = pos_str.encode()
sock.send(data)
except Exception as e:
# Print any error message and close the socket and connection
print(e)
sock.close()
# Remove the socket from the list of sockets to monitor
sockets_list.remove(sock)
# Remove the position of the client from the dictionary
clients_pos.pop(sock)
# Iterate over the error sockets
for sock in error_sockets:
# Close the socket and connection
sock.close()
# Remove the socket from the list of sockets to monitor
sockets_list.remove(sock)
# Remove the position of the client from the dictionary
clients_pos.pop(sock)