server refactor

This commit is contained in:
MeexReay 2024-12-22 00:59:32 +03:00
parent f3c592b6d8
commit 3a6df05fb7
16 changed files with 801 additions and 341 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

35
server/block.py Normal file
View file

@ -0,0 +1,35 @@
from config import SPAWN
class Block:
def __init__(self, x, y, block_type, color, collides):
self.x = x
self.y = y
self.type = block_type
self.color = color
self.collides = collides
async def tick(self):
pass
async def render(self):
pass
async def onCollide(self, player, x, y):
if x != 0: player.vel_x = self.x + x - player.x
if y != 0: player.vel_y = self.y + y - player.y
if x != 0 or y != 0: # special blocks
if self.type == "jump_boost":
player.setGravitySpeed(1.25)
player.setJumpSpeed(5)
player.on_ground = True
elif player.jump_speed != 2:
player.setGravitySpeed(0.5)
player.setJumpSpeed(2)
if self.type == "killer":
await player.setPos(*SPAWN)
await player.sendToPlayers()
def toStatement(self, add=True):
return f"B1{self.x},{self.y},{int(self.collides)},{self.type},{self.color}" if add else f"B0{self.x},{self.y}"

16
server/config.py Normal file
View file

@ -0,0 +1,16 @@
import sys
HOST,PORT = sys.argv[1].split(":")
PORT = int(PORT)
COLORS = ["#d22", "#2d2", "#22d", "#dd2", "#2dd", "#d2d", "#ddd"]
SPAWN = (0, 0)
REACH_DISTANCE = 15
BLOCK_TYPES = {
"normal": "#555",
"jump_boost": "#2d2",
"killer": "#d22",
}

View file

