155 lines
5.0 KiB
Python
155 lines
5.0 KiB
Python
"""
|
|
Starting launcher of the game. This is where you host new games or join other ones.
|
|
"""
|
|
import tkinter as tk
|
|
import concurrent.futures
|
|
import pypong.networking.client as client
|
|
|
|
global running
|
|
|
|
|
|
def main():
|
|
"""
|
|
Runs the game launcher.
|
|
"""
|
|
|
|
# Executor used for scanning local network for servers
|
|
executor = concurrent.futures.ThreadPoolExecutor(max_workers=512)
|
|
|
|
main_window = tk.Tk()
|
|
main_window.title('PyPong Launcher')
|
|
main_window.resizable(width=False, height=False)
|
|
|
|
main_window.geometry('500x300')
|
|
|
|
def quit_launcher():
|
|
"""
|
|
Quits the main menu.
|
|
"""
|
|
global running
|
|
running = False
|
|
# Stop executor from creating new scanning tasks
|
|
executor.shutdown(wait=False, cancel_futures=True)
|
|
|
|
def check_game(game_ip):
|
|
"""
|
|
Checks to see if there is a game bein ghosted at a given IP
|
|
|
|
:param game_ip: IP address to check for a host
|
|
:return: Response from the server (or timeout if nothing found)
|
|
"""
|
|
result = client.check_ip(game_ip)
|
|
return result
|
|
|
|
def get_games(network):
|
|
"""
|
|
Refreshes the game list.
|
|
|
|
:param network: The network to scan for games on
|
|
"""
|
|
listbox.delete(0, tk.END)
|
|
listbox.insert(0, ' Building IP range...')
|
|
main_window.update()
|
|
|
|
possible_ips = client.get_ips_from_network(network.get().split(' ')[0])
|
|
|
|
listbox.delete(0, tk.END)
|
|
listbox.insert(0, 'this might take some time...')
|
|
listbox.insert(0, 'Refreshing server listing,')
|
|
main_window.update()
|
|
|
|
futures = []
|
|
# Use the executor to create threads to search for games
|
|
for ip in possible_ips:
|
|
future = executor.submit(check_game, ip)
|
|
futures.append(future)
|
|
|
|
game_found = False
|
|
|
|
for future in futures:
|
|
result = future.result()
|
|
|
|
if 'PYPONGRST;SVRINFO' in result: # Check to make sure response has the right header and type
|
|
raw_response = result.split(';') # Split response by block (denoted by semicolons)
|
|
response = []
|
|
|
|
# Split up each block by chunks (<COMMAND>:<RESPONSE>)
|
|
# e.g. the block 'NAME:Nicholas Dyer' -> ['NAME', 'Nicholas Dyer']
|
|
for block in raw_response:
|
|
chunks = block.split(':')
|
|
for chunk in chunks:
|
|
response.append(chunk)
|
|
|
|
# Make sure the response has the NAME command
|
|
if 'NAME' in response:
|
|
# Session name is the next index after the command
|
|
session_name = response[response.index('NAME') + 1]
|
|
if not game_found: # If this is the first game found, clear out the list
|
|
listbox.delete(0, tk.END)
|
|
game_found = True
|
|
listbox.insert(0, session_name)
|
|
main_window.update() # Update screen while finding games (laggy, but works)
|
|
|
|
if not game_found:
|
|
listbox.delete(0, tk.END)
|
|
listbox.insert(0, 'No games found!')
|
|
|
|
def get_addresses():
|
|
"""
|
|
Get the current machine's local interfaces and their corresponding addresses in CIDR notation
|
|
|
|
:return: Address in CIDR notation along with the interface
|
|
|
|
Example:
|
|
|
|
['172.20.126.4/24 [eth0]', 192.168.2.43/16 [wlan0]']
|
|
"""
|
|
interface_info = client.get_interface_info()
|
|
shortened_info = []
|
|
|
|
for interface in interface_info:
|
|
shortened_info.append('{0} [{1}]'.format(interface.network, interface.interface_name))
|
|
|
|
return shortened_info
|
|
|
|
# Create the top title label
|
|
title_label = tk.Label(text='PyPong Launcher', fg='white', bg='#000040', padx=1, pady=20)
|
|
title_label.pack(side=tk.TOP, fill=tk.BOTH)
|
|
|
|
# Create the list of games
|
|
global listbox
|
|
listbox = tk.Listbox(main_window)
|
|
listbox.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
|
|
# Add scrollbar
|
|
scrollbar = tk.Scrollbar(main_window)
|
|
scrollbar.pack(side=tk.LEFT, fill=tk.Y)
|
|
# Link scrollbar to list of games
|
|
listbox.config(yscrollcommand=scrollbar.set)
|
|
scrollbar.config(command=listbox.yview)
|
|
|
|
# Create buttons
|
|
button_frame = tk.Frame(main_window)
|
|
tk.Button(button_frame, text='Host a Game', height=3, width=30).pack()
|
|
tk.Button(button_frame, text='Join Selected Game', height=3, width=30).pack()
|
|
tk.Button(button_frame, text='Refresh Game List', height=2, width=20,
|
|
command=lambda: get_games(selected_network)).pack()
|
|
networks = get_addresses()
|
|
selected_network = tk.StringVar()
|
|
selected_network.set(networks[0])
|
|
interface_menu = tk.OptionMenu(button_frame, selected_network, *networks)
|
|
interface_menu.pack()
|
|
button_frame.pack(side=tk.RIGHT)
|
|
|
|
# Set it so that if the X is pressed the application quits
|
|
main_window.protocol('WM_DELETE_WINDOW', quit_launcher)
|
|
|
|
global running
|
|
running = True
|
|
|
|
while running:
|
|
main_window.update()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|