|
7 | 7 | from src.networking.packets.clientbound import HeldItemChange as HeldItemChangeClientbound |
8 | 8 | from src.networking.packets.clientbound import PlayerAbilities as PlayerAbilitiesClientbound |
9 | 9 |
|
| 10 | +from src.networking.packets.exceptions import InvalidPacketID |
| 11 | + |
10 | 12 | from cryptography.hazmat.backends import default_backend |
11 | 13 | from cryptography.hazmat.primitives.asymmetric import rsa |
12 | 14 | from cryptography.hazmat.primitives import serialization |
@@ -60,118 +62,114 @@ def handle_position(self, packet): |
60 | 62 | self.mc_connection.game_state.release() |
61 | 63 |
|
62 | 64 | def join_world(self): |
63 | | - self.mc_connection.game_state.acquire() |
| 65 | + # If there's an exception releasing a lock actually happens this way |
| 66 | + with self.mc_connection.game_state.state_lock: |
| 67 | + # Send the player all the packets that lets them join the world |
| 68 | + for id_ in self.mc_connection.game_state.join_ids: |
| 69 | + if id_ in self.mc_connection.game_state.packet_log: |
| 70 | + packet = self.mc_connection.game_state.packet_log[id_] |
| 71 | + self.connection.send_packet_buffer_raw(packet.compressed_buffer) |
| 72 | + |
| 73 | + # Send their player abilities |
| 74 | + self.connection.send_packet_raw(self.mc_connection.game_state.abilities) |
| 75 | + |
| 76 | + # Send them their last position/look if it exists |
| 77 | + if PlayerPositionAndLookClientbound.id in self.mc_connection.game_state.packet_log: |
| 78 | + if self.mc_connection and self.mc_connection.game_state.last_pos_packet: |
| 79 | + last_packet = self.mc_connection.game_state.last_pos_packet |
| 80 | + |
| 81 | + pos_packet = PlayerPositionAndLookClientbound( \ |
| 82 | + X=last_packet.X, Y=last_packet.Y, Z=last_packet.Z, \ |
| 83 | + Yaw=self.mc_connection.game_state.last_yaw, Pitch=self.mc_connection.game_state.last_pitch, Flags=0, \ |
| 84 | + TeleportID=self.mc_connection.game_state.teleport_id) |
| 85 | + self.mc_connection.game_state.teleport_id += 1 |
| 86 | + self.connection.send_packet_raw(pos_packet) |
| 87 | + else: |
| 88 | + self.connection.send_packet_buffer_raw( |
| 89 | + self.mc_connection.game_state.packet_log[PlayerPositionAndLookClientbound.id] \ |
| 90 | + .compressed_buffer) # Send the last packet that we got |
64 | 91 |
|
65 | | - # Send the player all the packets that lets them join the world |
66 | | - for id_ in self.mc_connection.game_state.join_ids: |
67 | | - if id_ in self.mc_connection.game_state.packet_log: |
68 | | - packet = self.mc_connection.game_state.packet_log[id_] |
69 | | - self.connection.send_packet_buffer_raw(packet.compressed_buffer) |
70 | | - |
71 | | - # Send their player abilities |
72 | | - self.connection.send_packet_raw(self.mc_connection.game_state.abilities) |
73 | | - |
74 | | - # Send them their last position/look if it exists |
75 | | - if PlayerPositionAndLookClientbound.id in self.mc_connection.game_state.packet_log: |
76 | | - if self.mc_connection and self.mc_connection.game_state.last_pos_packet: |
77 | | - last_packet = self.mc_connection.game_state.last_pos_packet |
78 | | - |
79 | | - pos_packet = PlayerPositionAndLookClientbound( \ |
80 | | - X=last_packet.X, Y=last_packet.Y, Z=last_packet.Z, \ |
81 | | - Yaw=self.mc_connection.game_state.last_yaw, Pitch=self.mc_connection.game_state.last_pitch, Flags=0, \ |
82 | | - TeleportID=self.mc_connection.game_state.teleport_id) |
83 | | - self.mc_connection.game_state.teleport_id += 1 |
84 | | - self.connection.send_packet_raw(pos_packet) |
85 | | - else: |
86 | | - self.connection.send_packet_buffer_raw( |
87 | | - self.mc_connection.game_state.packet_log[PlayerPositionAndLookClientbound.id] \ |
88 | | - .compressed_buffer) # Send the last packet that we got |
89 | | - |
90 | | - if TimeUpdate.id in self.mc_connection.game_state.packet_log: |
91 | | - self.connection.send_packet_buffer_raw(self.mc_connection.game_state.packet_log\ |
92 | | - [TimeUpdate.id].compressed_buffer) |
93 | | - |
94 | | - # Send the player list items (to see other players) |
95 | | - self.connection.send_single_packet_dict(self.mc_connection.game_state.player_list) |
96 | | - |
97 | | - # Send all loaded chunks |
98 | | - print("Sending chunks", flush=True) |
99 | | - self.connection.send_single_packet_dict(self.mc_connection.game_state.chunks) |
100 | | - print("Done sending chunks", flush=True) |
101 | | - |
102 | | - # Send the player all the currently loaded entities |
103 | | - self.connection.send_single_packet_dict(self.mc_connection.game_state.entities) |
104 | | - |
105 | | - # Player sends ClientStatus, this is important for respawning if died |
106 | | - self.mc_connection.send_packet_raw(ClientStatus(ActionID=0)) |
107 | | - |
108 | | - # Send their last held item |
109 | | - self.connection.send_packet_raw(HeldItemChangeClientbound(Slot=self.mc_connection.game_state.held_item_slot)) |
110 | | - |
111 | | - # Send their current game state |
112 | | - self.connection.send_packet_raw(GameState(Reason=self.mc_connection.game_state.gs_reason,\ |
113 | | - Value=self.mc_connection.game_state.gs_value)) |
114 | | - # Send their inventory |
115 | | - self.connection.send_single_packet_dict(self.mc_connection.game_state.main_inventory) |
| 92 | + if TimeUpdate.id in self.mc_connection.game_state.packet_log: |
| 93 | + self.connection.send_packet_buffer_raw(self.mc_connection.game_state.packet_log\ |
| 94 | + [TimeUpdate.id].compressed_buffer) |
116 | 95 |
|
117 | | - self.mc_connection.game_state.release() |
| 96 | + # Send the player list items (to see other players) |
| 97 | + self.connection.send_single_packet_dict(self.mc_connection.game_state.player_list) |
| 98 | + |
| 99 | + # Send all loaded chunks |
| 100 | + print("Sending chunks", flush=True) |
| 101 | + self.connection.send_single_packet_dict(self.mc_connection.game_state.chunks) |
| 102 | + print("Done sending chunks", flush=True) |
| 103 | + |
| 104 | + # Send the player all the currently loaded entities |
| 105 | + self.connection.send_single_packet_dict(self.mc_connection.game_state.entities) |
| 106 | + |
| 107 | + # Player sends ClientStatus, this is important for respawning if died |
| 108 | + self.mc_connection.send_packet_raw(ClientStatus(ActionID=0)) |
| 109 | + |
| 110 | + # Send their last held item |
| 111 | + self.connection.send_packet_raw(HeldItemChangeClientbound(Slot=self.mc_connection.game_state.held_item_slot)) |
| 112 | + |
| 113 | + # Send their current game state |
| 114 | + self.connection.send_packet_raw(GameState(Reason=self.mc_connection.game_state.gs_reason,\ |
| 115 | + Value=self.mc_connection.game_state.gs_value)) |
| 116 | + # Send their inventory |
| 117 | + self.connection.send_single_packet_dict(self.mc_connection.game_state.main_inventory) |
118 | 118 |
|
119 | 119 | def setup(self): |
120 | | - print("Reading handshake", flush=True) |
| 120 | + try: |
| 121 | + print("Reading handshake", flush=True) |
121 | 122 |
|
122 | | - pkt = self.read_packet_from_stream() |
123 | | - if pkt is None: |
124 | | - return False |
125 | | - Handshake().read(pkt.packet_buffer) |
| 123 | + Handshake().read(self.read_packet_from_stream().packet_buffer) |
126 | 124 |
|
127 | | - print("Reading login start", flush=True) |
128 | | - pkt = self.read_packet_from_stream() |
129 | | - if pkt is None: |
130 | | - return False |
131 | | - LoginStart().read(pkt.packet_buffer) |
| 125 | + print("Reading login start", flush=True) |
| 126 | + LoginStart().read(self.read_packet_from_stream().packet_buffer) |
132 | 127 |
|
133 | | - # Generate a dummy (pubkey, privkey) pair |
134 | | - privkey = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) |
135 | | - pubkey = privkey.public_key().public_bytes(encoding=serialization.Encoding.DER, |
136 | | - format=serialization.PublicFormat.SubjectPublicKeyInfo) |
| 128 | + # Generate a dummy (pubkey, privkey) pair |
| 129 | + privkey = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) |
| 130 | + pubkey = privkey.public_key().public_bytes(encoding=serialization.Encoding.DER, |
| 131 | + format=serialization.PublicFormat.SubjectPublicKeyInfo) |
137 | 132 |
|
138 | | - print("Trying to send encryption request", flush=True) |
139 | | - self.connection.send_packet_raw( |
140 | | - EncryptionRequest(ServerID='', PublicKey=pubkey, VerifyToken=self.mc_connection.VerifyToken)) |
| 133 | + print("Trying to send encryption request", flush=True) |
| 134 | + self.connection.send_packet_raw( |
| 135 | + EncryptionRequest(ServerID='', PublicKey=pubkey, VerifyToken=self.mc_connection.VerifyToken)) |
141 | 136 |
|
142 | | - print("Encryption request sent", flush=True) |
| 137 | + print("Encryption request sent", flush=True) |
143 | 138 |
|
144 | | - # The encryption response will be encrypted with the server's public key |
145 | | - # Luckily, when this goes wrong read_packet returns None |
146 | | - _ = self.read_packet_from_stream() |
| 139 | + # The encryption response will be encrypted with the server's public key |
| 140 | + # Luckily, when this goes wrong read_packet returns None |
| 141 | + _ = self.read_packet_from_stream() |
147 | 142 |
|
148 | | - if _ is None: |
149 | | - print("Invalid encryption response!", flush=True) |
150 | | - self.connection.on_disconnect() |
151 | | - return False |
| 143 | + if _ is None: |
| 144 | + print("Invalid encryption response!", flush=True) |
| 145 | + self.connection.on_disconnect() |
| 146 | + return False |
152 | 147 |
|
153 | | - encryption_response = EncryptionResponse().read(_.packet_buffer) |
| 148 | + encryption_response = EncryptionResponse().read(_.packet_buffer) |
154 | 149 |
|
155 | | - # Decrypt and verify the verify token |
156 | | - verify_token = privkey.decrypt(encryption_response.VerifyToken, PKCS1v15()) |
157 | | - assert (verify_token == self.mc_connection.VerifyToken) |
| 150 | + # Decrypt and verify the verify token |
| 151 | + verify_token = privkey.decrypt(encryption_response.VerifyToken, PKCS1v15()) |
| 152 | + assert (verify_token == self.mc_connection.VerifyToken) |
158 | 153 |
|
159 | | - # Decrypt the shared secret |
160 | | - shared_secret = privkey.decrypt(encryption_response.SharedSecret, PKCS1v15()) |
| 154 | + # Decrypt the shared secret |
| 155 | + shared_secret = privkey.decrypt(encryption_response.SharedSecret, PKCS1v15()) |
161 | 156 |
|
162 | | - # Enable encryption using the shared secret |
163 | | - self.connection.enable_encryption(shared_secret) |
| 157 | + # Enable encryption using the shared secret |
| 158 | + self.connection.enable_encryption(shared_secret) |
164 | 159 |
|
165 | | - # Enable compression and assign the threshold to the connection |
166 | | - if self.mc_connection.compression_threshold >= 0: |
167 | | - self.connection.send_packet_raw(SetCompression(Threshold=self.mc_connection.compression_threshold)) |
168 | | - self.connection.compression_threshold = self.mc_connection.compression_threshold |
| 160 | + # Enable compression and assign the threshold to the connection |
| 161 | + if self.mc_connection.compression_threshold >= 0: |
| 162 | + self.connection.send_packet_raw(SetCompression(Threshold=self.mc_connection.compression_threshold)) |
| 163 | + self.connection.compression_threshold = self.mc_connection.compression_threshold |
169 | 164 |
|
170 | | - self.connection.send_packet_raw(self.mc_connection.login_success) |
| 165 | + self.connection.send_packet_raw(self.mc_connection.login_success) |
171 | 166 |
|
172 | | - print("Joining world", flush=True) |
173 | | - self.join_world() |
174 | | - print("Finished joining world", flush=True) |
| 167 | + print("Joining world", flush=True) |
| 168 | + self.join_world() |
| 169 | + print("Finished joining world", flush=True) |
| 170 | + except (ValueError, EOFError, InvalidPacketID, ConnectionRefusedError, ConnectionAbortedError, \ |
| 171 | + ConnectionResetError): |
| 172 | + return False |
175 | 173 |
|
176 | 174 | # Let the real connection know about our client |
177 | 175 | # Now the client can start receiving forwarded data |
|
0 commit comments