@ -1,190 +1,9 @@
from websockets.server import serve, ServerConnection
import random, sys, asyncio, time
class Block: import asyncio, time
def __init__(self, x, y, block_type, color, collides): from network import startServer
self.x = x from player import Player
self.y = y from block import Block
self.type = block_type from config import *
self.color = color
self.collides = collides
async def tick(self):
pass
async def render(self):
pass
async def onCollide(self, player, x, y):
if x != 0: player.vel_x = self.x + x - player.x
if y != 0: player.vel_y = self.y + y - player.y
if x != 0 or y != 0: # special blocks
if self.type == "jump_boost":
player.jump_speed = 5
player.gravity_speed = 1.25
player.on_ground = True
await writePacket(player.websocket, "S", ["J", "5"])
await writePacket(player.websocket, "S", ["G", "1.25"])
elif player.jump_speed != 2:
player.jump_speed = 2
player.gravity_speed = 0.5
await writePacket(player.websocket, "S", ["J", "2"])
await writePacket(player.websocket, "S", ["G", "0.5"])
if self.type == "killer":
await player.setPos(*SPAWN)
await player.sendToPlayers()
def toStatement(self, add=True):
return f"B1{self.x},{self.y},{int(self.collides)},{self.type},{self.color}" if add else f"B0{self.x},{self.y}"
class Player(Block):
def __init__(self, websocket, x=None, y=None, name=None, color=None, vel_x=None, vel_y=None):
super().__init__(x, y, None, color, True)
self.x = x
self.y = y
self.name = name
self.color = color
self.vel_x = vel_x
self.vel_y = vel_y
self.websocket = websocket
self.controls_x = 0
self.controls_jump = False
self.on_ground = False
self.walk_speed = 1
self.jump_speed = 2
self.gravity_speed = 0.5
self.last_
async def setWalkSpeed(self, speed):
await writePacket(self.websocket, "S", ["W", str(speed)])
self.walk_speed = speed
async def setGravitySpeed(self, speed):
await writePacket(self.websocket, "S", ["G", str(speed)])
self.gravity_speed = speed
async def setJumpSpeed(self, speed):
await writePacket(self.websocket, "S", ["J", str(speed)])
self.jump_speed = speed
async def sendName(self, name):
await writePacket(self.websocket, "N", [name])
async def setName(self, name):
self.name = name
await self.sendName(name)
async def setColor(self, color):
self.color = color
await writePacket(self.websocket, "C", [color])
async def setVel(self, x, y):
if x == self.vel_x and y == self.vel_y: return
self.vel_x = x
self.vel_y = y
await self.sendVel(x, y)
async def setPos(self, x, y):
if x == self.x and y == self.y: return
self.x = x
self.y = y
await self.sendPos(x, y)
async def sendVel(self, x, y):
await writePacket(self.websocket, "V", [str(x), str(y)])
async def sendPos(self, x, y):
await writePacket(self.websocket, "P", [str(x), str(y)])
async def sendMessage(self, message):
await writePacket(self.websocket, "M", message.split("\n"))
async def sendWorld(self, statements):
if len(statements) == 0: return
await writePacket(self.websocket, "W", statements)
async def sendBlockTypes(self, types):
await writePacket(self.websocket, "B", types)
async def sendToPlayers(self):
for p in getPlayers():
if p != self:
await p.sendWorld([self.toStatement()])
async def tick(self):
self.x = round(self.x * 100) / 100
self.y = round(self.y * 100) / 100
self.vel_x = round(self.vel_x * 100) / 100
self.vel_y = round(self.vel_y * 100) / 100
if not self.on_ground:
self.vel_y -= self.gravity_speed
await self.collide()
async def collide(self):
global WORLD
self.on_ground = False
for block in WORLD:
if not block.collides: continue
if block == self: continue
collide_x = 0
collide_y = 0
if self.x > block.x-1 and self.x < block.x+1:
if self.y > block.y and self.y + self.vel_y - 1 < block.y:
self.on_ground = True
collide_y = 1
if self.y < block.y and self.y + self.vel_y > block.y - 1:
collide_y = -1
if self.y > block.y-1 and self.y < block.y+1:
if self.x > block.x and self.x + self.vel_x - 1 < block.x:
collide_x = 1
if self.x < block.x and self.x + self.vel_x > block.x - 1:
collide_x = -1
await block.onCollide(self, collide_x, collide_y)
async def onCollide(self, player, x, y):
await super().onCollide(player, x, y)
# if x != 0:
# player.vel_x *= 0.5
# self.vel_x = player.vel_x
# if y != 0:
# player.vel_y *= 0.5
# self.vel_y = player.vel_y
# pass
async def render(self):
self.vel_x *= 0.5
self.vel_y *= 0.5
self.x += self.vel_x
self.y += self.vel_y
# await self.setVel(self.vel_x * 0.5, self.vel_y * 0.5)
# await self.setPos(self.x + self.vel_x, self.y + self.vel_y)
return self.vel_x != 0 or self.vel_y != 0
async def keepAlive(self):
await writePacket(self.websocket, "R", [str(self.x), str(self.y), str(self.vel_x), str(self.vel_y)])
def toStatement(self, add=True):
return f"P1{self.name},{self.x},{self.y},{self.vel_x},{self.vel_y},{self.color}" if add else f"P0{self.name}"
def getPlayers(): def getPlayers():
global WORLD global WORLD
@ -202,140 +21,6 @@ def getPlayer(name):
def current_milli_time(): def current_milli_time():
return round(time.time() * 1000) return round(time.time() * 1000)
async def readPacket(websocket: ServerConnection) -> tuple[str, list[str]]:
data = await websocket.recv()
id,data = data[0], data[1:].splitlines()
print(id, data)
return id,data
async def writePacket(websocket: ServerConnection, packet_id: str, packet_data: list[str]):
await websocket.send(packet_id + ("\n".join(packet_data)))
async def handler(websocket: ServerConnection):
packet_id, packet_data = await readPacket(websocket)
name = packet_data[0]
if packet_id != "J":
await writePacket(websocket, "K", ["join packet is invalid"])
return
if getPlayer(name) != None:
await writePacket(websocket, "K", ["this nickname is already in use"])
return
print(name, "joined to the server")
try:
player = Player(websocket)
await player.sendWorld([b.toStatement() for b in WORLD])
await player.sendBlockTypes(BLOCK_TYPES.keys())
await player.setColor(random.choice(COLORS))
await player.setPos(*SPAWN)
await player.setVel(0,0)
await player.setName(name)
await player.setWalkSpeed(0.5)
await player.sendToPlayers()
WORLD.append(player)
while True:
packet_id, packet_data = await readPacket(websocket)
if packet_id == "V":
vel_x, vel_y = float(packet_data[0]), float(packet_data[1])
vel_x = max(min(vel_x, player.walk_speed), -player.walk_speed)
vel_y = max(min(vel_y, player.jump_speed), 0)
player.vel_x += vel_x
if player.on_ground:
player.vel_y += vel_y
player.on_ground = False
await player.sendToPlayers()
if packet_id == "K":
key,pressed = packet_data
pressed = pressed == "1"
if key == "KeyR" and pressed:
await player.setPos(SPAWN[0], SPAWN[1])
if key == "ShiftLeft":
if pressed:
await player.setWalkSpeed(1)
else:
await player.setWalkSpeed(0.5)
if packet_id == "M":
message = packet_data[0]
message = f"{name} > {message}"
for p in getPlayers():
await p.sendMessage(message)
print(message)
if packet_id == "D":
x,y = packet_data
x,y = int(x),int(y)
block = None
for i in WORLD:
if type(i) == Player:
continue
if i.x == x and i.y == y:
block = i
break
if not block: continue
if block.type == "spawn": continue # spawn block protection
if abs(x - player.x) ** 2 + abs(y - player.y) ** 2 > REACH_DISTANCE ** 2:
continue
WORLD.remove(block)
for p in getPlayers():
await p.sendWorld([block.toStatement(False)])
if packet_id == "P":
x,y,block_type = packet_data
x,y = int(x),int(y)
if block_type not in BLOCK_TYPES:
continue
if abs(x - player.x) ** 2 + abs(y - player.y) ** 2 > REACH_DISTANCE ** 2:
continue
found_block = False
for i in WORLD:
if type(i) == Player:
continue
if i.x == x and i.y == y:
found_block = True
break
if found_block: continue
block = Block(x,y,block_type,BLOCK_TYPES[block_type],True)
WORLD.append(block)
for p in getPlayers():
await p.sendWorld([block.toStatement()])
except Exception as exc:
WORLD.remove(player)
for p in getPlayers():
await p.sendWorld([player.toStatement(False)])
await writePacket(websocket, "K", [str(exc)])
print(name, "left the server")
async def tickTimer(): async def tickTimer():
while True: while True:
for b in WORLD: for b in WORLD:
@ -358,25 +43,7 @@ async def main():
asyncio.get_event_loop().create_task(tickTimer()) asyncio.get_event_loop().create_task(tickTimer())
asyncio.get_event_loop().create_task(keepAliveTimer()) asyncio.get_event_loop().create_task(keepAliveTimer())
asyncio.get_event_loop().create_task(renderTimer()) asyncio.get_event_loop().create_task(renderTimer())
async with serve(handler, HOST, PORT) as server: await startServer(HOST, PORT, getPlayer, getPlayers, WORLD)
print(f"started server on {HOST}:{PORT}")
await asyncio.get_running_loop().create_future()
HOST,PORT = sys.argv[1].split(":")
PORT = int(PORT)
COLORS = ["#d22", "#2d2", "#22d", "#dd2", "#2dd", "#d2d", "#ddd"]
SPAWN = (0, 0)
REACH_DISTANCE = 15
BLOCK_TYPES = {
"normal": "#555",
"jump_boost": "#2d2",
"killer": "#d22",
}
WORLD = [ WORLD = [
Block(-1, -1, "normal", "#555", True), Block(-1, -1, "normal", "#555", True),
@ -384,7 +51,5 @@ WORLD = [
Block(1, -1, "normal", "#555", True) Block(1, -1, "normal", "#555", True)
] ]
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) asyncio.run(main())

