From 82b03165dcd4048b5a417a77f716e3f3adaf653a Mon Sep 17 00:00:00 2001
From: MeexReay <127148610+MeexReay@users.noreply.github.com>
Date: Sun, 22 Dec 2024 01:31:53 +0300
Subject: [PATCH 1/4] Delete index.html
---
index.html | 82 ------------------------------------------------------
1 file changed, 82 deletions(-)
delete mode 100644 index.html
diff --git a/index.html b/index.html
deleted file mode 100644
index 34f0d25..0000000
--- a/index.html
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
- cubic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Cubic
-
игра кубик переделанная на js
-
-
-
-
-
-
-
поддключт к серваа
-
-
-
-
-
-
-
-
список серверов:
-
-
- meex.lol
- других пока нет
-
-
-
-
-
инструкция по игре:
-
-
-ходить через кнопки A и D
-прыгать кнопкой Space
-выбрать блок цифрами
-поставить блок через ПКМ только перед этим выберите
-ломать блоки через ЛКМ
-меню отладки на F3
-ресетнуться на кнопку R
-открыть чат кнопка T
-
-
-
-
-
репозиторий гитхаб
-
протокол сервера
-
готовая реализация сервера на Python
-
-
-
-
-
\ No newline at end of file
From f5b3c3c27e5c02cc63a8be47628336a6cab2e9a6 Mon Sep 17 00:00:00 2001
From: MeexReay <127148610+MeexReay@users.noreply.github.com>
Date: Sun, 22 Dec 2024 01:32:03 +0300
Subject: [PATCH 2/4] Delete script.js
---
script.js | 729 ------------------------------------------------------
1 file changed, 729 deletions(-)
delete mode 100644 script.js
diff --git a/script.js b/script.js
deleted file mode 100644
index a339a83..0000000
--- a/script.js
+++ /dev/null
@@ -1,729 +0,0 @@
-const canvas = document.getElementById("game")
-const ctx = canvas.getContext("2d");
-
-const width = 640
-const height = 480
-
-function lerp(a, b, alpha) {
- return a + alpha * ( b - a )
-}
-
-const server_ip = document.getElementById("server-ip")
-const server_nick = document.getElementById("server-nick")
-const connect_server = document.getElementById("connect-server")
-const server_error = document.getElementById("server-error")
-
-const allowed_key_to_send = [
- "KeyR", "KeyW", "KeyE", "KeyQ", "KeyS",
- "Numpad1", "Numpad2", "Numpad3", "Numpad4", "Numpad5",
- "Numpad6", "Numpad7", "Numpad8", "Numpad9", "Numpad0",
- "ShiftLeft", "ControlLeft", "Enter",
- "F1", "F2", "KeyZ", "KeyX", "KeyC"
-]
-
-connect_server.onclick = () => {
- let ip = server_ip.value
- let nick = server_nick.value
-
- server_error.innerText = ""
-
- if (ip.length == 0) {
- server_error.innerText = "введите айпи пж"
- return
- }
-
- if (nick.length == 0) {
- server_error.innerText = "введите ник пж"
- return
- }
-
- if (!ip.includes(":")) {
- ip += ":8000"
- }
-
- if (typeof player.socket !== "undefined") {
- player.socket.close()
- }
-
- player.on_connect(nick)
-
- blocks = []
-
- try {
- let socket = new WebSocket(
- "ws://"+ip,
- "cubic",
- )
-
- socket.onopen = e => {
- socket.send("J"+nick);
- player.socket = socket
- }
-
- socket.onmessage = e => {
- player.recv_packet(e.data)
- }
-
- socket.onclose = e => {
- player.socket = undefined
- reset_world()
- }
-
- socket.onerror = () => {
- server_error.innerText = "Connection closed due to error"
- reset_world()
- }
- } catch (exception) {
- server_error.innerText = exception
- }
-}
-
-function wrapText(ctx, text, maxWidth) {
- const lines = [];
- let currentLine = '';
-
- for (let i = 0; i < text.length; i++) {
- const char = text[i];
- const testLine = currentLine + char;
- const testWidth = ctx.measureText(testLine).width;
-
- if (testWidth > maxWidth && currentLine) {
- lines.push(currentLine);
- currentLine = char;
- } else {
- currentLine = testLine;
- }
- }
-
- if (currentLine) {
- lines.push(currentLine);
- }
-
- return lines;
-}
-
-var ticksAlive = 0
-
-var debugMode = false
-
-var camera = {
- x: 0.0,
- y: 0.0,
- size: 1.5
-}
-
-var chatOpened = false
-var chatMessages = []
-var chatTyping = ""
-
-class Block {
- constructor(x, y, color, collides, type) {
- this.x = x
- this.y = y
- this.color = color
- this.collides = collides
- this.type = type
- }
-
- render() {
- let rect = this.translate_to_camera()
- if (this.is_need_render(rect)) {
- ctx.fillStyle = this.color
- ctx.fillRect(...rect)
- }
- }
-
- is_need_render(rect) {
- return rect[0] + rect[2] > 0 || rect[1] + rect[3] > 0 || rect[0] < width || rect[1] < height
- }
-
- translate_to_camera() {
- let size = camera.size * 16
-
- return [
- this.x * size - size / 2 + (width / 2 - camera.x * size),
- height - (this.y + 1) * size + size / 2 - (height / 2 - camera.y * size),
- size,
- size
- ]
- }
-
- tick() {
-
- }
-
- tick_render() {
-
- }
-
- on_collide(player, x, y) {
- if (x != 0) player.velocity_x = this.x + x - player.x
- if (y != 0) player.velocity_y = this.y + y - player.y
- }
-
- text_render() {
-
- }
-}
-
-class Player extends Block {
- constructor(x, y, name, color, velocity_x, velocity_y) {
- super(x, y, color, true, null)
-
- this.velocity_x = velocity_x
- this.velocity_y = velocity_y
-
- this.name = name
- }
-
- on_collide(player, x, y) {
- super.on_collide(player, x, y)
- // if (x != 0) {
- // player.vel_x *= 0.5
- // this.vel_x = player.vel_x
- // }
- // if (y != 0) {
- // player.vel_y *= 0.5
- // this.vel_y = player.vel_y
- // }
- }
-
- tick(collide=true) {
- this.x = Math.round(this.x * 100) / 100
- this.y = Math.round(this.y * 100) / 100
- this.velocity_x = Math.round(this.velocity_x * 100) / 100
- this.velocity_y = Math.round(this.velocity_y * 100) / 100
-
- if (collide) this.collide()
- }
-
- collide() {
- this.on_ground = false
-
- for (const block of blocks) {
- if (!block.collides) continue
-
- let collide_x = 0
- let collide_y = 0
-
- if (this.x > block.x-1 && this.x < block.x+1) {
- if (this.y > block.y && this.y + this.velocity_y - 1 < block.y) {
- this.on_ground = true
- collide_y = 1
- }
- if (this.y < block.y && this.y + this.velocity_y > block.y - 1) collide_y = -1
- }
-
- if (this.y > block.y-1 && this.y < block.y+1) {
- if (this.x > block.x && this.x + this.velocity_x - 1 < block.x) collide_x = 1
- if (this.x < block.x && this.x + this.velocity_x > block.x - 1) collide_x = -1
- }
-
- block.on_collide(this, collide_x, collide_y)
- }
- }
-
- tick_render() {
- this.velocity_x *= 0.5
- this.velocity_y *= 0.5
- this.x += this.velocity_x
- this.y += this.velocity_y
- }
-
- text_render() {
- super.text_render()
-
- let rect = this.translate_to_camera()
-
- if (this.is_need_render(rect)) {
- ctx.fillStyle = "#ddd"
- ctx.font = "15px monospace";
- let width = ctx.measureText(this.name).width
- ctx.fillText(this.name,rect[0]+rect[2]/2-width/2, rect[1]-5);
- }
- }
-
- teleport(x, y) {
- this.velocity_x = x - this.x
- this.velocity_y = y - this.y
- }
-
- force_teleport(x, y) {
- this.x = x
- this.y = y
- this.velocity_x = 0
- this.velocity_y = 0
- }
-}
-
-class MainPlayer extends Player {
- constructor() {
- super(0.0, 0.0, "unnamed player", "#5e6", 0, 0)
-
- this.init()
-
- this.socket = undefined
- }
-
- init() {
- this.walk_speed = 1
- this.jump_speed = 2
- this.gravity_speed = 0.5
-
- this.controls_x = 0
- this.controls_jump = 0
- this.on_ground = false
-
- this.block_type = null
- this.all_block_types = [
- "normal", "normal", "normal", "normal", "normal",
- "normal", "normal", "normal", "normal", "normal"
- ]
-
- this.ping = -1
- }
-
- on_connect(name) {
- this.x = 0.0
- this.y = 0.0
- this.velocity_x = 0.0
- this.velocity_y = 0.0
-
- camera.x = 0.0
- camera.y = 0.0
-
- chatOpened = false
- chatMessages = []
-
- this.name = name
- this.color = "#5e6"
-
- this.init()
- }
-
- register() {
- document.addEventListener("keydown", (e) => {
- let key = e.code
-
- if (chatOpened) {
- if (key == "Backspace") {
- chatTyping = chatTyping.slice(0, chatTyping.length-1)
- } else if (key == "Enter") {
- if (chatTyping == "") {
- chatOpened = false
- return
- }
- this.send_packet("M", chatTyping)
- chatTyping = ""
- chatOpened = false
- } else if (key == "Escape") {
- chatOpened = false
- } else if (e.key.length == 1) {
- chatTyping += e.key
- e.preventDefault()
- return false
- }
- } else {
- if (key == "KeyD") {
- this.controls_x = 1
- } else if (key == "KeyA") {
- this.controls_x = -1
- } else if (key == "Space") {
- this.controls_jump = true
- e.preventDefault()
- return false
- } else if (key == "KeyR") {
- if (typeof this.socket === "undefined") {
- this.force_teleport(0, 1)
- }
- } else if (key == "KeyT") {
- chatOpened = true
- }
-
- if (e.key == "0") this.block_type = null
- if ("123456789".includes(e.key)) this.block_type = this.all_block_types[parseInt(e.key)-1]
-
- if (allowed_key_to_send.includes(key)) {
- this.send_packet("K",key,"1")
- }
-
- if (key == "Escape") {
- if (typeof this.socket !== "undefined") {
- this.socket.close()
- }
- }
- }
-
- if (key == "F3") {
- debugMode = !debugMode
- e.preventDefault()
- return false
- }
- })
-
- document.addEventListener("keyup", (e) => {
- let key = e.code
-
- if ((key == "KeyD" && this.controls_x == 1)
- || (key == "KeyA" && this.controls_x == -1)) {
- this.controls_x = 0
- } else if (key == "Space" && this.controls_jump) {
- this.controls_jump = false
- }
-
- if (allowed_key_to_send.includes(key)) {
- this.send_packet("K",key,"0")
- }
- })
-
- canvas.addEventListener("wheel", e => {
- if (e.deltaY > 0) {
- camera.size *= 0.5 * (e.deltaY/114)
- } else {
- camera.size *= 2 * (e.deltaY/-114)
- }
- e.preventDefault()
- return false
- })
-
- canvas.addEventListener("mousedown", e => {
- let rect = canvas.getBoundingClientRect()
- let size = 16 * camera.size
- let x = Math.round((e.clientX - rect.x) / size - (width / size / 2) + camera.x)
- let y = Math.round((height - (e.clientY - rect.y)) / size - (height / size / 2) + camera.y)
-
- if (e.buttons == 2 && this.block_type != null) {
- if (typeof this.socket === "undefined") {
- placeBlock(new Block(x,y,"#555",true,this.block_type))
- }
- this.send_packet("P",x,y,this.block_type)
- } else if (e.buttons == 1) {
- if (typeof this.socket === "undefined") {
- removeBlock(x, y)
- }
- this.send_packet("D",x,y)
- }
- })
- }
-
- recv_packet(packet) {
- let packet_id = packet[0]
- let packet_data = packet.slice(1).split("\n")
- console.log(packet_id, packet_data)
-
- // console.log(packet_id, packet_data)
-
- if (packet_id == "K") {
- server_error.innerText = packet_data[0]
- this.socket.close()
- this.socket = null
- }
-
- if (packet_id == "N") {
- this.name = packet_data[0]
- }
-
- if (packet_id == "C") {
- this.color = packet_data[0]
- }
-
- if (packet_id == "M") {
- chatMessages.unshift(...packet_data)
- }
-
- if (packet_id == "R") {
- this.velocity_x = parseFloat(packet_data[0]) - this.x + parseFloat(packet_data[2])
- this.velocity_y = parseFloat(packet_data[1]) - this.y + parseFloat(packet_data[3])
- }
-
- if (packet_id == "P") {
- let x = parseFloat(packet_data[0])
- let y = parseFloat(packet_data[1])
- this.x = x
- this.y = y
- }
-
- if (packet_id == "V") {
- let x = parseFloat(packet_data[0])
- let y = parseFloat(packet_data[1])
- this.velocity_x = x
- this.velocity_y = y
- }
-
- if (packet_id == "S") {
- let speed_type = packet_data[0]
- let speed = parseFloat(packet_data[1])
-
- if (speed_type == "W") {
- this.walk_speed = speed
- } else if (speed_type == "J") {
- this.jump_speed = speed
- } else if (speed_type == "G") {
- this.gravity_speed = speed
- }
- }
-
- if (packet_id == "W") {
- for (const data of packet_data) {
- let type = data[0]
- let create = data[1] == "1"
- let params = data.slice(2).split(",")
-
- if (type == "B") {
- let x = parseFloat(params[0])
- let y = parseFloat(params[1])
-
- if (create) {
- let collides = params[2] == "1"
- let type = params[3]
- let color = params[4]
-
- let block = getBlock(x, y)
- if (block != null) {
- block.x = x
- block.y = y
- block.color = color
- block.collides = collides
- block.type = type
- } else {
- placeBlock(new Block(x,y,color,collides,type))
- }
- } else {
- removeBlock(x, y)
- }
- } else if (type == "P") {
- let name = params[0]
-
- if (create) {
- let x = parseFloat(params[1])
- let y = parseFloat(params[2])
- let vel_x = parseFloat(params[3])
- let vel_y = parseFloat(params[4])
- let color = params[5]
-
- let player = getPlayer(name)
- if (player != null) {
- player.x = x
- player.y = y
- player.color = color
- player.velocity_x = vel_x
- player.velocity_y = vel_y
- } else {
- placeBlock(new Player(x,y,name,color,vel_x,vel_y))
- }
- } else {
- removePlayer(name)
- }
- }
- }
- }
-
- if (packet_id == "B") {
- this.all_block_types = packet_data.slice(0, 9)
- this.block_type = null
- }
- }
-
- send_packet(id, ...params) {
- if (typeof this.socket === "undefined") {
- return
- }
- this.socket.send(id+params.join("\n"))
- }
-
- send_velocity_packet(x, y) {
- if (x == 0 && y == 0) return
- this.send_packet("V", x, y)
- }
-
- tick() {
- super.tick(false)
-
- let vel_x = this.velocity_x
- let vel_y = this.velocity_y
-
- this.velocity_x += this.controls_x * this.walk_speed
-
- if (this.controls_jump && this.on_ground) {
- this.velocity_y += this.jump_speed
- this.on_ground = false
- } else {
- this.velocity_y -= this.gravity_speed
- }
-
- this.collide()
-
- ticksAlive++
-
- this.send_velocity_packet(this.velocity_x-vel_x, this.velocity_y-vel_y)
- }
-
- render() {
- super.render()
-
- camera.x = Math.round(lerp(camera.x, this.x, 0.075) * 1000) / 1000
- camera.y = Math.round(lerp(camera.y, this.y, 0.075) * 1000) / 1000
- }
-
- text_render() {
- super.text_render()
-
- if (this.block_type != null) {
- ctx.fillStyle = "#76d"
- ctx.font = "15px monospace";
- ctx.fillText("selected: "+this.block_type, 0, 15);
- }
-
- if (debugMode) {
- ctx.fillStyle = "#de7"
- ctx.font = "20px monospace";
- ctx.fillText("x: "+this.x, 0, 20);
- ctx.fillText("y: "+this.y, 0, 40);
- ctx.fillText("velocity_x: "+this.velocity_x, 0, 60);
- ctx.fillText("velocity_y: "+this.velocity_y, 0, 80);
- ctx.fillText("camera.x: "+camera.x, 0, 100);
- ctx.fillText("camera.y: "+camera.y, 0, 120);
- }
-
- if (!chatOpened) {
- ctx.fillStyle = "#22222288"
- ctx.fillRect(5, height - 35, width * 0.4, 30)
-
- ctx.fillStyle = "#aaaaaa88"
- ctx.font = "15px monospace";
- ctx.fillText("Нажмите T для чата", 15, height - 15);
- } else {
- ctx.fillStyle = "#22222288"
- ctx.fillRect(5, height - 35, width -10, 30)
-
- ctx.save();
-
- ctx.beginPath();
- ctx.moveTo(5, height - 35);
- ctx.lineTo(width - 5 , height - 35);
- ctx.lineTo(width - 5, height - 5);
- ctx.lineTo(5, height - 5);
- ctx.lineTo(5, height - 35);
- ctx.closePath();
-
- ctx.clip();
-
- ctx.font = "15px monospace"
- if (chatTyping.length == 0) {
- ctx.fillStyle = "#aaaaaa88"
- ctx.fillText("Напишите сообщение...", 15, height - 15);
- } else {
- ctx.fillStyle = "#ffffff"
- let text_width = ctx.measureText(chatTyping).width
- ctx.fillText(chatTyping, Math.min(10, width - text_width - 10), height - 15)
- }
-
- ctx.restore();
- }
-
- if (chatMessages.length > 0) {
- let draw_message = (message) => {
- let lines = wrapText(ctx, message, message_width - 20)
- let height = lines.length * 20 + 5 + 5
-
- let top = message_bottom - height
-
- ctx.fillStyle = "#22222288"
- ctx.fillRect(5, top, message_width, height)
-
- let y = 5
- for (let line of lines) {
- ctx.fillStyle = "#ffffff"
- ctx.fillText(line, 15, top+y+15);
- y += 5 + 20
- }
-
- message_bottom -= height + 5
- }
-
- let message_width = width * 0.4
- let message_bottom = height - 35 - 5
-
- if (chatOpened) {
- chatMessages.forEach(draw_message)
- } else {
- draw_message(chatMessages[0])
- }
- }
- }
-}
-
-var player = new MainPlayer()
-player.register()
-
-var blocks = []
-
-blocks.push(new Block(-1, -1, "#555", true));
-blocks.push(new Block(0, -1, "#a67", true));
-blocks.push(new Block(1, -1, "#555", true));
-
-function reset_world() {
- player.on_connect("unnamed player")
- blocks = []
- blocks.push(new Block(-1, -1, "#555", true));
- blocks.push(new Block(0, -1, "#a67", true));
- blocks.push(new Block(1, -1, "#555", true));
-}
-
-function getBlock(x, y) {
- let value = blocks.find(o => !(o instanceof Player) && o.x == x && o.y == y)
- if (typeof value === "undefined") {
- return null
- } else {
- return value
- }
-}
-
-function placeBlock(block) {
- blocks.push(block);
-}
-
-function removeBlock(x, y) {
- blocks = blocks.filter(o => o instanceof Player || o.x != x || o.y != y)
-}
-
-function getPlayer(name) {
- let value = blocks.find(o => o instanceof Player && o.name == name)
- if (typeof value === "undefined") {
- return null
- } else {
- return value
- }
-}
-
-function removePlayer(name) {
- blocks = blocks.filter(o => !(o instanceof Player) || o.name != name)
-}
-
-setInterval(() => {
- for (const block of blocks)
- block.tick()
- player.tick()
-}, 1000 / 20)
-
-setInterval(() => {
- for (const block of blocks)
- block.tick_render()
- player.tick_render()
-}, 1000 / 60)
-
-let renderTimer = () => {
- ctx.fillStyle = "#333";
- ctx.fillRect(0, 0, width, height);
-
- for (const block of blocks)
- block.render()
- for (const block of blocks)
- block.text_render()
- player.render()
- player.text_render()
-
- requestAnimationFrame(renderTimer)
-}
-
-requestAnimationFrame(renderTimer)
\ No newline at end of file
From 9e9b739eafac13176fbf99bf15991eb8c1f45974 Mon Sep 17 00:00:00 2001
From: MeexReay <127148610+MeexReay@users.noreply.github.com>
Date: Sun, 22 Dec 2024 01:32:13 +0300
Subject: [PATCH 3/4] Delete server.py
---
server.py | 388 ------------------------------------------------------
1 file changed, 388 deletions(-)
delete mode 100644 server.py
diff --git a/server.py b/server.py
deleted file mode 100644
index cffd3f6..0000000
--- a/server.py
+++ /dev/null
@@ -1,388 +0,0 @@
-from websockets.server import serve, ServerConnection
-import random, sys, asyncio, time
-
-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.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
-
- 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():
- 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 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():
- 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())
- async with serve(handler, HOST, PORT) as server:
- 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 = [
- Block(-1, -1, "normal", "#555", True),
- Block(0, -1, "spawn", "#2ad", True),
- Block(1, -1, "normal", "#555", True)
-]
-
-
-
-if __name__ == "__main__":
- asyncio.run(main())
\ No newline at end of file
From 6760dc57eb3414bce8b4e352fa8d3a6b92ec6343 Mon Sep 17 00:00:00 2001
From: MeexReay <127148610+MeexReay@users.noreply.github.com>
Date: Sun, 22 Dec 2024 01:32:51 +0300
Subject: [PATCH 4/4] Delete server/__pycache__ directory
---
server/__pycache__/block.cpython-311.pyc | Bin 2548 -> 0 bytes
server/__pycache__/config.cpython-311.pyc | Bin 577 -> 0 bytes
server/__pycache__/main.cpython-311.pyc | Bin 3778 -> 0 bytes
server/__pycache__/network.cpython-311.pyc | Bin 5040 -> 0 bytes
server/__pycache__/player.cpython-311.pyc | Bin 14182 -> 0 bytes
5 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 server/__pycache__/block.cpython-311.pyc
delete mode 100644 server/__pycache__/config.cpython-311.pyc
delete mode 100644 server/__pycache__/main.cpython-311.pyc
delete mode 100644 server/__pycache__/network.cpython-311.pyc
delete mode 100644 server/__pycache__/player.cpython-311.pyc
diff --git a/server/__pycache__/block.cpython-311.pyc b/server/__pycache__/block.cpython-311.pyc
deleted file mode 100644
index 9b4b878150fb756010a5f38c16c7f1ceae0f5a2d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2548
zcmZ3^%ge>Uz`!7Bk)H0r!octt#DQTJDC4sZ0|Uc!h7^Vr#vF!R#wbQc1||k~h7_h2
zh7{&yObiUGnV>2eqL@-xf*CYfUxEZQ8E>%$2RMfNC4+b{3{(4A4{T~GLlk2QNFQSh
zLliT}%qW%=<`#x1))bZ&hA6fajuf^Q#whj__Fx80&ReWbIr+)i$!svw7#J8p1~M}+
zFnsm_8(70w!w?Ubu3@TSh-ZXxYnW>o;z8=c0yQi(4DleH5FTp{Lp;pO3@Hr33@aJ^
zG+A#kR@`E&yv0&dS&({*H90>gzvvc6a(+%uW=d+YCfhBR;?$h9A`S)yh9XV|28LT)
zNgxNtgOnF>gG6~47#NB{?o|MTUncq)`MIh3rNya5`o;OBMail9xv8lY#hE3k`sL;2
z`pKn9naTRasYPX}MfzaF^a?6(am2@G=4F<|$5-)zLqHG42f4UdlYxPuf#HUrPzOs7
z8;lZ>>tN~Oydf&p!P3KZLqeg0rH8kJw}baH$h*n#FkxU|0I{1vAqYwf-VBW3$PQ*G
zVrO7rSjn)Gv4|Js2aqa-B7Tr3SxPdKv#YouE&;JYKK%?LUNF1>xrBqKgQZB2fq{W&
zdy0fXfhB@w5nEAeUP@|F6%WKJ2#*h>QVc|ZZ2*M;$V(s$icPRjzA-W|Kzvfel){(|
z5@#q9uVKgnsRH8~rYZ&oh8jkMNEH`|kDOXibRyCXGgK#B1gaBk3&?PUoyBZ5%y3iI
z)i7kiU7Ny`4c1>|RKvJ{6{HItr!X&LWMEhg3U{y+BSQ`20#Hf<^N@)Y7Ic**5!5mU28PveE~*|-ZiQh@PQM~hz~17@D$Om3Ps-0P
zE=h;z`(pnR6wo0rLGn4R7Ef_%iC1ZE0ffhypBJBAlwX>c0=2mqtca~RwIm?FxCoRsZt)hU
z=B0$>2jnDHrWO@zvVb$zEw%zMA8b3=+G0>50hJbz+{T%o=L{)us`w%K04@N^0L40t
z3=GhG$IUZ=vCFK(Y=O!WwH1a7wN{j^sko?QKEZ5?%?&(q4`6Z=0%s^r3Y!pigI~C(
za)!?(evJ$K8aD*Qt_!GL5>Q)Ea#29%ih#}q0UfZI@+AS~1&$X5)UF7qT@X;ap{zcE
z?TUcH1p$Q{g5p;Ml@=sk5>&q+s18;PHlX;Tfc6yu?F$0hU@2B&KyyOy`Oi$Wa%-PQxTWh%<=EObGiR#2}!0
zfnNn&Foh(85&z&Oomn%;&g&DtXb3xTAi{C9KC&OQiI=7gd3^dsg3Fa1aW?o4V
zsHOoYRdB_l$plGV%!w%}#UQ^bKnri~lKkM3#FEt9)Vz`^K}do^5>f&c!D)~RB|Pc^
zf7u28;0Z2M+%E73U*Hd(5i%zX!djZVLS>EaMKy~nY8DWY9VUA$A*=%>N2(w!5SdVR
zAvN<#YW9VMG7!oux{_A{VkDGZ;3xw}hMy*5h$aW9lFLiXO^uJg#T6f)o1ape17h>U
z#}}3+=0IeM1VH7!D2R{%5$YfU)RKVIAE5jI&NM}!@C6f~XekC2@(m0y_<@O)Rq+D@
zoZw?*<^8|_C)nUDuoxRZtL_H|IHAPITFwYkjhh7f32eEaCi5+}Msr(h+plBjG>iraWDe|!v|(YM#dWq5*JX>2NnfJkk#19j|?F3FJSTm1_^c;0Ft#U
Aj{pDw
diff --git a/server/__pycache__/config.cpython-311.pyc b/server/__pycache__/config.cpython-311.pyc
deleted file mode 100644
index 2f4b86e28eb219ecf563ae569399417929d70243..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 577
zcmZ3^%ge>Uz`&qtk)F=L$iVOz#DQTJDC2V*0|Uc!h7^Vr#vFzyhE#?uMyL!!3X>#^
z$*_P4&O~CQFfU_dU|0>;5yQm55XBtD!o-lun#zJI7sZm!kixQvF^VmPHHtljEs7(B
zJ%uBMGlgpoa};L^V=#jz_e+r3elJ1h{bIDzWdFsYoML43i$&Qe#i)u!*~loRibXjE
z!cGCRp&}`oOhq6Uf{B;>3=9laEXt;)rbTQF3=BoA3=9mK%(vL`@{4j4b8c~EmF5=2
zC*|iCm)v5@&dkY4Ez;z=#avuje2XQqD81|!YjHtNX2~rU5C7nhTPy+oK_R!8GxJJr
zu{rzu_y+~wVhs*(4EMXm7v$>b>=Ezc864v1=j?im+sVh@**iWYGQc%>CBtWsUw)bD
zXXNLm>X#O$7U>t~mlh?b>gT4WRupHJr0SQKm+L2&CS@k;7pE4Lr55QY=jWwmrt1|{
z{^GF7%}*)KNwq7IU|;~n9Yb*z0|UbcW=2NF8w^|xVEBQJfmfi%V?tn$_jMlGOFXhO
zk}vWoUExt`!gASm3x_koRpU-X8M
b#0@@?59}=bOdl9n_?a5GL9j@Sfq?-4zTA%-
diff --git a/server/__pycache__/main.cpython-311.pyc b/server/__pycache__/main.cpython-311.pyc
deleted file mode 100644
index fecc7947d764568a182aea61dc9ae44063531405..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3778
zcmZ3^%ge>Uz`(#|o}SLj%E0g##DQT>DC6@F1_p-d3@HpLj5!QZ5SlTHF@+(8DTgVS
zIf|JPB*vV>lFJ&!3TCt9u!S(Bu(mKnv8S-LFhp^ru(vQoai(yzFhp^saJDc+ai?
zFhudBaJMi-@iH-_@}=;k@TBmj@TKrCV`X4i%?h=f0W8Om!jmG9B8XK^2)n*?=0yHf
zffV6vCXh)*QcMh~3|WF8HV88@r1CCfU|?7c;(+loMh1q}j9?}M0|O&N3S%%BYl^%C
z3HoU=-r_DUNh~S}PAw`+Ez)GX#TJm0SP5n^Y9)izz%UB~1A{UH1H^uae2X>Q
zKgh@B7E4KGL2405&q~Hyj7h~HUnu-C*3Zb#P1P?gPA$?e&Mz%WPSwv%O|2--EJ@Wb
zFE7_mE=|fz)-MM8Mn5+(Gf%Ie@)lQmY6-+!#YMaf3=HfH3=G9q3=9k}7+x?mFg)cI
z`oO@%Dgq`t81HiPPhjjc?=hd6c8Oc&0=LW!A>j^p5d86h6{hSn$Ro)hH-Q)+400*R
z_n$Ms=?X1uG;0}A!iJGx*zngdW`WEDnaxnc01q+7WF|(2V1^zoP>2xI&021Z~`4*I36cmbh7#J9Cal(V62$Xa{AyVwfzyJ#gW>(1$
z49u*O*g}F|pr>qxNLO7)-3@Vx2^=8!;{!WPM-d+b0|Q2)0eRxH2P9CC0t`7gvf$#V
z$v1_uij@H^83i+FGWlh`U}j*@WV*##lwX>ca*L%TGdFc51K56uABB=ji;7b7O5$@f
zb8<4{LGndn3=9k+pums@d-g83z*Lbbk`tw;NY5y_D5!jqTjdJ3$^{k`urVm!0C}Mq
zl=j=;X`d54^~=;U)G#c7djuS9Py*Fc%?v3F)y!cG)0x{?7#VtGOW>sf0|O$)QVSqRBKkg(w_$xO};
z$;?eHDguRsI4DSB;JM)g6EmyQT|u!A42+!8V4}nEhLG@dwMl9dHKu5E_@TnPe4;(M
zGh8n6DPG}Iyug74fzpQ(o-mz)9HvO&hZ<@w46*bLVK-0+zXZ8QlLcIA+~UkmO)YTD
z$t+7Pk_2TeDG-4kxO{L$kPwzm
zPt={Fdx1wDD}sc!<3&EXD|~VnIItj4Xv<@UHYkmOQ}8hc28MQM`OV2t0`ec$RO-SI
zYtqHgi6cdKFr+c2FtuzQtHj3^H9op#hvRe{tF5
zWEPhs=jRsKRmns2f)kORO-_DtVotH09zuZ{C`|=0FfjapB&rK?u@JPu@Ic}5ibE9#
ztBzD%a12C8aMwx#7s6t|X(wF}3?|xZvO?2-
zacW*lcz#h%3Z$-qX8=vcB2|!6KnckK?2TL8NKP-(fHoV7ry<-5$^c4886ch!lmp@!
z;aLFU69~DN^@3dNMY-53Fb_h6A>>7&xGO?&7kIEENNypcYyzc0aBeAMgtv7-9)u7nytK@8aLxud0l`fp^ne5f(JfY|oc!c$aGnR*18P^s$KPTuD$PR+*Iyhqx%nxj
zIjMF?BE-_19V3xeW!r$QD;yr<70?S0UDQp*6#ILZ3Ux1+x%nU427nmi%F%JOMVGKe5
diff --git a/server/__pycache__/network.cpython-311.pyc b/server/__pycache__/network.cpython-311.pyc
deleted file mode 100644
index 763cf31661513477df28e09578ea0f81ee6da2d8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 5040
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