-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
152 lines (137 loc) · 5.33 KB
/
server.py
File metadata and controls
152 lines (137 loc) · 5.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import socket
import threading
class Server:
"""
A simple TCP server for multiplayer game synchronization.
Handles client connections, player state management, and broadcasting updates to all clients.
"""
def __init__(self, host="localhost", port=12345):
"""
Initialize the server and prepare to accept connections.
Args:
host (str): The IP address or hostname to bind the server. Defaults to "localhost".
port (int): The port number to listen on. Defaults to 12345.
"""
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.host = host
self.port = port
self.clients = []
self.players = {}
self.lock = threading.Lock()
self.running = True
def broadcast(self, message, exclude_socket=None):
"""
Send a message to all connected clients, optionally excluding one socket.
Args:
message (str): The message to broadcast.
exclude_socket (socket.socket, optional): A client socket to exclude from broadcast.
"""
with self.lock:
for client in self.clients[:]:
if client != exclude_socket:
try:
client.sendall(message.encode("utf-8"))
except Exception:
self.clients.remove(client)
if client in self.players:
del self.players[client]
client.close()
def handle_client(self, client_socket, address):
"""
Handle communication with a single client.
Processes JOIN and MOVE commands, updates player state,
and broadcasts changes to all clients.
Args:
client_socket (socket.socket): The client's socket.
address (tuple): The client's (IP, port) address.
"""
try:
while True:
data = client_socket.recv(1024).decode("utf-8")
if not data:
break
parts = data.split(",")
if parts[0] == "JOIN" and len(parts) == 2:
name = parts[1].strip()
with self.lock:
self.players[client_socket] = {"name": name, "x": 0, "y": 0}
join_msg = f"JOINED,{name}"
self.broadcast(join_msg, exclude_socket=None)
elif parts[0] == "MOVE" and len(parts) == 4:
name, x, y = parts[1], parts[2], parts[3]
try:
x = int(x)
y = int(y)
with self.lock:
self.players[client_socket] = {"name": name, "x": x, "y": y}
with self.lock:
all_data = ";".join(
f"{info['name']},{info['x']},{info['y']}"
for info in self.players.values()
)
self.broadcast(f"UPDATE;{all_data}")
except Exception:
continue
else:
continue
except Exception as e:
print(f"Error with {address}: {e}")
finally:
with self.lock:
if client_socket in self.clients:
self.clients.remove(client_socket)
player = self.players.pop(client_socket, None)
client_socket.close()
if player:
leave_msg = f"LEFT,{player['name']}"
self.broadcast(leave_msg, exclude_socket=None)
def start(self):
"""
Start the server and accept incoming connections.
For each new client, a separate thread is started to handle communication.
"""
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(5)
print("Server started.")
try:
while self.running:
try:
client_socket, address = self.server_socket.accept()
print("New connection from:", address)
except OSError:
break
with self.lock:
self.clients.append(client_socket)
threading.Thread(
target=self.handle_client,
args=(client_socket, address),
daemon=True,
).start()
finally:
print("Server shutting down.")
with self.lock:
for client in self.clients:
try:
client.close()
except Exception:
pass
self.clients.clear()
self.players.clear()
self.server_socket.close()
def stop(self):
"""
Stop the server and close all connections.
"""
self.running = False
self.server_socket.close()
if __name__ == "__main__":
server = None
try:
server = Server()
server.start()
except KeyboardInterrupt:
print("Stopping server...")
if server is not None:
server.stop()
print("Server stopped.")