61
server/network.py Normal file
View file

@ -0,0 +1,61 @@
from websockets.server import serve, ServerConnection
import asyncio
from config import BLOCK_TYPES, COLORS, SPAWN
from main import WORLD, getPlayer, getPlayers
from player import Player
import random
async def startServer(host, port):
async with serve(handler, host, port):
print(f"started server on {host}:{port}")
await asyncio.get_running_loop().create_future()
async def readPacket(websocket: ServerConnection) -> tuple[str, list[str]]:
data = await websocket.recv()
return data[0], data[1:].splitlines()
async def writePacket(websocket: ServerConnection, packet_id: str, packet_data: list[str]):
await websocket.send(packet_id + ("\n".join(packet_data)))
async def handler(websocket: ServerConnection):
packet_id, packet_data = await readPacket(websocket)
name = packet_data[0]
if packet_id != "J":
await writePacket(websocket, "K", ["join packet is invalid"])
return
if getPlayer(name) != None:
await writePacket(websocket, "K", ["this nickname is already in use"])
return
print(name, "joined to the server")
try:
player = Player(websocket)
await player.sendWorld([b.toStatement() for b in WORLD])
await player.sendBlockTypes(BLOCK_TYPES.keys())
await player.setColor(random.choice(COLORS))
await player.setPos(*SPAWN)
await player.setVel(0,0)
await player.setName(name)
await player.setWalkSpeed(0.5)
await player.sendToPlayers()
WORLD.append(player)
while True:
packet_id, packet_data = await readPacket(websocket)
player.onPacket(packet_id, packet_data)
except Exception as exc:
WORLD.remove(player)
for p in getPlayers():
await p.sendWorld([player.toStatement(False)])
await writePacket(websocket, "K", [str(exc)])
print(name, "left the server")

