I have created a program that scans for Minecraft servers on port 25565 using the mcstatus library for python. The program takes ip addresses from an excel sheet, and writes relevant server information in the row.
I want to enable my program to scan multiple ip’s at once, however after trying some basic threading, my internet would crash with just 10 threads. I am very inexperienced with threading and asynchronous operations.
Here is the code (not all of it, only relevant parts):
import ipaddress
from mcstatus import JavaServer
from enum import Enum
import openpyxl
from datetime import date
def scan_server(mc_ip, ws, row):
status_fail = 0
global rescan
global failed_rescan
try:
server = JavaServer.lookup(mc_ip, timeout=.1)
status = server.status()
ws[f'B{row}'] = status.version.name
ws[f'C{row}'] = format_motd(status.motd.parsed)
ws[f'F{row}'] = status.players.max
ws[f'D{row}'] = int(str(status.latency).split('.')[0])
current_value_players = ws[f'E{row}'].value
if current_value_players:
ws[f'E{row}'].value = f"{current_value_players}, {status.players.online} ({date.today()})"
else:
ws[f'E{row}'] = str(f"{status.players.online} ({date.today()})")
ws[f'I{row}'] = str(status.enforces_secure_chat)
if status.players.sample:
# Collect names and IDs
new_names = [player.name for player in status.players.sample]
new_ids = [player.id for player in status.players.sample]
current_names = ws[f'L{row}'].value
current_ids = ws[f'M{row}'].value
current_names_list = current_names.split(", ") if current_names else []
current_ids_list = current_ids.split(", ") if current_ids else []
unique_names = []
unique_ids = []
for name, id_ in zip(new_names, new_ids):
if name not in current_names_list and id_ not in current_ids_list:
unique_names.append(name)
unique_ids.append(id_)
if unique_names:
ws[f'L{row}'].value = ", ".join(current_names_list + unique_names)
if unique_ids:
ws[f'M{row}'].value = ", ".join(current_ids_list + unique_ids)
if 'forgeData' in status.raw:
ws[f'N{row}'].value = "Yes"
forge_data_entries = [
f"{key}: {value}" for key, value in status.raw['forgeData'].items() if key != 'd'
]
forge_data_string = ", ".join(forge_data_entries)
ws[f'O{row}'].value = forge_data_string
else:
ws[f'N{row}'].value = "No"
print(f"Fetching status for server {mc_ip}: {bold_green('Success')}")
except Exception as e:
print(f"Error fetching status for server {mc_ip}: {bold_red(e)}")
current_version_value = ws[f'B{row}'].value
if not current_version_value:
ws[f'B{row}'] = 'N/A'
elif current_version_value and current_version_value != 'N/A':
ws[f'B{row}'] = f"{current_version_value}, N/A"
status_fail = 1
if rescan == 1:
failed_rescan += 1
if not status_fail:
try:
# Attempt to query additional details from the server
server_query = JavaServer.lookup(mc_ip, timeout=.3)
query = server_query.query()
ws[f'H{row}'] = str(query.raw)
except Exception as e:
print(bold_red(f"Could not query server {mc_ip}: {bold_red(e)}"))
def process_servers(filename):
wb = openpyxl.load_workbook(filename)
ws = wb.active
row_count = 0
for row, cell in enumerate(ws['A'], start=1):
if cell.value and not ws[f'B{row}'].value:
ip = str(cell.value).strip()
try:
ipaddress.ip_address(ip)
scan_server(ip, ws, row)
row_count += 1
if row_count % 100 == 0: # Save every 100 scans
wb.save(filename)
print(f"Saved progress at row {row}")
except ValueError:
print(f"Invalid IP address: {ip}")
wb.save(filename) # Final save after all rows are processed
wb.close()
print("Finished processing all servers.")
Anyns is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.