2020-08-19 01:04:43 +00:00
|
|
|
import struct
|
|
|
|
import socket
|
|
|
|
import base64
|
|
|
|
import json
|
2021-08-28 22:48:30 +00:00
|
|
|
import os
|
2022-03-01 17:58:39 +00:00
|
|
|
import re
|
2020-08-19 01:04:43 +00:00
|
|
|
import logging.config
|
2022-03-01 17:58:39 +00:00
|
|
|
import uuid
|
|
|
|
import random
|
2022-01-26 01:45:30 +00:00
|
|
|
|
2021-08-28 22:48:30 +00:00
|
|
|
from app.classes.shared.console import console
|
2022-03-01 03:40:11 +00:00
|
|
|
from app.classes.minecraft.bedrock_ping import BedrockPing
|
2020-08-19 01:04:43 +00:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class Server:
|
|
|
|
def __init__(self, data):
|
|
|
|
self.description = data.get('description')
|
|
|
|
# print(self.description)
|
|
|
|
if isinstance(self.description, dict):
|
|
|
|
|
|
|
|
# cat server
|
|
|
|
if "translate" in self.description:
|
|
|
|
self.description = self.description['translate']
|
|
|
|
|
|
|
|
# waterfall / bungee
|
|
|
|
elif 'extra' in self.description:
|
|
|
|
lines = []
|
|
|
|
|
|
|
|
description = self.description
|
|
|
|
if 'extra' in description.keys():
|
|
|
|
for e in description['extra']:
|
2021-08-28 22:48:30 +00:00
|
|
|
#Conversion format code needed only for Java Version
|
|
|
|
lines.append(get_code_format("reset"))
|
|
|
|
if "bold" in e.keys():
|
|
|
|
lines.append(get_code_format("bold"))
|
|
|
|
if "italic" in e.keys():
|
|
|
|
lines.append(get_code_format("italic"))
|
|
|
|
if "underlined" in e.keys():
|
|
|
|
lines.append(get_code_format("underlined"))
|
|
|
|
if "strikethrough" in e.keys():
|
|
|
|
lines.append(get_code_format("strikethrough"))
|
|
|
|
if "obfuscated" in e.keys():
|
|
|
|
lines.append(get_code_format("obfuscated"))
|
|
|
|
if "color" in e.keys():
|
2022-01-26 01:45:30 +00:00
|
|
|
lines.append(get_code_format(e['color']))
|
2021-08-28 22:48:30 +00:00
|
|
|
#Then append the text
|
2020-08-19 01:04:43 +00:00
|
|
|
if "text" in e.keys():
|
2021-08-28 22:48:30 +00:00
|
|
|
if e['text'] == '\n':
|
|
|
|
lines.append("§§")
|
|
|
|
else:
|
|
|
|
lines.append(e['text'])
|
2020-08-19 01:04:43 +00:00
|
|
|
|
|
|
|
total_text = " ".join(lines)
|
|
|
|
self.description = total_text
|
|
|
|
|
|
|
|
# normal MC
|
|
|
|
else:
|
|
|
|
self.description = self.description['text']
|
|
|
|
|
|
|
|
self.icon = base64.b64decode(data.get('favicon', '')[22:])
|
|
|
|
self.players = Players(data['players']).report()
|
|
|
|
self.version = data['version']['name']
|
|
|
|
self.protocol = data['version']['protocol']
|
|
|
|
|
|
|
|
|
|
|
|
class Players(list):
|
|
|
|
def __init__(self, data):
|
|
|
|
super().__init__(Player(x) for x in data.get('sample', []))
|
|
|
|
self.max = data['max']
|
|
|
|
self.online = data['online']
|
|
|
|
|
|
|
|
def report(self):
|
|
|
|
players = []
|
|
|
|
|
|
|
|
for x in self:
|
|
|
|
players.append(str(x))
|
|
|
|
|
|
|
|
r_data = {
|
|
|
|
'online': self.online,
|
|
|
|
'max': self.max,
|
|
|
|
'players': players
|
|
|
|
}
|
|
|
|
|
|
|
|
return json.dumps(r_data)
|
|
|
|
|
|
|
|
|
|
|
|
class Player:
|
|
|
|
def __init__(self, data):
|
|
|
|
self.id = data['id']
|
|
|
|
self.name = data['name']
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
|
|
|
|
2021-08-28 22:48:30 +00:00
|
|
|
def get_code_format(format_name):
|
|
|
|
root_dir = os.path.abspath(os.path.curdir)
|
|
|
|
format_file = os.path.join(root_dir, 'app', 'config', 'motd_format.json')
|
|
|
|
try:
|
|
|
|
with open(format_file, "r", encoding='utf-8') as f:
|
|
|
|
data = json.load(f)
|
|
|
|
|
|
|
|
if format_name in data.keys():
|
|
|
|
return data.get(format_name)
|
|
|
|
else:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.error(f"Format MOTD Error: format name {format_name} does not exist")
|
|
|
|
console.error(f"Format MOTD Error: format name {format_name} does not exist")
|
2021-08-28 22:48:30 +00:00
|
|
|
return ""
|
|
|
|
|
|
|
|
except Exception as e:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.critical(f"Config File Error: Unable to read {format_file} due to {e}")
|
|
|
|
console.critical(f"Config File Error: Unable to read {format_file} due to {e}")
|
2021-08-28 22:48:30 +00:00
|
|
|
|
|
|
|
return ""
|
|
|
|
|
2020-08-19 01:04:43 +00:00
|
|
|
|
|
|
|
# For the rest of requests see wiki.vg/Protocol
|
2021-03-09 03:01:42 +00:00
|
|
|
def ping(ip, port):
|
2020-08-19 01:04:43 +00:00
|
|
|
def read_var_int():
|
|
|
|
i = 0
|
|
|
|
j = 0
|
|
|
|
while True:
|
|
|
|
k = sock.recv(1)
|
|
|
|
if not k:
|
|
|
|
return 0
|
|
|
|
k = k[0]
|
|
|
|
i |= (k & 0x7f) << (j * 7)
|
|
|
|
j += 1
|
|
|
|
if j > 5:
|
|
|
|
raise ValueError('var_int too big')
|
2022-01-26 01:45:30 +00:00
|
|
|
if not k & 0x80:
|
2020-08-19 01:04:43 +00:00
|
|
|
return i
|
|
|
|
|
2021-03-09 03:01:42 +00:00
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
2020-08-19 01:04:43 +00:00
|
|
|
try:
|
2021-03-09 21:57:45 +00:00
|
|
|
sock.connect((ip, port))
|
2021-03-09 03:01:42 +00:00
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
except socket.error:
|
2022-02-10 23:20:36 +00:00
|
|
|
return False
|
2020-08-19 01:04:43 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
host = ip.encode('utf-8')
|
|
|
|
data = b'' # wiki.vg/Server_List_Ping
|
|
|
|
data += b'\x00' # packet ID
|
|
|
|
data += b'\x04' # protocol variant
|
|
|
|
data += struct.pack('>b', len(host)) + host
|
|
|
|
data += struct.pack('>H', port)
|
|
|
|
data += b'\x01' # next state
|
|
|
|
data = struct.pack('>b', len(data)) + data
|
|
|
|
sock.sendall(data + b'\x01\x00') # handshake + status ping
|
|
|
|
length = read_var_int() # full packet length
|
|
|
|
if length < 10:
|
|
|
|
if length < 0:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
sock.recv(1) # packet type, 0 for pings
|
|
|
|
length = read_var_int() # string length
|
|
|
|
data = b''
|
|
|
|
while len(data) != length:
|
|
|
|
chunk = sock.recv(length - len(data))
|
|
|
|
if not chunk:
|
|
|
|
return False
|
|
|
|
|
|
|
|
data += chunk
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.debug(f"Server reports this data on ping: {data}")
|
2020-08-19 01:04:43 +00:00
|
|
|
return Server(json.loads(data))
|
|
|
|
finally:
|
|
|
|
sock.close()
|
2022-02-10 23:20:36 +00:00
|
|
|
|
|
|
|
# For the rest of requests see wiki.vg/Protocol
|
|
|
|
def ping_bedrock(ip, port):
|
2022-03-01 17:58:39 +00:00
|
|
|
rd = random.Random()
|
|
|
|
try:
|
2022-03-01 22:10:04 +00:00
|
|
|
#pylint: disable=consider-using-f-string
|
2022-03-01 17:58:39 +00:00
|
|
|
rd.seed(''.join(re.findall('..', '%012x' % uuid.getnode())))
|
|
|
|
client_guid = uuid.UUID(int=rd.getrandbits(32)).int
|
|
|
|
except:
|
2022-03-01 03:40:11 +00:00
|
|
|
client_guid = 0
|
2022-02-10 23:20:36 +00:00
|
|
|
try:
|
2022-03-01 03:40:11 +00:00
|
|
|
brp = BedrockPing(ip, port, client_guid)
|
|
|
|
return brp.ping()
|
2022-03-02 00:47:29 +00:00
|
|
|
except:
|
2022-03-01 03:40:11 +00:00
|
|
|
logger.debug("Unable to get RakNet stats")
|