255
server/player.py Normal file
View file

@ -0,0 +1,255 @@
from block import Block
from network import writePacket
from config import SPAWN, REACH_DISTANCE, BLOCK_TYPES
from main import getPlayers, WORLD
import time
class Player(Block):
def __init__(self, websocket, x=None, y=None, name=None, color=None, vel_x=None, vel_y=None):
super().__init__(x, y, None, color, True)
self.x = x
self.y = y
self.name = name
self.color = color
self.vel_x = vel_x
self.vel_y = vel_y
self.websocket = websocket
self.on_ground = False
self.walk_speed = 1
self.jump_speed = 2
self.gravity_speed = 0.5
self.lsd_pos = (x, y)
self.lsd_time = time.time()
async def sendPacket(self, packet_id, packet_data):
await writePacket(self.websocket, packet_id, packet_data)
async def onPacket(self, packet_id, packet_data):
if packet_id == "V":
vel_x, vel_y = float(packet_data[0]), float(packet_data[1])
vel_x = max(min(vel_x, self.walk_speed), -self.walk_speed)
vel_y = max(min(vel_y, self.jump_speed), 0)
self.vel_x += vel_x
if self.on_ground:
self.vel_y += vel_y
self.on_ground = False
await self.sendToPlayers()
if packet_id == "X":
x, y = float(packet_data[0]), float(packet_data[1])
ticks = (time.time() - self.lsd_time) * 20
rx, ry = abs(x - self.lsd_pos[0]), y - self.lsd_pos[1]
if rx > self.walk_speed * ticks: return
if ry < 0 and abs(ry) > self.gravity_speed * ticks: return
if ry > 0 and ry > self.jump_speed * ticks: return
self.x = x
self.y = y
self.lsd_pos = (x, y)
self.lsd_time = time.time()
await self.sendToPlayers()
if packet_id == "K":
key,pressed = packet_data
pressed = pressed == "1"
if key == "KeyR" and pressed:
await self.setPos(SPAWN[0], SPAWN[1])
if key == "ShiftLeft":
if pressed:
await self.setWalkSpeed(1)
else:
await self.setWalkSpeed(0.5)
if packet_id == "M":
message = packet_data[0]
message = f"{self.name} > {message}"
for p in getPlayers():
await p.sendMessage(message)
print(message)
if packet_id == "D":
x,y = packet_data
x,y = int(x),int(y)
block = None
for i in WORLD:
if type(i) == Player:
continue
if i.x == x and i.y == y:
block = i
break
if not block: return
if block.type == "spawn": return # spawn block protection
if abs(x - self.x) ** 2 + abs(y - self.y) ** 2 > REACH_DISTANCE ** 2:
return
WORLD.remove(block)
for p in getPlayers():
await p.sendWorld([block.toStatement(False)])
if packet_id == "P":
x,y,block_type = packet_data
x,y = int(x),int(y)
if block_type not in BLOCK_TYPES:
return
if abs(x - self.x) ** 2 + abs(y - self.y) ** 2 > REACH_DISTANCE ** 2:
return
found_block = False
for i in WORLD:
if type(i) == Player:
continue
if i.x == x and i.y == y:
found_block = True
break
if found_block: return
block = Block(x,y,block_type,BLOCK_TYPES[block_type],True)
WORLD.append(block)
for p in getPlayers():
await p.sendWorld([block.toStatement()])
async def setWalkSpeed(self, speed):
await writePacket(self.websocket, "S", ["W", str(speed)])
self.walk_speed = speed
async def setGravitySpeed(self, speed):
await writePacket(self.websocket, "S", ["G", str(speed)])
self.gravity_speed = speed
async def setJumpSpeed(self, speed):
await writePacket(self.websocket, "S", ["J", str(speed)])
self.jump_speed = speed
async def sendName(self, name):
await writePacket(self.websocket, "N", [name])
async def setName(self, name):
self.name = name
await self.sendName(name)
async def setColor(self, color):
self.color = color
await writePacket(self.websocket, "C", [color])
async def setVel(self, x, y):
if x == self.vel_x and y == self.vel_y: return
self.vel_x = x
self.vel_y = y
await self.sendVel(x, y)
async def setPos(self, x, y):
if x == self.x and y == self.y: return
self.x = x
self.y = y
await self.sendPos(x, y)
async def sendVel(self, x, y):
await writePacket(self.websocket, "V", [str(x), str(y)])
async def sendPos(self, x, y):
await writePacket(self.websocket, "P", [str(x), str(y)])
async def sendMessage(self, message):
await writePacket(self.websocket, "M", message.split("\n"))
async def sendWorld(self, statements):
if len(statements) == 0: return
await writePacket(self.websocket, "W", statements)
async def sendBlockTypes(self, types):
await writePacket(self.websocket, "B", types)
async def sendToPlayers(self):
for p in getPlayers():
if p != self:
await p.sendWorld([self.toStatement()])
async def tick(self):
self.x = round(self.x * 100) / 100
self.y = round(self.y * 100) / 100
self.vel_x = round(self.vel_x * 100) / 100
self.vel_y = round(self.vel_y * 100) / 100
if not self.on_ground:
self.vel_y -= self.gravity_speed
await self.collide()
async def collide(self):
global WORLD
self.on_ground = False
for block in WORLD:
if not block.collides: continue
if block == self: continue
collide_x = 0
collide_y = 0
if self.x > block.x-1 and self.x < block.x+1:
if self.y > block.y and self.y + self.vel_y - 1 < block.y:
self.on_ground = True
collide_y = 1
if self.y < block.y and self.y + self.vel_y > block.y - 1:
collide_y = -1
if self.y > block.y-1 and self.y < block.y+1:
if self.x > block.x and self.x + self.vel_x - 1 < block.x:
collide_x = 1
if self.x < block.x and self.x + self.vel_x > block.x - 1:
collide_x = -1
await block.onCollide(self, collide_x, collide_y)
async def onCollide(self, player, x, y):
await super().onCollide(player, x, y)
# if x != 0:
# player.vel_x *= 0.5
# self.vel_x = player.vel_x
# if y != 0:
# player.vel_y *= 0.5
# self.vel_y = player.vel_y
# pass
async def render(self):
self.vel_x *= 0.5
self.vel_y *= 0.5
self.x += self.vel_x
self.y += self.vel_y
# await self.setVel(self.vel_x * 0.5, self.vel_y * 0.5)
# await self.setPos(self.x + self.vel_x, self.y + self.vel_y)
return self.vel_x != 0 or self.vel_y != 0
async def keepAlive(self):
await writePacket(self.websocket, "R", [str(self.x), str(self.y), str(self.vel_x), str(self.vel_y)])
def toStatement(self, add=True):
return f"P1{self.name},{self.x},{self.y},{self.vel_x},{self.vel_y},{self.color}" if add else f"P0{self.name}"

