Skip to content

Commit 0e456cb

Browse files
authored
Create server_diff_graceful.py
1 parent 88052c7 commit 0e456cb

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# server_diff_graceful.py
2+
import asyncio
3+
import json
4+
import websockets
5+
from difflib import SequenceMatcher
6+
7+
PORT = 8765
8+
9+
# ---------------- GLOBAL STATE ---------------- #
10+
document_content = "" # Shared document
11+
clients = {} # websocket -> username
12+
lock = asyncio.Lock() # Async lock for safe updates
13+
14+
# ---------------- DIFF HELPERS ---------------- #
15+
def apply_diff(content, ops):
16+
"""Apply a list of diff operations to content"""
17+
new_content = []
18+
for op in ops:
19+
tag = op["tag"]
20+
i1, i2, text = op["i1"], op["i2"], op.get("text", "")
21+
if tag == "equal":
22+
new_content.append(content[i1:i2])
23+
elif tag in ("replace", "insert"):
24+
new_content.append(text)
25+
elif tag == "delete":
26+
pass # skip deleted text
27+
return "".join(new_content)
28+
29+
# ---------------- BROADCAST ---------------- #
30+
async def broadcast(message, exclude=None):
31+
"""Send a message to all clients except the sender"""
32+
if clients:
33+
websockets_to_send = [ws for ws in clients if ws != exclude]
34+
if websockets_to_send:
35+
await asyncio.gather(*(ws.send(message) for ws in websockets_to_send))
36+
37+
# ---------------- HANDLER ---------------- #
38+
async def handler(websocket):
39+
global document_content
40+
# Receive initial username
41+
username = await websocket.recv()
42+
clients[websocket] = username
43+
print(f"[+] {username} connected")
44+
45+
# Send full document to new client
46+
await websocket.send(json.dumps({"type": "full_update", "content": document_content}))
47+
48+
try:
49+
async for message in websocket:
50+
data = json.loads(message)
51+
52+
async with lock:
53+
if data["type"] == "diff":
54+
# Apply diff to server content
55+
document_content = apply_diff(document_content, data["ops"])
56+
# Broadcast diff to others
57+
await broadcast(json.dumps({"type": "diff", "ops": data["ops"], "user": username}), exclude=websocket)
58+
59+
elif data["type"] == "cursor":
60+
# Broadcast cursor positions to other clients
61+
await broadcast(json.dumps({
62+
"type": "cursor",
63+
"position": data["position"],
64+
"user": username
65+
}), exclude=websocket)
66+
67+
except websockets.ConnectionClosed:
68+
print(f"[-] {username} disconnected")
69+
finally:
70+
if websocket in clients:
71+
del clients[websocket]
72+
73+
# ---------------- START SERVER ---------------- #
74+
async def main():
75+
async with websockets.serve(handler, "0.0.0.0", PORT):
76+
print(f"Diff-Based Collaborative Server running on port {PORT}")
77+
await asyncio.Future() # run forever
78+
79+
if __name__ == "__main__":
80+
try:
81+
asyncio.run(main())
82+
except KeyboardInterrupt:
83+
print("\nServer stopped gracefully.")

0 commit comments

Comments
 (0)