# I'll probably split this file into more parts later on, like such:
# proxy folder: __init__.py (Proxy), client.py (Client), server.py (Server), network.py (Packet), bot.py (will contain Bot, for bot code)
# this could definitely use some code-cleaning.
import socket, threading, struct, StringIO, time, traceback, json, random, hashlib, os, zlib, binascii, uuid, md5, storage, world
from config import Config
try: # Weird system for handling non-standard modules
import encryption, requests
IMPORT_SUCCESS = True
except:
IMPORT_SUCCESS = False
[docs]class Proxy:
def __init__(self, wrapper):
self.wrapper = wrapper
self.server = wrapper.server
self.socket = False
self.isServer = False
self.clients = []
self.skins = {}
self.uuidTranslate = {}
self.storage = storage.Storage("proxy-data")
self.privateKey = encryption.generate_key_pair()
self.publicKey = encryption.encode_public_key(self.privateKey)
[docs] def host(self):
# get the protocol version from the server
while not self.wrapper.server.status == 2:
time.sleep(.2)
self.pollServer()
while not self.socket:
time.sleep(1)
try:
self.socket = socket.socket()
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((self.wrapper.config["Proxy"]["proxy-bind"], self.wrapper.config["Proxy"]["proxy-port"]))
self.socket.listen(5)
except:
print "Proxy mode could not bind - retrying"
self.socket = False
while not self.wrapper.halt:
try:
sock, addr = self.socket.accept()
client = Client(sock, addr, self.wrapper, self.publicKey, self.privateKey, self)
t = threading.Thread(target=client.handle, args=())
t.daemon = True
t.start()
self.clients.append(client)
# remove stale clients
for i, client in enumerate(self.wrapper.proxy.clients):
if client.abort:
del self.wrapper.proxy.clients[i]
except:
print traceback.print_exc()
try:
client.disconnect("Some error")
except:
pass
[docs] def pollServer(self):
sock = socket.socket()
sock.connect(("localhost", self.wrapper.config["Proxy"]["server-port"]))
packet = Packet(sock, self)
packet.send(0x00, "varint|string|ushort|varint", (5, "localhost", self.wrapper.config["Proxy"]["server-port"], 1))
packet.send(0x00, "", ())
packet.flush()
while True:
id, original = packet.grabPacket()
if id == 0x00:
data = json.loads(packet.read("string:response")["response"])
self.wrapper.server.protocolVersion = data["version"]["protocol"]
self.wrapper.server.maxPlayers = self.wrapper.config["Proxy"]["max-players"]
break
sock.close()
[docs] def getClientByServerUUID(self, id):
for client in self.clients:
if str(client.serverUUID) == str(id):
self.uuidTranslate[str(id)] = str(client.uuid)
return client
if str(id) in self.uuidTranslate:
return uuid.UUID(hex=self.uuidTranslate[str(id)])
[docs] def lookupUUID(self, uuid):
#if not self.storage.key("uuid-cache"):
# self.storage.key("uuid-cache", {})
if "uuid-cache" not in self.storage:
self.storage["uuid-cache"] = {}
if uuid in self.storage["uuid-cache"]:
return self.storage["uuid-cache"][uuid]
#with open("usercache.json", "r") as f:
# usercache = json.loads(f.read())
#for i in usercache:
# if i["uuid"] == str(uuid):
# return i
return None
[docs] def lookupUsername(self, username):
if "uuid-cache" not in self.storage:
self.storage["uuid-cache"] = {}
for uuid in self.storage["uuid-cache"]:
if self.storage["uuid-cache"][uuid]["name"] == username:
return uuid
r = requests.get("https://api.mojang.com/users/profiles/minecraft/%s" % username)
uuid = self.formatUUID(r.json()["id"])
self.setUUID(uuid, username)
return uuid
[docs] def setUUID(self, uuid, name):
if not self.storage.key("uuid-cache"):
self.storage.key("uuid-cache", {})
self.storage.key("uuid-cache")[str(uuid)] = {"uuid": str(uuid), "name": name, "expiresOn": time.strftime("%Y-%m-%d %H:%M:%S %z")}
[docs] def banUUID(self, uuid, reason="Banned by an operator", source="Server"):
if not self.storage.key("banned-uuid"):
self.storage.key("banned-uuid", {})
self.storage.key("banned-uuid")[str(uuid)] = {"reason": reason, "source": source, "created": time.time(), "name": self.lookupUUID(uuid)["name"]}
[docs] def isUUIDBanned(self, uuid): # Check if the UUID of the user is banned
if not self.storage.key("banned-uuid"):
self.storage.key("banned-uuid", {})
if uuid in self.storage.key("banned-uuid"):
return True
else:
return False
[docs] def isAddressBanned(self, address): # Check if the IP address is banned
if not self.storage.key("banned-address"):
self.storage.key("banned-address", {})
if address in self.storage.key("banned-address"):
return True
else:
return False
[docs]class Client: # handle client/game connection
def __init__(self, socket, addr, wrapper, publicKey, privateKey, proxy):
self.socket = socket
self.wrapper = wrapper
self.config = wrapper.config
self.socket = socket
self.publicKey = publicKey
self.privateKey = privateKey
self.proxy = proxy
self.addr = addr
self.abort = False
self.log = wrapper.log
self.tPing = time.time()
self.server = None
self.isServer = False
self.isLocal = True
self.uuid = None
self.serverUUID = None
self.server = None
self.address = None
self.handshake = False
self.state = 0 # 0 = init, 1 = motd, 2 = login, 3 = active, 4 = authorizing
self.packet = Packet(self.socket, self)
self.send = self.packet.send
self.read = self.packet.read
self.sendRaw = self.packet.sendRaw
self.username = None
self.gamemode = 0
self.dimension = 0
self.position = (0, 0, 0)
self.inventory = {}
self.slot = 0
self.windowCounter = 2
for i in range(45): self.inventory[i] = None
[docs] def connect(self, ip=None, port=None):
if not self.server == None:
self.address = (ip, port)
if not ip == None:
self.server_temp = Server(self, self.wrapper, ip, port)
try:
self.server_temp.connect()
self.server.close(kill_client=False)
self.server = self.server_temp
except:
self.server_temp.close(kill_client=False)
self.server_temp = None
self.send(0x02, "string|byte", ("{text:'Could not connect to that server!', color:red, bold:true}", 0))
self.address = None
return
else:
self.server = Server(self, self.wrapper, ip, port)
self.server.connect()
t = threading.Thread(target=self.server.handle, args=())
t.daemon = True
t.start()
self.server.send(0x00, "varint|string|ushort|varint", (self.version, "localhost", self.config["Proxy"]["server-port"], 2))
self.server.send(0x00, "string", (self.username,))
# self.server.send(0x46, "varint", (-1,))
# self.server.packet.compression = True
# self.packet.compression = True
self.server.state = 2
[docs] def close(self):
self.abort = True
try:
self.socket.close()
except:
pass
if self.server:
self.server.abort = True
self.server.close()
for i, client in enumerate(self.wrapper.proxy.clients):
if client.username == self.username:
del self.wrapper.proxy.clients[i]
[docs] def disconnect(self, message):
print "Client disconnecting: %s" % message
if self.state == 3:
self.send(0x40, "json", ({"text": message, "color": "red"},))
else:
self.send(0x00, "json", ({"text": message, "color": "red"},))
time.sleep(1)
self.close()
[docs] def flush(self):
while not self.abort:
self.packet.flush()
time.sleep(0.05)
# UUID operations
[docs] def UUIDIntToHex(self, uuid):
uuid = uuid.encode("hex")
uuid = "%s-%s-%s-%s-%s" % (uuid[:8], uuid[8:12], uuid[12:16], uuid[16:20], uuid[20:])
return uuid
[docs] def UUIDHexToInt(self, uuid):
uuid = uuid.replace("-", "").decode("hex")
return uuid
[docs] def UUIDFromName(self, name):
m = md5.new()
m.update(name)
d = bytearray(m.digest())
d[6] &= 0x0f
d[6] |= 0x30
d[8] &= 0x3f
d[8] |= 0x80
return uuid.UUID(bytes=str(d))
[docs] def getPlayerObject(self):
if self.username in self.wrapper.server.players:
return self.wrapper.server.players[self.username]
return False
[docs] def parse(self, id):
if id == 0x00:
if self.state == 0:
data = self.read("varint:version|string:address|ushort:port|varint:state")
self.version = data["version"]
self.wrapper.server.protocolVersion = self.version
self.packet.version = self.version
if not self.wrapper.server.status == 2:
self.disconnect("Server has not finished booting. Please try connecting again in a few seconds")
return
if data["state"] in (1, 2):
self.state = data["state"]
else:
self.disconnect("Invalid state '%d'" % data["state"])
return False
elif self.state == 1:
sample = []
for i in self.wrapper.server.players:
player = self.wrapper.server.players[i]
sample.append({"name": player.username, "id": str(player.uuid)})
MOTD = {"description": self.config["Proxy"]["motd"],
"players": {"max": self.wrapper.server.maxPlayers, "online": len(self.wrapper.server.players), "sample": sample},
"version": {"name": self.wrapper.server.version, "protocol": self.wrapper.server.protocolVersion}
}
if os.path.exists("server-icon.png"):
f = open("server-icon.png", "r")
serverIcon = "data:image/png;base64," + f.read().encode("base64")
f.close()
MOTD["favicon"] = serverIcon
self.send(0x00, "string", (json.dumps(MOTD),))
self.state = 5
return False
elif self.state == 2:
data = self.read("string:username")
self.username = data["username"]
if self.config["Proxy"]["online-mode"]:
self.state = 4
self.verifyToken = encryption.generate_challenge_token()
self.serverID = encryption.generate_server_id()
self.send(0x01, "string|bytearray|bytearray", (self.serverID, self.publicKey, self.verifyToken))
else:
self.connect()
self.uuid = uuid.uuid3(uuid.NAMESPACE_OID, "OfflinePlayer: %s" % self.username)
self.serverUUID = self.UUIDFromName("OfflinePlayer:" + self.username)
self.send(0x02, "string|string", (str(self.uuid), self.username))
self.state = 3
self.log.info("%s logged in (IP: %s)" % (self.username, self.addr[0]))
return False
elif self.state == 3:
return False
if id == 0x01:
if self.state == 3: # chat packet
if not self.isLocal == True: return True
data = self.read("string:message")
if data is None: return False
try:
if not self.wrapper.callEvent("player.rawMessage", {"player": self.getPlayerObject(), "message": data["message"]}): return False
if data["message"][0] == "/":
def args(i):
try: return data["message"].split(" ")[i]
except: return ""
def argsAfter(i):
try: return data["message"].split(" ")[i:]
except: return ""
return self.wrapper.callEvent("player.runCommand", {"player": self.getPlayerObject(), "command": args(0)[1:], "args": argsAfter(1)})
except:
print traceback.format_exc()
elif self.state == 4: # encryption response packet
data = self.read("bytearray:shared_secret|bytearray:verify_token")
sharedSecret = encryption.decrypt_shared_secret(data["shared_secret"], self.privateKey)
verifyToken = encryption.decrypt_shared_secret(data["verify_token"], self.privateKey)
h = hashlib.sha1()
h.update(self.serverID)
h.update(sharedSecret)
h.update(self.publicKey)
serverId = self.packet.hexdigest(h)
r = requests.get("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s" % (self.username, serverId))
# print "SessionServer response: %s" % r.text
self.packet.sendCipher = encryption.AES128CFB8(sharedSecret)
self.packet.recvCipher = encryption.AES128CFB8(sharedSecret)
if not verifyToken == self.verifyToken:
self.disconnect("Verify tokens are not the same")
return False
try:
data = r.json()
self.uuid = data["id"]
self.uuid = "%s-%s-%s-%s-%s" % (self.uuid[:8], self.uuid[8:12], self.uuid[12:16], self.uuid[16:20], self.uuid[20:])
self.uuid = uuid.UUID(self.uuid)
if not data["name"] == self.username:
self.disconnect("Client's username did not match Mojang's record")
return False
for property in data["properties"]:
if property["name"] == "textures":
self.skinBlob = property["value"]
self.wrapper.proxy.skins[self.uuid] = self.skinBlob
self.properties = data["properties"]
except:
self.disconnect("Session Server Error")
return False
if self.proxy.lookupUUID(self.uuid):
self.username = self.proxy.lookupUUID(self.uuid)["name"]
self.serverUUID = self.UUIDFromName("OfflinePlayer:" + self.username)
if self.version > 26:
self.packet.setCompression(256)
# Ban code should go here
self.send(0x02, "string|string", (str(self.uuid), self.username))
self.state = 3
self.connect()
self.log.info("%s logged in (UUID: %s | IP: %s)" % (self.username, self.uuid, self.addr[0]))
self.proxy.setUUID(self.uuid, self.username)
return False
elif self.state == 5: # ping packet during status request
keepAlive = self.read("long:keepAlive")["keepAlive"]
self.send(0x01, "long", (keepAlive,))
if id == 0x04:
data = self.read("double:x|double:y|double:z|bool:on_ground")
#objection = self.wrapper.callEvent("player.move", {"player": self.username, "xyz": (data["x"], data["y"], data["z"]), "on_ground": data["on_ground"]})
self.position = (data["x"], data["y"], data["z"])
#else:
#self.wrapper.server.run("tp %s %d %d %d" % (self.username, data["x"], data["y"], data["z"]))
if id == 0x06:
data = self.read("double:x|double:y|double:z|float:yaw|float:pitch|bool:on_ground")
#objection = self.wrapper.callEvent("player.move", {"player": self.username, "xyz": (data["x"], data["y"], data["z"]), "on_ground": data["on_ground"]})
self.position = (data["x"], data["y"], data["z"])
if self.server.state is not 3: return False
if id == 0x07: # Player Block Dig
if not self.isLocal == True: return True
if self.version < 6:
data = self.read("byte:status|int:x|ubyte:y|int:z|byte:face")
position = (data["x"], data["y"], data["z"])
else:
data = self.read("byte:status|position:position|byte:face")
position = data["position"]
if data is None: return False
if data["status"] == 2:
if not self.wrapper.callEvent("player.dig", {"player": self.getPlayerObject(), "position": position, "action": "end_break", "face": data["face"]}): return False
if data["status"] == 0:
if not self.gamemode == 1:
if not self.wrapper.callEvent("player.dig", {"player": self.getPlayerObject(), "position": position, "action": "begin_break", "face": data["face"]}): return False
else:
if not self.wrapper.callEvent("player.dig", {"player": self.getPlayerObject(), "position": position, "action": "end_break", "face": data["face"]}): return False
if self.server.state is not 3: return False
if id == 0x08: # Player Block Placement
if not self.isLocal == True: return True
if self.version < 6:
data = self.read("int:x|ubyte:y|int:z|byte:face|slot:item")
position = (data["x"], data["y"], data["z"])
else:
data = self.read("position:position|byte:face|slot:item")
position = data["position"]
position = data["position"]
if position == None:
if not self.wrapper.callEvent("player.action", {"player": self.getPlayerObject()}): return False
else:
face = data["face"]
if face == 0: # Compensate
position = (position[0], position[1] - 1, position[2])
elif face == 1:
position = (position[0], position[1] + 1, position[2])
elif face == 2:
position = (position[0], position[1], position[2] - 1)
elif face == 3:
position = (position[0], position[1], position[2] + 1)
elif face == 4:
position = (position[0] - 1, position[1], position[2])
elif face == 5:
position = (position[0] + 1, position[1], position[2])
if not self.wrapper.callEvent("player.place", {"player": self.getPlayerObject(), "position": position, "item": data["item"]}): return False
if self.server.state is not 3: return False
if id == 0x09: # Held Item Change
slot = self.read("short:short")["short"]
if self.slot > -1 and self.slot < 9:
self.slot = slot
else:
return False
return True
[docs] def handle(self):
t = threading.Thread(target=self.flush, args=())
t.daemon = True
t.start()
try:
while not self.abort:
try:
id, original = self.packet.grabPacket()
self.original = original
except EOFError:
self.close()
break
except:
if Config.debug:
print "Failed to grab packet (CLIENT):"
print traceback.format_exc()
self.close()
break
if time.time() - self.tPing > 1 and self.state == 3:
if self.version > 32:
self.send(0x00, "varint", (random.randrange(0, 99999),))
else:
self.send(0x00, "int", (random.randrange(0, 99999),))
self.tPing = time.time()
if self.parse(id) and self.server:
if self.server.state == 3:
self.server.sendRaw(original)
except:
print "Error in the Client->Server method:"
print traceback.format_exc()
[docs]class Server: # Handle Server Connection
def __init__(self, client, wrapper, ip=None, port=None):
self.client = client
self.wrapper = wrapper
self.ip = ip
self.port = port
self.abort = False
self.isServer = True
self.proxy = wrapper.proxy
self.lastPacketIDs = []
self.state = 0 # 0 = init, 1 = motd, 2 = login, 3 = active, 4 = authorizing
self.packet = None
self.version = self.wrapper.server.protocolVersion
self.log = wrapper.log
self.safe = False
[docs] def connect(self):
self.socket = socket.socket()
if self.ip == None:
self.socket.connect(("localhost", self.wrapper.config["Proxy"]["server-port"]))
else:
self.socket.connect((self.ip, self.port))
self.client.isLocal = False
self.packet = Packet(self.socket, self)
self.packet.version = self.client.version
self.username = self.client.username
self.send = self.packet.send
self.read = self.packet.read
self.sendRaw = self.packet.sendRaw
t = threading.Thread(target=self.flush, args=())
t.daemon = True
t.start()
[docs] def close(self, reason="Disconnected", kill_client=True):
if Config.debug:
print "Last packet IDs (Server->Client) before disconnection:"
print self.lastPacketIDs
self.abort = True
self.packet = None
try:
self.socket.close()
except:
pass
if self.client.isLocal == False and kill_client:
self.client.isLocal = True
self.client.send(0x02, "string|byte", ("{text:'Disconnected from server: %s', color:red}" % reason.replace("'", "\\'"), 0))
self.client.connect()
return
# I may remove this later so the client can remain connected upon server disconnection
# self.client.send(0x02, "string|byte", (json.dumps({"text": "Disconnected from server. Reason: %s" % reason, "color": "red"}),0))
# self.abort = True
# self.client.connect()
if kill_client:
self.client.abort = True
self.client.server = None
self.client.close()
[docs] def flush(self):
while not self.abort:
self.packet.flush()
# try:
# self.packet.flush()
# except:
# print "Error while flushing, stopping"
# print traceback.format_exc()
# self.close()
# break
time.sleep(0.05)
[docs] def parse(self, id, original):
if id == 0x00:
if self.state < 3:
message = self.read("string:string")
self.log.info("Disconnected from Server: %s" % message["string"])
elif self.state == 3:
if self.client.version > 7:
id = self.read("int:i")["i"]
if not id == None:
self.send(0x00, "varint", (id,))
return False
if id == 0x01:
if self.state == 3:
data = self.read("int:eid|ubyte:gamemode|byte:dimension|ubyte:difficulty|ubyte:max_players|string:level_type")
self.client.gamemode = data["gamemode"]
self.client.dimension = data["dimension"]
if self.client.handshake:
self.client.send(0x07, "int|ubyte|ubyte|string", (self.client.dimension, data["difficulty"], data["gamemode"], data["level_type"]))
self.safe = True
return False
else:
self.safe = True
self.client.handshake = True
elif self.state == 2:
self.client.disconnect("Server is online mode. Please turn it off in server.properties.\n\nWrapper.py will handle authentication on its own, so do not worry about hackers.")
return False
if id == 0x02:
if self.state == 2: # Login Success - UUID & Username are sent in this packet
self.state = 3
return False
elif self.state == 3:
try:
data = json.loads(self.read("string:json")["json"])
except: pass
if not self.wrapper.callEvent("player.chatbox", {"player": self.client.getPlayerObject(), "json": data}): return False
try:
if data["translate"] == "chat.type.admin": return False
except: pass
if id == 0x03:
if self.state == 2:
data = self.read("varint:threshold")
if not data["threshold"] == -1:
self.packet.compression = True
self.packet.compressThreshold = data["threshold"]
else:
self.packet.compression = False
self.packet.compressThreshold = -1
return False
if id == 0x05:
data = self.read("int:x|int:y|int:z")
self.wrapper.server.spawnPoint = (data["x"], data["y"], data["z"])
if id == 0x0c: # Spawn Player
data = self.read("varint:eid|uuid:uuid|int:x|int:y|int:z|byte:yaw|byte:pitch|short:item|rest:metadata")
if self.proxy.getClientByServerUUID(data["uuid"]):
self.client.send(0x0c, "varint|uuid|int|int|int|byte|byte|short|raw", (
data["eid"],
self.proxy.getClientByServerUUID(data["uuid"]).uuid,
data["x"],
data["y"],
data["z"],
data["yaw"],
data["pitch"],
data["item"],
data["metadata"]))
return False
# if id == 0x21: # Chunk Data
# if self.client.packet.compressThreshold == -1:
# print "CLIENT COMPRESSION ENABLED"
# self.client.packet.setCompression(256)
if id == 0x26: # Map Chunk Bulk
data = self.read("bool:skylight|varint:chunks")
chunks = []
for i in range(data["chunks"]):
meta = self.read("int:x|int:z|ushort:primary")
chunks.append(meta)
for i in range(data["chunks"]):
meta = chunks[i]
bitmask = bin(meta["primary"])[2:].zfill(16)
primary = []
for i in bitmask:
if i == "0": primary.append(False)
if i == "1": primary.append(True)
chunkColumn = bytearray()
for i in primary:
if i == True:
chunkColumn += bytearray(self.packet.read_data(16*16*16 * 2)) # packetanisc
metalight = bytearray(self.packet.read_data(16*16*16))
if data["skylight"]:
skylight = bytearray(self.packet.read_data(16*16*16))
else:
chunkColumn += bytearray(16*16*16 * 2) # Null Chunk
#self.wrapper.server.world.setChunk(meta["x"], meta["z"], world.Chunk(chunkColumn, meta["x"], meta["z"]))
#print "Reading chunk %d,%d" % (meta["x"], meta["z"])
if id == 0x2b: # Change Game State
data = self.read("ubyte:reason|float:value")
if data["reason"] == 3:
self.client.gamemode = data["value"]
if id == 0x2f:
data = self.read("byte:wid|short:slot|slot:data")
if data["wid"] == 0:
self.client.inventory[data["slot"]] = data["data"]
if id == 0x40:
message = self.read("json:json")["json"]
self.log.info("Disconnected from server: %s" % message)
if self.client.isLocal == False:
self.server.close(message)
else:
self.client.disconnect(message)
return False
if id == 0x38:
head = self.read("varint:action|varint:length")
z = 0
while z < head["length"]:
serverUUID = self.read("uuid:uuid")["uuid"]
client = self.client.proxy.getClientByServerUUID(serverUUID)
if not client:
z += 1
continue
z += 1
if head["action"] == 0:
properties = client.properties
raw = ""
for i in properties:
raw += self.client.packet.send_string(i["name"]) # name
raw += self.client.packet.send_string(i["value"]) # value
if "signature" in i:
raw += self.client.packet.send_bool(True)
raw += self.client.packet.send_string(i["signature"]) # signature
else:
raw += self.client.packet.send_bool(False)
raw += self.client.packet.send_varInt(0)
raw += self.client.packet.send_varInt(0)
raw += self.client.packet.send_bool(False)
self.client.send(0x38, "varint|varint|uuid|string|varint|raw", (0, 1, client.uuid, client.username, len(properties), raw))
elif head["action"] == 1:
data = self.read("varint:gamemode")
self.client.send(0x38, "varint|varint|uuid|varint", (1, 1, client.uuid, data["gamemode"]))
elif head["action"] == 2:
data = self.read("varint:ping")
self.client.send(0x38, "varint|varint|uuid|varint", (2, 1, client.uuid, data["ping"]))
elif head["action"] == 3:
data = self.read("bool:has_display")
if data["has_display"]:
data = self.read("string:displayname")
self.client.send(0x38, "varint|varint|uuid|bool|string", (3, 1, client.uuid, True, data["displayname"]))
else:
self.client.send(0x38, "varint|varint|uuid|varint", (3, 1, client.uuid, False))
elif head["action"] == 4:
self.client.send(0x38, "varint|varint|uuid", (4, 1, client))
return False
return True
[docs] def handle(self):
try:
while not self.abort:
try:
id, original = self.packet.grabPacket()
self.lastPacketIDs.append((hex(id), len(original)))
if len(self.lastPacketIDs) > 10:
for i,v in enumerate(self.lastPacketIDs):
del self.lastPacketIDs[i]
break
except EOFError:
print traceback.format_exc()
self.close()
break
except:
if Config.debug:
print "Failed to grab packet (SERVER)"
print traceback.format_exc()
#self.disconnect("Internal Wrapper.py Error")
# break
if self.client.abort:
self.close()
break
try:
if self.parse(id, original) and self.safe:
self.client.sendRaw(original)
except:
self.log.debug("Could not parse packet, connection may crumble:")
self.log.debug(traceback.format_exc())
except:
if Config.debug:
print "Error in the Server->Client method:"
print traceback.format_exc()
[docs]class Packet: # PACKET PARSING CODE
def __init__(self, socket, obj):
self.socket = socket
self.obj = obj
self.recvCipher = None
self.sendCipher = None
self.compressThreshold = -1
self.version = 5
self.bonk = False
self.buffer = StringIO.StringIO()
self.queue = []
[docs] def hexdigest(self, sh):
d = long(sh.hexdigest(), 16)
if d >> 39 * 4 & 0x8:
return "-%x" % ((-d) & (2 ** (40 * 4) - 1))
return "%x" % d
[docs] def grabPacket(self):
length = self.unpack_varInt()
# if length == 0: return None
# if length > 256:
# print "Length: %d" % length
dataLength = 0
if not self.compressThreshold == -1:
dataLength = self.unpack_varInt()
length = length - len(self.pack_varInt(dataLength))
payload = self.recv(length)
if dataLength > 0:
payload = zlib.decompress(payload)
self.buffer = StringIO.StringIO(payload)
id = self.read_varInt()
return (id, payload)
[docs] def pack_varInt(self, val):
total = b''
if val < 0:
val = (1<<32)+val
while val>=0x80:
bits = val&0x7F
val >>= 7
total += struct.pack('B', (0x80|bits))
bits = val&0x7F
total += struct.pack('B', bits)
return total
[docs] def unpack_varInt(self):
total = 0
shift = 0
val = 0x80
while val&0x80:
val = struct.unpack('B', self.recv(1))[0]
total |= ((val&0x7F)<<shift)
shift += 7
if total&(1<<31):
total = total - (1<<32)
return total
[docs] def setCompression(self, threshold):
# self.sendRaw("\x03\x80\x02")
self.send(0x03, "varint", (threshold,))
self.compressThreshold = threshold
#time.sleep(1.5)
[docs] def flush(self):
for p in self.queue:
packet = p[1]
id = struct.unpack("B", packet[0])[0]
if p[0] > -1: # p[0] > -1:
if len(packet) > self.compressThreshold:
packetCompressed = self.pack_varInt(len(packet)) + zlib.compress(packet)
packet = self.pack_varInt(len(packetCompressed)) + packetCompressed
else:
packet = self.pack_varInt(0) + packet
packet = self.pack_varInt(len(packet)) + packet
else:
packet = self.pack_varInt(len(packet)) + packet
# if not self.obj.isServer:
# print packet.encode("hex")
if self.sendCipher is None:
self.socket.send(packet)
else:
self.socket.send(self.sendCipher.encrypt(packet))
self.queue = []
[docs] def sendRaw(self, payload):
self.queue.append((self.compressThreshold, payload))
# -- SENDING AND PARSING PACKETS -- #
[docs] def read(self, expression):
result = {}
for exp in expression.split("|"):
type = exp.split(":")[0]
name = exp.split(":")[1]
try:
if type == "string": result[name] = self.read_string()
if type == "json": result[name] = self.read_json()
if type == "ubyte": result[name] = self.read_ubyte()
if type == "byte": result[name] = self.read_byte()
if type == "int": result[name] = self.read_int()
if type == "short": result[name] = self.read_short()
if type == "ushort": result[name] = self.read_ushort()
if type == "long": result[name] = self.read_long()
if type == "double": result[name] = self.read_double()
if type == "float": result[name] = self.read_float()
if type == "bool": result[name] = self.read_bool()
if type == "varint": result[name] = self.read_varInt()
if type == "bytearray": result[name] = self.read_bytearray()
if type == "position": result[name] = self.read_position()
if type == "slot": result[name] = self.read_slot()
if type == "uuid": result[name] = self.read_uuid()
if type == "metadata": result[name] = self.read_metadata()
if type == "rest": result[name] = self.read_rest()
except:
# print traceback.format_exc()
result[name] = None
return result
[docs] def send(self, id, expression, payload):
result = ""
result += self.send_varInt(id)
if len(expression) > 0:
for i,type in enumerate(expression.split("|")):
try:
pay = payload[i]
if type == "string": result += self.send_string(pay)
if type == "json": result += self.send_json(pay)
if type == "ubyte": result += self.send_ubyte(pay)
if type == "byte": result += self.send_byte(pay)
if type == "int": result += self.send_int(pay)
if type == "short": result += self.send_short(pay)
if type == "ushort": result += self.send_ushort(pay)
if type == "varint": result += self.send_varInt(pay)
if type == "float": result += self.send_float(pay)
if type == "double": result += self.send_double(pay)
if type == "long": result += self.send_long(pay)
if type == "json": result += self.send_json(pay)
if type == "bytearray": result += self.send_bytearray(pay)
if type == "uuid": result += self.send_uuid(pay)
if type == "metadata": result += self.send_metadata(pay)
if type == "bool": result += self.send_bool(pay)
if type == "position": result += self.send_position(pay)
if type == "raw": result += pay
except:
pass
#print traceback.format_exc()
self.sendRaw(result)
return result
# -- SENDING DATA TYPES -- #
[docs] def send_byte(self, payload):
return struct.pack("b", payload)
[docs] def send_ubyte(self, payload):
return struct.pack("B", payload)
[docs] def send_string(self, payload):
return self.send_varInt(len(payload)) + payload.encode("utf8")
def send_json(self, payload):
return self.send_string(json.dumps(payload))
[docs] def send_int(self, payload):
return struct.pack(">i", payload)
[docs] def send_long(self, payload):
return struct.pack(">q", payload)
[docs] def send_short(self, payload):
return struct.pack(">h", payload)
[docs] def send_ushort(self, payload):
return struct.pack(">H", payload)
[docs] def send_float(self, payload):
return struct.pack(">f", payload)
[docs] def send_double(self, payload):
return struct.pack(">d", payload)
[docs] def send_varInt(self, payload):
return self.pack_varInt(payload)
[docs] def send_bytearray(self, payload):
return self.send_varInt(len(payload)) + payload
[docs] def send_json(self, payload):
return self.send_string(json.dumps(payload))
[docs] def send_uuid(self, payload):
return payload.bytes
[docs] def send_position(self, payload):
x, y, z = position
return self.send_long(((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF))
[docs] def send_bool(self, payload):
if payload == False: return self.send_byte(0)
if payload == True: return self.send_byte(1)
# -- READING DATA TYPES -- #
[docs] def recv(self, length):
if length > 5000:
d = ""
while len(d) < length:
m = length - len(d)
if m > 5000: m = 5000
d += self.socket.recv(m)
else:
d = self.socket.recv(length)
if len(d) == 0:
raise EOFError("Packet was zero length, disconnecting")
# while length > len(d):
# print "Need %d more" % length - len(d)
# d += self.socket.recv(length - len(d))
# if not length == len(d):
# print "ACTUAL PACKET NOT LONG %d %d" % (length, len(d))
# print "Read more: %d" % len(self.socket.recv(1024))
#raise EOFError("Actual length of packet was not as long as expected!")
if self.recvCipher is None:
return d
return self.recvCipher.decrypt(d)
[docs] def read_data(self, length):
d = self.buffer.read(length)
# if len(d) == 0 and length is not 0:
# self.disconnect("Received no data - connection closed")
# return ""
return d
[docs] def read_byte(self):
return struct.unpack("b", self.read_data(1))[0]
[docs] def read_ubyte(self):
return struct.unpack("B", self.read_data(1))[0]
[docs] def read_long(self):
return struct.unpack(">q", self.read_data(8))[0]
[docs] def read_ulong(self):
return struct.unpack(">Q", self.read_data(8))[0]
[docs] def read_float(self):
return struct.unpack(">f", self.read_data(4))[0]
[docs] def read_int(self):
return struct.unpack(">i", self.read_data(4))[0]
[docs] def read_double(self):
return struct.unpack(">d", self.read_data(8))[0]
[docs] def read_bool(self):
if self.read_data(1) == 0x01: return True
else: return False
[docs] def read_short(self):
return struct.unpack(">h", self.read_data(2))[0]
[docs] def read_ushort(self):
return struct.unpack(">H", self.read_data(2))[0]
[docs] def read_bytearray(self):
return self.read_data(self.read_varInt())
[docs] def read_short_bytearray(self):
return self.read_data(self.read_short())
[docs] def read_position(self):
position = self.read_long()
if position == -1: return None
if position & 0x3FFFFFF > 67100000: z = -(-position & 0x3FFFFFF)
else: z = position & 0x3FFFFFF
position = (position >> 38, (position >> 26) & 0xFFF, z)
return position
[docs] def read_slot(self):
id = self.read_short()
if not id == -1:
count = self.read_ubyte()
damage = self.read_short()
return {"id": id, "count": count, "damage": damage}
# nbtLength = self.read_short()
# print count ,damage, nbtLength
# if nbtLength > 0: nbt = self.read_data(nbtLength)
# else: nbt = ""
[docs] def read_varInt(self):
total = 0
shift = 0
val = 0x80
while val&0x80:
val = struct.unpack('B', self.read_data(1))[0]
total |= ((val&0x7F)<<shift)
shift += 7
if total&(1<<31):
total = total - (1<<32)
return total
[docs] def read_uuid(self):
i = self.read_data(16)
i = uuid.UUID(bytes=i)
return i
[docs] def read_string(self):
return self.read_data(self.read_varInt())
[docs] def read_json(self):
return json.loads(self.read_string())
[docs] def read_rest(self):
return self.read_data(1024 * 1024)