35
server/src/block.py Normal file
View file

@ -0,0 +1,35 @@
from config import SPAWN
class Block:
def __init__(self, x, y, block_type, color, collides):
self.x = x
self.y = y
self.type = block_type
self.color = color
self.collides = collides
async def tick(self):
pass
async def render(self):
pass
async def onCollide(self, player, x, y):
if x != 0: player.vel_x = self.x + x - player.x
if y != 0: player.vel_y = self.y + y - player.y
if x != 0 or y != 0: # special blocks
if self.type == "jump_boost":
player.setGravitySpeed(1.25)
player.setJumpSpeed(5)
player.on_ground = True
elif player.jump_speed != 2:
player.setGravitySpeed(0.5)
player.setJumpSpeed(2)
if self.type == "killer":
await player.setPos(*SPAWN)
await player.sendToPlayers()
def toStatement(self, add=True):
return f"B1{self.x},{self.y},{int(self.collides)},{self.type},{self.color}" if add else f"B0{self.x},{self.y}"

16
server/src/config.py Normal file
View file

@ -0,0 +1,16 @@
import sys
HOST,PORT = sys.argv[1].split(":")
PORT = int(PORT)
COLORS = ["#d22", "#2d2", "#22d", "#dd2", "#2dd", "#d2d", "#ddd"]
SPAWN = (0, 0)
REACH_DISTANCE = 15
BLOCK_TYPES = {
"normal": "#555",
"jump_boost": "#2d2",
"killer": "#d22",
}

