Skip to content

Commit a0e6a8b

Browse files
committed
Blocks EHEHEHEHEHHEHEHEHEHE :3
I DID IT, I SAVED THE WORLD
1 parent 0ed33b5 commit a0e6a8b

4 files changed

Lines changed: 228 additions & 17 deletions

File tree

PythonWalker/__init__.py

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,34 @@
22
import requests
33
from . import player as player_template
44
from websockets.sync.client import connect as ws_conn
5+
from . import world
56

67
def login_with_pass(email, password):
8+
print("Logging in...")
79
player = player_template.player()
810
r = requests.post("https://api.pixelwalker.net/api/collections/users/auth-with-password", json={'identity': email, 'password': password})
911
data = r.json()
1012
player.token = data["token"]
1113
record = data["record"]
1214
player.username = record["username"]
1315
player.id = record["id"]
16+
print("Logged in")
1417
return player
1518

16-
def connect(world_id, user, on_chat=None, on_init=None, on_join=None, on_leave=None, commands=None, custom_init=False):
19+
def connect(world_id, user, on_chat=None, on_init=None, on_join=None, on_leave=None, on_block=None, commands=None, custom_init=False):
20+
print("Getting world type...")
1721
version = requests.get("https://game.pixelwalker.net/listroomtypes").json()[0]
22+
print("Getting join key...")
1823
headers = {"Authorization": f"Bearer {user.token}"}
1924
r = requests.get(f"https://api.pixelwalker.net/api/joinkey/{version}/{world_id}", headers=headers)
2025
join_key = r.json()["token"]
2126
player_list = {}
27+
print("Gathering block info...")
28+
block_list = requests.get('https://game.pixelwalker.net/listblocks').json()
2229

