mcrcon.py 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. # Minecraft Remote Console module.
  2. #
  3. # Copyright (C) 2015 Barnaby Gale
  4. #
  5. # SPDX-License-Identifier: MIT
  6. import socket
  7. import select
  8. import struct
  9. import time
  10. class MCRconException(Exception):
  11. pass
  12. class MCRcon(object):
  13. socket = None
  14. def connect(self, host, port, password):
  15. if self.socket is not None:
  16. raise MCRconException("Already connected")
  17. self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  18. self.socket.settimeout(0.9)
  19. self.socket.connect((host, port))
  20. self.send(3, password)
  21. def disconnect(self):
  22. if self.socket is None:
  23. raise MCRconException("Already disconnected")
  24. self.socket.close()
  25. self.socket = None
  26. def read(self, length):
  27. data = b""
  28. while len(data) < length:
  29. data += self.socket.recv(length - len(data))
  30. return data
  31. def send(self, out_type, out_data):
  32. if self.socket is None:
  33. raise MCRconException("Must connect before sending data")
  34. # Send a request packet
  35. out_payload = struct.pack('<ii', 0, out_type) + out_data.encode('utf8') + b'\x00\x00'
  36. out_length = struct.pack('<i', len(out_payload))
  37. self.socket.send(out_length + out_payload)
  38. # Read response packets
  39. in_data = ""
  40. while True:
  41. # Read a packet
  42. in_length, = struct.unpack('<i', self.read(4))
  43. in_payload = self.read(in_length)
  44. in_id = struct.unpack('<ii', in_payload[:8])
  45. in_data_partial, in_padding = in_payload[8:-2], in_payload[-2:]
  46. # Sanity checks
  47. if in_padding != b'\x00\x00':
  48. raise MCRconException("Incorrect padding")
  49. if in_id == -1:
  50. raise MCRconException("Login failed")
  51. # Record the response
  52. in_data += in_data_partial.decode('utf8')
  53. # If there's nothing more to receive, return the response
  54. if len(select.select([self.socket], [], [], 0)[0]) == 0:
  55. return in_data
  56. def command(self, command):
  57. result = self.send(2, command)
  58. time.sleep(0.003) # MC-72390 workaround
  59. return result