55
server/src/main.py Normal file
View file

@ -0,0 +1,55 @@
import asyncio, time
from network import startServer
from player import Player
from block import Block
from config import *
def getPlayers():
global WORLD
for b in WORLD:
if type(b) == Player:
yield b
def getPlayer(name):
global WORLD
for b in WORLD:
if type(b) == Player:
if b.name == name:
return b
def current_milli_time():
return round(time.time() * 1000)
async def tickTimer():
while True:
for b in WORLD:
await b.tick()
await asyncio.sleep(1/20)
async def keepAliveTimer():
while True:
for b in getPlayers():
await b.keepAlive()
await asyncio.sleep(1)
async def renderTimer():
while True:
for p in getPlayers():
await p.sendWorld([b.toStatement() for b in WORLD if await b.render() and b != p])
await asyncio.sleep(1/60)
async def main():
asyncio.get_event_loop().create_task(tickTimer())
asyncio.get_event_loop().create_task(keepAliveTimer())
asyncio.get_event_loop().create_task(renderTimer())
await startServer(HOST, PORT)
WORLD = [
Block(-1, -1, "normal", "#555", True),
Block(0, -1, "spawn", "#2ad", True),
Block(1, -1, "normal", "#555", True)
]
if __name__ == "__main__":
asyncio.run(main())

61
server/src/network.py Normal file
View file