23-
with ws_conn(f"wss://game.pixelwalker.net/ws?joinKey={join_key}") as websocket:
30+
print("Connecting...")
31+
with ws_conn(f"wss://game.pixelwalker.net/ws?joinKey={join_key}", max_size=None) as websocket:
32+
print("Connected")
2433
while True:
2534
message = websocket.recv()
2635
packet = world_pb2.WorldPacket()
@@ -32,53 +41,101 @@ def connect(world_id, user, on_chat=None, on_init=None, on_join=None, on_leave=N
3241
send = world_pb2.WorldPacket(player_init_received=world_pb2.PlayerInitReceivedPacket()).SerializeToString()
3342
websocket.send(send)
3443

44+
world_data = world.decode(packet.player_init_packet.world_data, block_list, packet.player_init_packet.world_width, packet.player_init_packet.world_height)
45+
3546
if commands:
3647
for command in commands.keys():
3748
cmd = world_pb2.PlayerChatPacket()
3849
cmd.message = f"/custom register {command}"
3950
send = world_pb2.WorldPacket(player_chat_packet=cmd).SerializeToString()
4051
websocket.send(send)
4152
player_id = packet.player_init_packet.player_properties.player_id
42-
_run_user_handle(on_init, websocket, world_pb2, player_list, packet.player_init_packet)
53+
_run_user_handle(on_init, websocket, world_pb2, player_list, packet.player_init_packet, world_data)
4354

4455
elif packet.HasField("player_chat_packet"):
4556
if not packet.player_chat_packet.player_id == player_id:
46-
_run_user_handle(on_chat, websocket, world_pb2, player_list, packet.player_chat_packet)
57+
_run_user_handle(on_chat, websocket, world_pb2, player_list, packet.player_chat_packet, world_data)
4758

4859
elif packet.HasField("ping"):
4960
send = world_pb2.WorldPacket(ping=world_pb2.Ping()).SerializeToString()
5061
websocket.send(send)
5162

5263
elif packet.HasField("player_joined_packet"):
5364
player_list[packet.player_joined_packet.properties.player_id] = packet.player_joined_packet.properties.username
54-
_run_user_handle(on_join, websocket, world_pb2, player_list, packet.player_joined_packet)
65+
_run_user_handle(on_join, websocket, world_pb2, player_list, packet.player_joined_packet, world_data)
5566

5667
elif packet.HasField("player_left_packet"):
57-
_run_user_handle(on_leave, websocket, world_pb2, player_list, packet.player_left_packet)
68+
_run_user_handle(on_leave, websocket, world_pb2, player_list, packet.player_left_packet, world_data)
5869
del player_list[packet.player_left_packet.player_id]
70+
71+
elif packet.HasField('world_block_placed_packet'):
72+
id = packet.world_block_placed_packet.block_id
73+
if packet.world_block_placed_packet.layer == 0:
74+
layer = 'bg'
75+
if packet.world_block_placed_packet.layer == 1:
76+
layer = 'fg'
77+
if packet.world_block_placed_packet.layer == 2:
78+
layer = 'ol'
79+
if packet.world_block_placed_packet.extra_fields:
80+
block_data = world.decode_block_placed_data(packet.world_block_placed_packet.extra_fields)
81+
else:
82+
block_data = []
83+
84+
for position in packet.world_block_placed_packet.positions:
85+
world_data[layer][position.x][position.y]['id'] = id
86+
world_data[layer][position.x][position.y]['data'] = block_data
87+
88+
_run_user_handle(on_block, websocket, world_pb2, player_list, packet.world_block_placed_packet, world_data)
5989

6090
elif packet.HasField("player_direct_message_packet"):
6191
if commands:
6292
for command in commands.keys():
6393
if f"//{command}" == packet.player_direct_message_packet.message or f"{packet.player_direct_message_packet.message}".startswith(f"//{command} "):
64-
_run_custom_cmd(commands[command], websocket, world_pb2, player_list, packet.player_direct_message_packet.from_player_id, packet.player_direct_message_packet.message)
94+
_run_custom_cmd(commands[command], websocket, world_pb2, player_list, packet.player_direct_message_packet.from_player_id, packet.player_direct_message_packet.message, world_data)
6595

6696
class Connection:
67-
def __init__(self, websocket, world_pb2, player_list):
97+
def __init__(self, websocket, world_pb2, player_list, world_data):
6898
self.websocket = websocket
6999
self.proto = world_pb2
70100
self.players = player_list
101+
self.world_data = world_data
71102

72103
def send_chat(self, message):
73104
packet = self.proto.PlayerChatPacket()
74105
packet.message = message
75106
send = self.proto.WorldPacket(player_chat_packet=packet).SerializeToString()
76107
self.websocket.send(send)
77108

78-
def _run_user_handle(function, websocket, world_pb2, player_list, packet):
109+
def place_block(self, x, y, layer, id, data=None):
110+
if layer == 0:
111+
layer = 'bg'
112+
if layer == 1:
113+
layer = 'fg'
114+
if layer == 2:
115+
layer = 'ol'
116+
if not self.world_data[layer][x][y]['id'] == id or not self.world_data[layer][x][y]['data'] == data:
117+
if layer == 'bg':
118+
layer = 0
119+
elif layer == 'fg':
120+
layer = 1
121+
elif layer == 'ol':
122+
layer = 2
123+
124+
data = world.encode_block_placed_data(id, self.world_data['block_list'], data)
125+
126+
packet = self.proto.WorldBlockPlacedPacket()
127+
packet.block_id = id
128+
packet.layer = layer
129+
if data:
130+
packet.extra_fields = data
131+
packet.positions.append(self.proto.PointInteger(x=x,y=y))
132+
send = self.proto.WorldPacket(world_block_placed_packet=packet).SerializeToString()
133+
self.websocket.send(send)
134+
135+
def _run_user_handle(function, websocket, world_pb2, player_list, packet, world_data):
79136
if function:
80-
function(Connection(websocket, world_pb2, player_list), packet)
137+
function(Connection(websocket, world_pb2, player_list, world_data), packet)
81138

82-
def _run_custom_cmd(function, websocket, world_pb2, player_list, player_id, message):
139+
def _run_custom_cmd(function, websocket, world_pb2, player_list, player_id, message, world_data):
83140
args = message.split(' ')[1:]
84-
function(Connection(websocket, world_pb2, player_list), args, player_id)
141+
function(Connection(websocket, world_pb2, player_list, world_data), args, player_id)

PythonWalker/world.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
from binreader import BinaryReader
2+
from io import BytesIO
3+
import struct
4+
import json
5+
6+
def get_block_by_id(block_list, id):
7+
for block in block_list:
8+
if block.get("Id") == id:
9+
return block
10+
return None
11+
12+
def decode(data, block_list, width, height):
13+
reader = BinaryReader(BytesIO(data))
14+
world_data = {'width': width, 'height': height, 'bg': {}, 'fg': {}, 'ol': {}, 'block_list': block_list}
15+
i = 0
16+
for l in range(3):
17+
if l == 0:
18+
layer = 'bg'
19+
if l == 1:
20+
layer = 'fg'
21+
if l == 2:
22+
layer = 'ol'
23+
24+
for x in range(width):
25+
for y in range(height):
26+
i += 1
27+
id = reader.read_uint32()
28+
block = get_block_by_id(block_list, id)
29+
block_data = {'id': id, 'data': []}
30+
31+
if not block:
32+
raise ValueError(f"Id {id} is not a valid block id.")
33+
34+
if "BlockDataArgs" in block:
35+
for arg in block['BlockDataArgs']:
36+
block_data['data'].append(decode_argument(arg, reader))
37+
if not x in world_data[layer]:
38+
world_data[layer][x] = []
39+
world_data[layer][x].append(block_data)
40+
41+
return world_data
42+
43+
def decode_argument(arg, reader, endian='big'):
44+
if arg == 0:
45+
size = 0
46+
shift = 0
47+
while True:
48+
byte = ord(reader.read(1))
49+
size |= (byte & 0x7F) << shift
50+
shift += 7
51+
if (byte & 0x80) == 0:
52+
break
53+
string = reader.read_string(size)
54+
data = string
55+
elif arg == 1:
56+
data = reader.read_bool()
57+
elif arg == 3:
58+
if endian == 'big':
59+
data = reader.read_uint32()
60+
else:
61+
data = int.from_bytes(reader.read(4))
62+
elif arg == 7:
63+
data = reader.read_bool()
64+
elif arg == 8:
65+
data = int.from_bytes(reader.read(reader.read_ubyte()))
66+
elif arg == 9:
67+
uint32_color = reader.read_uint32()
68+
red = (uint32_color >> 16) & 0xFF
69+
green = (uint32_color >> 8) & 0xFF
70+
blue = uint32_color & 0xFF
71+
72+
hex_red = format(red, '02X')
73+
hex_green = format(green, '02X')
74+
hex_blue = format(blue, '02X')
75+
76+
data = f"#{hex_red}{hex_green}{hex_blue}"
77+
else:
78+
raise ValueError(f"Argument id {arg} is not a valid block argument.")
79+
80+
return data
81+
82+
def decode_block_placed_data(data):
83+
reader = BinaryReader(BytesIO(data))
84+
length = len(data)
85+
block_data = []
86+
while not reader.tell() >= length:
87+
arg = reader.read_ubyte()
88+
block_data.append(decode_argument(arg, reader, endian='little'))
89+
return block_data
90+
91+
def encode_argument(arg, data):
92+
if arg == 0:
93+
bytes = b''
94+
value = len(data)
95+
while True:
96+
byte = value & 0x7F
97+
value >>= 7
98+
if value > 0:
99+
byte |= 0x80
100+
bytes += byte.to_bytes(1)
101+
if value == 0:
102+
break
103+
bytes += data.encode('utf-8')
104+
elif arg == 1:
105+
if data:
106+
bytes = b'\x01'
107+
else:
108+
bytes = b'\x00'
109+
elif arg == 3:
110+
bytes = data.to_bytes(4)
111+
elif arg == 7:
112+
if data:
113+
bytes = b'\x01'
114+
else:
115+
bytes = b'\x00'
116+
elif arg == 8:
117+
bytes = len(data.to_bytes()).to_bytes() + data.to_bytes()
118+
elif arg == 9:
119+
if not data.startswith('#') or len(data) != 7:
120+
raise ValueError("Invalid hexadecimal color string format. Expected '#RRGGBB'.")
121+
122+
hex_red = data[1:3]
123+
hex_green = data[3:5]
124+
hex_blue = data[5:7]
125+
126+
red = int(hex_red, 16)
127+
green = int(hex_green, 16)
128+
blue = int(hex_blue, 16)
129+
130+
uint32_color = (red << 16) | (green << 8) | blue
131+
bytes = uint32_color.to_bytes(4, byteorder='little')
132+
else:
133+
raise ValueError(f"Argument id {arg} is not a valid block argument.")
134+
135+
return bytes
136+
137+
def encode_block_placed_data(id, block_list, data=None):
138+
block = get_block_by_id(block_list, id)
139+
if bool("BlockDataArgs" in block) ^ bool(data):
140+
raise ValueError("Block has data, but none given from user.")
141+
142+
if "BlockDataArgs" in block:
143+
i = 0
144+
block_data = b''
145+
for arg in block['BlockDataArgs']:
146+
block_data += arg.to_bytes(1)
147+
block_data += encode_argument(arg, data[i])
148+
i += 1
149+
else:
150+
block_data = None
151+
152+
return block_data
153+

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@ A Python based bot library for [PixelWalker](https://pixelwalker.net)
44
Examples can be found [here](https://github.com/Tycho10101/PythonWalker/tree/main/examples).
55

66
## Setup
7-
- In a command line, run `pip install PythonWalker`
7+
- In a command line, run `pip install PythonWalker` (add `--upgrade` when updating)
88
- Put `import PythonWalker as pw` in your main.py or main Python script
99

1010
## Setup (Manual)
1111
- Put the `/PythonWalker` directory from this repository into the root of your program
1212
- Put `import PythonWalker as pw` in your main.py or main Python script
13-
- Install the Protobuf, Requests, and Websockets libraries
13+
- Install Protobuf, binreader, Requests, and Websockets
1414

1515
## Features
1616
- Connect to rooms
1717
- Chat functions
18-
- Recive leave and join packets
18+
- Receive leave and join packets
1919
- Custom commands
20+
- Blocks
2021

2122
## Todo List
22-
- Blocks
2323
- Private Messaging
2424
- And more! (suggest them in the issues page)
2525

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
setup(
44
name="PythonWalker", # Replace with your project name
5-
version="0.2.3",
5+
version="0.3.0",
66
packages=find_packages(), # Automatically finds packages in your project
77
install_requires=[
88
"requests>=2.32.3",
99
"websockets>=13.1",
1010
"protobuf",
11+
"binreader",
1112
],
1213
author="Tycho10101",
1314
author_email="supallawma@gmail.com",

0 commit comments

Comments
 (0)