init commit
This commit is contained in:
commit
c740131ec7
4 changed files with 1232 additions and 0 deletions
1
README.md
Normal file
1
README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# cubicjs
|
145
index.html
Normal file
145
index.html
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>cubic</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/icon/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/icon/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/icon/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/icon/site.webmanifest">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
if (!new URLSearchParams(location.search).has("PLEASE_HTTPS")) {
|
||||||
|
if (location.protocol !== 'http:') {
|
||||||
|
location.replace(`http:${location.href.substring(location.protocol.length)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
pre {
|
||||||
|
font-family: 'Fira Code', 'Times New Roman', Times, serif;
|
||||||
|
font-size: 15px;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body oncontextmenu="return false;">
|
||||||
|
<div class="container">
|
||||||
|
<h1>Cubic</h1>
|
||||||
|
<p>игра кубик переделанная на js</p>
|
||||||
|
|
||||||
|
<div class="margin-25"></div>
|
||||||
|
|
||||||
|
<input type="text" id="server-ip" placeholder="айпи серрве"><br>
|
||||||
|
<input type="text" id="server-nick" placeholder="ник игроаа"><br>
|
||||||
|
<span id="server-error" style="color: red;margin: 5px 0;display: inline-block;"></span><br>
|
||||||
|
<button id="connect-server">поддключт к серваа</button><br>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="margin-25"></div>
|
||||||
|
|
||||||
|
<canvas width="640" height="480" id="game"></canvas>
|
||||||
|
|
||||||
|
<div class="margin-25"></div>
|
||||||
|
|
||||||
|
<h3>список серверов:</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>meex.lol</li>
|
||||||
|
<li>других пока нет</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="margin-25"></div>
|
||||||
|
|
||||||
|
<h3>инструкция по игре:</h3>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
ходить через кнопки A и D
|
||||||
|
прыгать кнопкой Space
|
||||||
|
выбрать блок цифрами
|
||||||
|
поставить блок через ПКМ только перед этим выберите
|
||||||
|
ломать блоки через ЛКМ
|
||||||
|
меню отладки на F3
|
||||||
|
ресетнуться на кнопку R
|
||||||
|
открыть чат кнопка T
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<div class="margin-25"></div>
|
||||||
|
|
||||||
|
<h3>протокол сервачный:</h3>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
работает через вебсокеты
|
||||||
|
дефолтный порт 8000
|
||||||
|
|
||||||
|
булевые значения пишутся как 0 и 1
|
||||||
|
в нике не должны быть пробелы
|
||||||
|
установка - добавление / изменение
|
||||||
|
|
||||||
|
формат пакетов:
|
||||||
|
первый символ - тип пакета
|
||||||
|
остальное - данные пакета, параметры через \n (перенос строки)
|
||||||
|
пример пакета отправки мира:
|
||||||
|
"WB10,0,1,normal,#fff\nP1name,0,1,0,0,#d00"
|
||||||
|
🠅└─┬───────────────┘ └┬────────────────┘
|
||||||
|
тип параметр (блок) параметр (игрок)
|
||||||
|
|
||||||
|
|
||||||
|
список пакетов:
|
||||||
|
{данные} - типа это какието данные так обозначаю, скобки не надо
|
||||||
|
[C] - айди пакета
|
||||||
|
|
||||||
|
клиент отправляет:
|
||||||
|
заход игрока [J]: {ник_игрока}
|
||||||
|
прыжок выкл (controls_jump = false) [C]: "J", "0"
|
||||||
|
прыжок вкл (controls_jump = true) [C]: "J", "1"
|
||||||
|
идти выкл (controls_x = 0) [C]: "W", "0"
|
||||||
|
идти левее (controls_x = -1) [C]: "W", "1"
|
||||||
|
идти правее (controls_x = 1) [C]: "W", "2"
|
||||||
|
корректировка движения [R]: {x}, {y}, {vel_x}, {vel_y}
|
||||||
|
установить блок [P]: {x}, {y}, {тип}
|
||||||
|
сломать блок [D]: {x}, {y}
|
||||||
|
нажатие кнопки (список кнопок ниже) [K]: {кнопка}, {нажата ли}
|
||||||
|
отправить сообщение [M]: {сообщение}
|
||||||
|
|
||||||
|
сервер отправляет:
|
||||||
|
кикнуть игрока с ошибкой [K]: {ошибка}
|
||||||
|
установить цвета игроку [C]: {цвет}
|
||||||
|
установить ник игрока [N]: {ник}
|
||||||
|
установить позицию игрока [P]: {x}, {y}
|
||||||
|
установить velocity игрока [V]: {x}, {y}
|
||||||
|
установить walk speed игрока [S]: "W", {скорость}
|
||||||
|
установить jump speed игрока [S]: "J", {скорость}
|
||||||
|
установить gravity speed игрока [S]: "G", {скорость}
|
||||||
|
отправить мир [W]: {изм. мира}, {изм. мира}, ...
|
||||||
|
отправить все типы блоков: [B]: {тип_1}, ..., {тип_9}
|
||||||
|
отправить сообщение [M]: {сообщение}, {сообщение}, ...
|
||||||
|
|
||||||
|
список кнопок которые может отправить игрок через отдельный пакет:
|
||||||
|
["KeyR", "KeyW", "KeyE", "KeyQ", "KeyS", "KeyZ", "KeyX", "KeyC"
|
||||||
|
"Numpad1", "Numpad2", "Numpad3", "Numpad4", "Numpad5",
|
||||||
|
"Numpad6", "Numpad7", "Numpad8", "Numpad9", "Numpad0",
|
||||||
|
"ShiftLeft", "ControlLeft", "Enter", "F1", "F2"]
|
||||||
|
|
||||||
|
формат изменения мира:
|
||||||
|
установка блока: "B1{x},{y},{collides},{type},{color}"
|
||||||
|
установка игрока: "P1{name},{x},{y},{vel_x},{vel_y},{color}"
|
||||||
|
удаление блока: "B0{x},{y}"
|
||||||
|
удаление игрока: "P0{name}"
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<a href="server.py">готовая реализация сервера на Python</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
717
script.js
Normal file
717
script.js
Normal file
|
@ -0,0 +1,717 @@
|
||||||
|
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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
this.send_packet("C","W","2")
|
||||||
|
} else if (key == "KeyA") {
|
||||||
|
this.controls_x = -1
|
||||||
|
this.send_packet("C","W","1")
|
||||||
|
} else if (key == "Space") {
|
||||||
|
this.controls_jump = true
|
||||||
|
this.send_packet("C","J","1")
|
||||||
|
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
|
||||||
|
this.send_packet("C","W","0")
|
||||||
|
} else if (key == "Space" && this.controls_jump) {
|
||||||
|
this.controls_jump = false
|
||||||
|
this.send_packet("C","J","0")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 == "P") {
|
||||||
|
let x = parseFloat(packet_data[0])
|
||||||
|
let y = parseFloat(packet_data[1])
|
||||||
|
if (Math.abs(x - this.x) > 2) this.x = x
|
||||||
|
if (Math.abs(y - this.y) > 2) this.y = y
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet_id == "V") {
|
||||||
|
let x = parseFloat(packet_data[0])
|
||||||
|
let y = parseFloat(packet_data[1])
|
||||||
|
if (Math.abs(x - this.velocity_x) > 2) this.velocity_x = x
|
||||||
|
if (Math.abs(y - this.velocity_y) > 2) 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"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tick() {
|
||||||
|
super.tick(false)
|
||||||
|
|
||||||
|
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++
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
369
server.py
Normal file
369
server.py
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
from websockets.asyncio.server import serve, ServerConnection
|
||||||
|
import threading, random, sys, asyncio
|
||||||
|
|
||||||
|
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 writePacket(self.websocket, "V", [str(x), str(y)])
|
||||||
|
|
||||||
|
async def setPos(self, x, y):
|
||||||
|
if x == self.x and y == self.y: return
|
||||||
|
|
||||||
|
self.x = x
|
||||||
|
self.y = 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
|
||||||
|
|
||||||
|
self.vel_x += self.controls_x * self.walk_speed
|
||||||
|
|
||||||
|
if self.controls_jump and self.on_ground:
|
||||||
|
self.vel_y += self.jump_speed
|
||||||
|
self.on_ground = False
|
||||||
|
else:
|
||||||
|
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):
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if packet_id == "C":
|
||||||
|
if packet_data[0] == "W":
|
||||||
|
if packet_data[1] == "0":
|
||||||
|
player.controls_x = 0
|
||||||
|
if packet_data[1] == "1":
|
||||||
|
player.controls_x = -1
|
||||||
|
if packet_data[1] == "2":
|
||||||
|
player.controls_x = 1
|
||||||
|
if packet_data[0] == "J":
|
||||||
|
if packet_data[1] == "0":
|
||||||
|
player.controls_jump = False
|
||||||
|
if packet_data[1] == "1":
|
||||||
|
player.controls_jump = True
|
||||||
|
|
||||||
|
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, "leaved the server")
|
||||||
|
|
||||||
|
async def tickTimer():
|
||||||
|
while True:
|
||||||
|
for b in WORLD:
|
||||||
|
await b.tick()
|
||||||
|
await asyncio.sleep(1/20)
|
||||||
|
|
||||||
|
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(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())
|
Loading…
Add table
Add a link
Reference in a new issue