@ -0,0 +1,61 @@
from websockets.server import serve, ServerConnection
import asyncio
from config import BLOCK_TYPES, COLORS, SPAWN
from main import WORLD, getPlayer, getPlayers
from player import Player
import random
async def startServer(host, port):
async with serve(handler, host, port):
print(f"started server on {host}:{port}")
await asyncio.get_running_loop().create_future()
async def readPacket(websocket: ServerConnection) -> tuple[str, list[str]]:
data = await websocket.recv()
return data[0], data[1:].splitlines()
async def writePacket(websocket: ServerConnection, packet_id: str, packet_data: list[str]):
await websocket.send(packet_id + ("\n".join(packet_data)))
async def handler(websocket: ServerConnection):
packet_id, packet_data = await readPacket(websocket)
name = packet_data[0]
if packet_id != "J":
await writePacket(websocket, "K", ["join packet is invalid"])
return
if getPlayer(name) != None:
await writePacket(websocket, "K", ["this nickname is already in use"])
return
print(name, "joined to the server")
try:
player = Player(websocket)
await player.sendWorld([b.toStatement() for b in WORLD])
await player.sendBlockTypes(BLOCK_TYPES.keys())
await player.setColor(random.choice(COLORS))
await player.setPos(*SPAWN)
await player.setVel(0,0)
await player.setName(name)
await player.setWalkSpeed(0.5)
await player.sendToPlayers()
WORLD.append(player)
while True:
packet_id, packet_data = await readPacket(websocket)
player.onPacket(packet_id, packet_data)
except Exception as exc:
WORLD.remove(player)
for p in getPlayers():
await p.sendWorld([player.toStatement(False)])
await writePacket(websocket, "K", [str(exc)])
print(name, "left the server")

255
server/src/player.py Normal file
View file

@ -0,0 +1,255 @@
from block import Block
from network import writePacket
from config import SPAWN, REACH_DISTANCE, BLOCK_TYPES
from main import getPlayers, WORLD
import time
class Player(Block):
def __init__(self, websocket, x=None, y=None, name=None, color=None, vel_x=None, vel_y=None):
super().__init__(x, y, None, color, True)
self.x = x
self.y = y
self.name = name
self.color = color
self.vel_x = vel_x
self.vel_y = vel_y
self.websocket = websocket
self.on_ground = False
self.walk_speed = 1
self.jump_speed = 2
self.gravity_speed = 0.5
self.lsd_pos = (x, y)
self.lsd_time = time.time()
async def sendPacket(self, packet_id, packet_data):
await writePacket(self.websocket, packet_id, packet_data)
async def onPacket(self, packet_id, packet_data):
if packet_id == "V":
vel_x, vel_y = float(packet_data[0]), float(packet_data[1])
vel_x = max(min(vel_x, self.walk_speed), -self.walk_speed)
vel_y = max(min(vel_y, self.jump_speed), 0)
self.vel_x += vel_x
if self.on_ground:
self.vel_y += vel_y
self.on_ground = False
await self.sendToPlayers()
if packet_id == "X":
x, y = float(packet_data[0]), float(packet_data[1])
ticks = (time.time() - self.lsd_time) * 20
rx, ry = abs(x - self.lsd_pos[0]), y - self.lsd_pos[1]
if rx > self.walk_speed * ticks: return
if ry < 0 and abs(ry) > self.gravity_speed * ticks: return
if ry > 0 and ry > self.jump_speed * ticks: return
self.x = x
self.y = y
self.lsd_pos = (x, y)
self.lsd_time = time.time()
await self.sendToPlayers()
if packet_id == "K":
key,pressed = packet_data
pressed = pressed == "1"
if key == "KeyR" and pressed:
await self.setPos(SPAWN[0], SPAWN[1])
if key == "ShiftLeft":
if pressed:
await self.setWalkSpeed(1)
else:
await self.setWalkSpeed(0.5)
if packet_id == "M":
message = packet_data[0]
message = f"{self.name} > {message}"
for p in getPlayers():
await p.sendMessage(message)
print(message)
if packet_id == "D":
x,y = packet_data
x,y = int(x),int(y)
block = None
for i in WORLD:
if type(i) == Player:
continue
if i.x == x and i.y == y:
block = i
break
if not block: return
if block.type == "spawn": return # spawn block protection
if abs(x - self.x) ** 2 + abs(y - self.y) ** 2 > REACH_DISTANCE ** 2:
return
WORLD.remove(block)
for p in getPlayers():
await p.sendWorld([block.toStatement(False)])
if packet_id == "P":
x,y,block_type = packet_data
x,y = int(x),int(y)
if block_type not in BLOCK_TYPES:
return
if abs(x - self.x) ** 2 + abs(y - self.y) ** 2 > REACH_DISTANCE ** 2:
return
found_block = False
for i in WORLD:
if type(i) == Player:
continue
if i.x == x and i.y == y:
found_block = True
break
if found_block: return
block = Block(x,y,block_type,BLOCK_TYPES[block_type],True)
WORLD.append(block)
for p in getPlayers():
await p.sendWorld([block.toStatement()])
async def setWalkSpeed(self, speed):
await writePacket(self.websocket, "S", ["W", str(speed)])
self.walk_speed = speed
async def setGravitySpeed(self, speed):
await writePacket(self.websocket, "S", ["G", str(speed)])
self.gravity_speed = speed
async def setJumpSpeed(self, speed):
await writePacket(self.websocket, "S", ["J", str(speed)])
self.jump_speed = speed
async def sendName(self, name):
await writePacket(self.websocket, "N", [name])
async def setName(self, name):
self.name = name
await self.sendName(name)
async def setColor(self, color):
self.color = color
await writePacket(self.websocket, "C", [color])
async def setVel(self, x, y):
if x == self.vel_x and y == self.vel_y: return
self.vel_x = x
self.vel_y = y
await self.sendVel(x, y)
async def setPos(self, x, y):
if x == self.x and y == self.y: return
self.x = x
self.y = y
await self.sendPos(x, y)
async def sendVel(self, x, y):
await writePacket(self.websocket, "V", [str(x), str(y)])
async def sendPos(self, x, y):
await writePacket(self.websocket, "P", [str(x), str(y)])
async def sendMessage(self, message):
await writePacket(self.websocket, "M", message.split("\n"))
async def sendWorld(self, statements):
if len(statements) == 0: return
await writePacket(self.websocket, "W", statements)
async def sendBlockTypes(self, types):
await writePacket(self.websocket, "B", types)
async def sendToPlayers(self):
for p in getPlayers():
if p != self:
await p.sendWorld([self.toStatement()])
async def tick(self):
self.x = round(self.x * 100) / 100
self.y = round(self.y * 100) / 100
self.vel_x = round(self.vel_x * 100) / 100
self.vel_y = round(self.vel_y * 100) / 100
if not self.on_ground:
self.vel_y -= self.gravity_speed
await self.collide()
async def collide(self):
global WORLD
self.on_ground = False
for block in WORLD:
if not block.collides: continue
if block == self: continue
collide_x = 0
collide_y = 0
if self.x > block.x-1 and self.x < block.x+1:
if self.y > block.y and self.y + self.vel_y - 1 < block.y:
self.on_ground = True
collide_y = 1
if self.y < block.y and self.y + self.vel_y > block.y - 1:
collide_y = -1
if self.y > block.y-1 and self.y < block.y+1:
if self.x > block.x and self.x + self.vel_x - 1 < block.x:
collide_x = 1
if self.x < block.x and self.x + self.vel_x > block.x - 1:
collide_x = -1
await block.onCollide(self, collide_x, collide_y)
async def onCollide(self, player, x, y):
await super().onCollide(player, x, y)
# if x != 0:
# player.vel_x *= 0.5
# self.vel_x = player.vel_x
# if y != 0:
# player.vel_y *= 0.5
# self.vel_y = player.vel_y
# pass
async def render(self):
self.vel_x *= 0.5
self.vel_y *= 0.5
self.x += self.vel_x
self.y += self.vel_y
# await self.setVel(self.vel_x * 0.5, self.vel_y * 0.5)
# await self.setPos(self.x + self.vel_x, self.y + self.vel_y)
return self.vel_x != 0 or self.vel_y != 0
async def keepAlive(self):
await writePacket(self.websocket, "R", [str(self.x), str(self.y), str(self.vel_x), str(self.vel_y)])
def toStatement(self, add=True):
return f"P1{self.name},{self.x},{self.y},{self.vel_x},{self.vel_y},{self.color}" if add else f"P0{self.name}"

6
server/start.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/sh
HOST=localhost
PORT=8000
python3 src/main.py $HOST:$PORT