2019-12-09 18:27:56 +00:00
import argparse
import asyncio
import functools
import json
import logging
import re
2020-01-14 09:42:27 +00:00
import shlex
2019-12-09 18:27:56 +00:00
import urllib . request
2020-01-04 21:08:13 +00:00
import zlib
2020-02-09 04:28:48 +00:00
import collections
2019-12-09 18:27:56 +00:00
2020-01-18 14:45:52 +00:00
import ModuleUpdate
ModuleUpdate . update ( )
import websockets
import aioconsole
2019-12-09 18:27:56 +00:00
import Items
import Regions
2020-02-09 04:28:48 +00:00
import Utils
2019-12-09 18:27:56 +00:00
from MultiClient import ReceivedItem , get_item_name_from_id , get_location_name_from_address
class Client :
def __init__ ( self , socket ) :
self . socket = socket
self . auth = False
self . name = None
self . team = None
self . slot = None
self . send_index = 0
class Context :
2020-02-09 04:28:48 +00:00
def __init__ ( self , host : str , port : int , password : str , location_check_points : int , hint_cost : int ) :
2019-12-09 18:27:56 +00:00
self . data_filename = None
self . save_filename = None
self . disable_save = False
2020-01-14 09:42:27 +00:00
self . player_names = { }
2020-01-04 21:08:13 +00:00
self . rom_names = { }
2020-01-18 08:50:12 +00:00
self . remote_items = set ( )
2020-01-04 21:08:13 +00:00
self . locations = { }
2019-12-09 18:27:56 +00:00
self . host = host
self . port = port
self . password = password
self . server = None
2020-01-10 21:44:07 +00:00
self . countdown_timer = 0
2019-12-09 18:27:56 +00:00
self . clients = [ ]
self . received_items = { }
2020-02-09 11:10:12 +00:00
self . location_checks = collections . defaultdict ( set )
2020-02-09 04:28:48 +00:00
self . hint_cost = hint_cost
self . location_check_points = location_check_points
self . hints_used = collections . defaultdict ( lambda : 0 )
def get_save ( self ) - > dict :
return {
" rom_names " : list ( self . rom_names . items ( ) ) ,
" received_items " : tuple ( ( k , [ i . __dict__ for i in v ] ) for k , v in self . received_items . items ( ) ) ,
" hints_used " : tuple ( ( key , value ) for key , value in self . hints_used . items ( ) ) ,
2020-02-09 11:10:12 +00:00
" location_checks " : tuple ( ( key , tuple ( value ) ) for key , value in self . location_checks . items ( ) )
2020-02-09 04:28:48 +00:00
}
def set_save ( self , savedata : dict ) :
rom_names = savedata [ " rom_names " ]
received_items = { tuple ( k ) : [ ReceivedItem ( * * i ) for i in v ] for k , v in savedata [ " received_items " ] }
if not all ( [ self . rom_names [ tuple ( rom ) ] == ( team , slot ) for rom , ( team , slot ) in rom_names ] ) :
raise Exception ( ' Save file mismatch, will start a new game ' )
self . received_items = received_items
self . hints_used . update ( { tuple ( key ) : value for key , value in savedata [ " hints_used " ] } )
2020-02-09 11:10:12 +00:00
self . location_checks . update ( { tuple ( key ) : set ( value ) for key , value in savedata [ " location_checks " ] } )
2020-02-09 04:28:48 +00:00
logging . info ( f ' Loaded save file with { sum ( [ len ( p ) for p in received_items . values ( ) ] ) } received items '
f ' for { len ( received_items ) } players ' )
2019-12-09 18:27:56 +00:00
async def send_msgs ( websocket , msgs ) :
if not websocket or not websocket . open or websocket . closed :
return
try :
await websocket . send ( json . dumps ( msgs ) )
except websockets . ConnectionClosed :
pass
def broadcast_all ( ctx : Context , msgs ) :
for client in ctx . clients :
if client . auth :
asyncio . create_task ( send_msgs ( client . socket , msgs ) )
def broadcast_team ( ctx : Context , team , msgs ) :
for client in ctx . clients :
2020-01-14 09:42:27 +00:00
if client . auth and client . team == team :
2019-12-09 18:27:56 +00:00
asyncio . create_task ( send_msgs ( client . socket , msgs ) )
def notify_all ( ctx : Context , text ) :
2020-01-18 11:21:57 +00:00
logging . info ( " Notice (all): %s " % text )
2019-12-09 18:27:56 +00:00
broadcast_all ( ctx , [ [ ' Print ' , text ] ] )
2020-01-14 09:42:27 +00:00
def notify_team ( ctx : Context , team : int , text : str ) :
2020-01-18 11:21:57 +00:00
logging . info ( " Notice (Team # %d ): %s " % ( team + 1 , text ) )
2019-12-09 18:27:56 +00:00
broadcast_team ( ctx , team , [ [ ' Print ' , text ] ] )
def notify_client ( client : Client , text : str ) :
if not client . auth :
return
2020-01-18 11:21:57 +00:00
logging . info ( " Notice (Player %s in team %d ): %s " % ( client . name , client . team + 1 , text ) )
2019-12-09 18:27:56 +00:00
asyncio . create_task ( send_msgs ( client . socket , [ [ ' Print ' , text ] ] ) )
async def server ( websocket , path , ctx : Context ) :
client = Client ( websocket )
ctx . clients . append ( client )
try :
await on_client_connected ( ctx , client )
async for data in websocket :
for msg in json . loads ( data ) :
if len ( msg ) == 1 :
cmd = msg
args = None
else :
cmd = msg [ 0 ]
args = msg [ 1 ]
await process_client_cmd ( ctx , client , cmd , args )
except Exception as e :
2019-12-16 17:39:00 +00:00
if not isinstance ( e , websockets . WebSocketException ) :
2019-12-09 18:27:56 +00:00
logging . exception ( e )
finally :
await on_client_disconnected ( ctx , client )
ctx . clients . remove ( client )
async def on_client_connected ( ctx : Context , client : Client ) :
2020-01-15 02:00:30 +00:00
await send_msgs ( client . socket , [ [ ' RoomInfo ' , {
' password ' : ctx . password is not None ,
' players ' : [ ( client . team , client . slot , client . name ) for client in ctx . clients if client . auth ]
} ] ] )
2019-12-09 18:27:56 +00:00
async def on_client_disconnected ( ctx : Context , client : Client ) :
if client . auth :
await on_client_left ( ctx , client )
async def on_client_joined ( ctx : Context , client : Client ) :
2020-01-14 09:42:27 +00:00
notify_all ( ctx , " %s (Team # %d ) has joined the game " % ( client . name , client . team + 1 ) )
2019-12-09 18:27:56 +00:00
async def on_client_left ( ctx : Context , client : Client ) :
2020-01-14 09:42:27 +00:00
notify_all ( ctx , " %s (Team # %d ) has left the game " % ( client . name , client . team + 1 ) )
2019-12-09 18:27:56 +00:00
2020-01-10 21:44:07 +00:00
async def countdown ( ctx : Context , timer ) :
notify_all ( ctx , f ' [Server]: Starting countdown of { timer } s ' )
if ctx . countdown_timer :
ctx . countdown_timer = timer
return
ctx . countdown_timer = timer
while ctx . countdown_timer > 0 :
notify_all ( ctx , f ' [Server]: { ctx . countdown_timer } ' )
ctx . countdown_timer - = 1
await asyncio . sleep ( 1 )
notify_all ( ctx , f ' [Server]: GO ' )
2019-12-09 18:27:56 +00:00
def get_connected_players_string ( ctx : Context ) :
auth_clients = [ c for c in ctx . clients if c . auth ]
if not auth_clients :
return ' No player connected '
2020-01-14 09:42:27 +00:00
auth_clients . sort ( key = lambda c : ( c . team , c . slot ) )
2019-12-09 18:27:56 +00:00
current_team = 0
2020-01-14 09:42:27 +00:00
text = ' Team #1: '
2019-12-09 18:27:56 +00:00
for c in auth_clients :
if c . team != current_team :
2020-01-14 09:42:27 +00:00
text + = f ' :: Team # { c . team + 1 } : '
2019-12-09 18:27:56 +00:00
current_team = c . team
2020-01-14 09:42:27 +00:00
text + = f ' { c . name } '
2019-12-09 18:27:56 +00:00
return ' Connected players: ' + text [ : - 1 ]
def get_received_items ( ctx : Context , team , player ) :
2020-01-14 09:42:27 +00:00
return ctx . received_items . setdefault ( ( team , player ) , [ ] )
2019-12-09 18:27:56 +00:00
def tuplize_received_items ( items ) :
2020-01-14 09:42:27 +00:00
return [ ( item . item , item . location , item . player ) for item in items ]
2019-12-09 18:27:56 +00:00
def send_new_items ( ctx : Context ) :
for client in ctx . clients :
if not client . auth :
continue
items = get_received_items ( ctx , client . team , client . slot )
if len ( items ) > client . send_index :
asyncio . create_task ( send_msgs ( client . socket , [ [ ' ReceivedItems ' , ( client . send_index , tuplize_received_items ( items ) [ client . send_index : ] ) ] ] ) )
client . send_index = len ( items )
2020-01-14 09:42:27 +00:00
def forfeit_player ( ctx : Context , team , slot ) :
2019-12-09 18:27:56 +00:00
all_locations = [ values [ 0 ] for values in Regions . location_table . values ( ) if type ( values [ 0 ] ) is int ]
2020-01-14 09:42:27 +00:00
notify_all ( ctx , " %s (Team # %d ) has forfeited " % ( ctx . player_names [ ( team , slot ) ] , team + 1 ) )
register_location_checks ( ctx , team , slot , all_locations )
2019-12-09 18:27:56 +00:00
2020-01-14 09:42:27 +00:00
def register_location_checks ( ctx : Context , team , slot , locations ) :
2020-02-09 11:10:12 +00:00
ctx . location_checks [ team , slot ] | = set ( locations )
2019-12-09 18:27:56 +00:00
found_items = False
for location in locations :
2020-01-04 21:08:13 +00:00
if ( location , slot ) in ctx . locations :
target_item , target_player = ctx . locations [ ( location , slot ) ]
2020-01-18 08:50:12 +00:00
if target_player != slot or slot in ctx . remote_items :
2019-12-09 18:27:56 +00:00
found = False
recvd_items = get_received_items ( ctx , team , target_player )
for recvd_item in recvd_items :
2020-01-14 09:42:27 +00:00
if recvd_item . location == location and recvd_item . player == slot :
2019-12-09 18:27:56 +00:00
found = True
break
2020-02-09 04:28:48 +00:00
2019-12-09 18:27:56 +00:00
if not found :
2020-01-14 09:42:27 +00:00
new_item = ReceivedItem ( target_item , location , slot )
2019-12-09 18:27:56 +00:00
recvd_items . append ( new_item )
2020-01-18 10:26:45 +00:00
if slot != target_player :
broadcast_team ( ctx , team , [ [ ' ItemSent ' , ( slot , location , target_player , target_item ) ] ] )
2020-01-18 11:21:57 +00:00
logging . info ( ' (Team # %d ) %s sent %s to %s ( %s ) ' % ( team + 1 , ctx . player_names [ ( team , slot ) ] , get_item_name_from_id ( target_item ) , ctx . player_names [ ( team , target_player ) ] , get_location_name_from_address ( location ) ) )
2019-12-09 18:27:56 +00:00
found_items = True
send_new_items ( ctx )
2020-02-09 04:28:48 +00:00
if found_items :
save ( ctx )
def save ( ctx : Context ) :
if not ctx . disable_save :
2019-12-09 18:27:56 +00:00
try :
with open ( ctx . save_filename , " wb " ) as f :
2020-02-09 04:28:48 +00:00
jsonstr = json . dumps ( ctx . get_save ( ) )
2020-01-04 21:08:13 +00:00
f . write ( zlib . compress ( jsonstr . encode ( " utf-8 " ) ) )
2019-12-09 18:27:56 +00:00
except Exception as e :
logging . exception ( e )
2020-02-09 04:28:48 +00:00
def hint ( ctx : Context , team , slot , item : str ) :
found = 0
seeked_item_id = Items . item_table [ item ] [ 3 ]
for check , result in ctx . locations . items ( ) :
item_id , receiving_player = result
if receiving_player == slot and item_id == seeked_item_id :
location_id , finding_player = check
hint = f " [Hint]: { ctx . player_names [ ( team , slot ) ] } ' s { item } can be found at " \
f " { get_location_name_from_address ( location_id ) } in { ctx . player_names [ team , finding_player ] } ' s World "
notify_team ( ctx , team , hint )
found + = 1
return found
2019-12-09 18:27:56 +00:00
async def process_client_cmd ( ctx : Context , client : Client , cmd , args ) :
if type ( cmd ) is not str :
await send_msgs ( client . socket , [ [ ' InvalidCmd ' ] ] )
return
if cmd == ' Connect ' :
if not args or type ( args ) is not dict or \
' password ' not in args or type ( args [ ' password ' ] ) not in [ str , type ( None ) ] or \
2020-01-14 09:42:27 +00:00
' rom ' not in args or type ( args [ ' rom ' ] ) is not list :
2019-12-09 18:27:56 +00:00
await send_msgs ( client . socket , [ [ ' InvalidArguments ' , ' Connect ' ] ] )
return
errors = set ( )
2020-01-14 09:42:27 +00:00
if ctx . password is not None and args [ ' password ' ] != ctx . password :
2019-12-09 18:27:56 +00:00
errors . add ( ' InvalidPassword ' )
2020-01-14 09:42:27 +00:00
if tuple ( args [ ' rom ' ] ) not in ctx . rom_names :
errors . add ( ' InvalidRom ' )
2019-12-09 18:27:56 +00:00
else :
2020-01-14 09:42:27 +00:00
team , slot = ctx . rom_names [ tuple ( args [ ' rom ' ] ) ]
if any ( [ c . slot == slot and c . team == team for c in ctx . clients if c . auth ] ) :
errors . add ( ' SlotAlreadyTaken ' )
else :
client . name = ctx . player_names [ ( team , slot ) ]
client . team = team
client . slot = slot
2019-12-09 18:27:56 +00:00
if errors :
await send_msgs ( client . socket , [ [ ' ConnectionRefused ' , list ( errors ) ] ] )
else :
client . auth = True
2020-01-14 09:42:27 +00:00
reply = [ [ ' Connected ' , [ ( client . team , client . slot ) , [ ( p , n ) for ( t , p ) , n in ctx . player_names . items ( ) if t == client . team ] ] ] ]
2019-12-09 18:27:56 +00:00
items = get_received_items ( ctx , client . team , client . slot )
if items :
reply . append ( [ ' ReceivedItems ' , ( 0 , tuplize_received_items ( items ) ) ] )
client . send_index = len ( items )
await send_msgs ( client . socket , reply )
await on_client_joined ( ctx , client )
if not client . auth :
return
if cmd == ' Sync ' :
items = get_received_items ( ctx , client . team , client . slot )
if items :
client . send_index = len ( items )
2020-01-18 08:50:12 +00:00
await send_msgs ( client . socket , [ [ ' ReceivedItems ' , ( 0 , tuplize_received_items ( items ) ) ] ] )
2019-12-09 18:27:56 +00:00
if cmd == ' LocationChecks ' :
if type ( args ) is not list :
await send_msgs ( client . socket , [ [ ' InvalidArguments ' , ' LocationChecks ' ] ] )
return
2020-01-14 09:42:27 +00:00
register_location_checks ( ctx , client . team , client . slot , args )
2019-12-09 18:27:56 +00:00
2020-01-18 08:50:12 +00:00
if cmd == ' LocationScouts ' :
if type ( args ) is not list :
await send_msgs ( client . socket , [ [ ' InvalidArguments ' , ' LocationScouts ' ] ] )
return
locs = [ ]
for location in args :
if type ( location ) is not int or 0 > = location > len ( Regions . location_table ) :
await send_msgs ( client . socket , [ [ ' InvalidArguments ' , ' LocationScouts ' ] ] )
return
loc_name = list ( Regions . location_table . keys ( ) ) [ location - 1 ]
target_item , target_player = ctx . locations [ ( Regions . location_table [ loc_name ] [ 0 ] , client . slot ) ]
replacements = { ' SmallKey ' : 0xA2 , ' BigKey ' : 0x9D , ' Compass ' : 0x8D , ' Map ' : 0x7D }
item_type = [ i [ 2 ] for i in Items . item_table . values ( ) if type ( i [ 3 ] ) is int and i [ 3 ] == target_item ]
if item_type :
target_item = replacements . get ( item_type [ 0 ] , target_item )
locs . append ( [ loc_name , location , target_item , target_player ] )
2020-01-18 11:21:57 +00:00
logging . info ( f " { client . name } in team { client . team + 1 } scouted { ' , ' . join ( [ l [ 0 ] for l in locs ] ) } " )
2020-01-18 08:50:12 +00:00
await send_msgs ( client . socket , [ [ ' LocationInfo ' , [ l [ 1 : ] for l in locs ] ] ] )
2019-12-09 18:27:56 +00:00
if cmd == ' Say ' :
if type ( args ) is not str or not args . isprintable ( ) :
await send_msgs ( client . socket , [ [ ' InvalidArguments ' , ' Say ' ] ] )
return
notify_all ( ctx , client . name + ' : ' + args )
2020-01-10 21:44:07 +00:00
if args . startswith ( ' !players ' ) :
2019-12-09 18:27:56 +00:00
notify_all ( ctx , get_connected_players_string ( ctx ) )
2020-02-09 04:28:48 +00:00
elif args . startswith ( ' !forfeit ' ) :
2020-01-14 09:42:27 +00:00
forfeit_player ( ctx , client . team , client . slot )
2020-02-09 04:28:48 +00:00
elif args . startswith ( ' !countdown ' ) :
2020-01-10 21:44:07 +00:00
try :
timer = int ( args . split ( ) [ 1 ] )
except ( IndexError , ValueError ) :
timer = 10
asyncio . create_task ( countdown ( ctx , timer ) )
2020-02-09 04:28:48 +00:00
elif args . startswith ( " !hint " ) :
2020-02-09 11:10:12 +00:00
points_available = ctx . location_check_points * len ( ctx . location_checks [ client . team , client . slot ] ) - ctx . hint_cost * ctx . hints_used [ client . team , client . slot ]
2020-02-09 04:28:48 +00:00
itemname = args [ 6 : ]
if not itemname :
notify_client ( client , " Use !hint {itemname} . For example !hint Lamp. "
f " A hint costs { ctx . hint_cost } points. \n "
f " You have { points_available } points. " )
elif itemname in Items . item_table :
if ctx . hint_cost : can_pay = points_available / / ctx . hint_cost > = 1
else : can_pay = True
if can_pay :
found = hint ( ctx , client . team , client . slot , itemname )
ctx . hints_used [ client . team , client . slot ] + = found
if not found :
notify_client ( client , " No items found, points refunded. " )
else :
save ( ctx )
else :
notify_client ( client , f " You can ' t afford the hint. "
f " You have { points_available } points and need { ctx . hint_cost } " )
else :
notify_client ( client , f ' Item " { itemname } " not found. ' )
2019-12-09 18:27:56 +00:00
def set_password ( ctx : Context , password ) :
ctx . password = password
2020-01-18 11:21:57 +00:00
logging . warning ( ' Password set to ' + password if password is not None else ' Password disabled ' )
2019-12-09 18:27:56 +00:00
async def console ( ctx : Context ) :
while True :
input = await aioconsole . ainput ( )
2020-01-14 23:34:12 +00:00
try :
2019-12-09 18:27:56 +00:00
2020-01-14 23:34:12 +00:00
command = shlex . split ( input )
if not command :
continue
2019-12-09 18:27:56 +00:00
2020-01-14 23:34:12 +00:00
if command [ 0 ] == ' /exit ' :
ctx . server . ws_server . close ( )
break
2019-12-09 18:27:56 +00:00
2020-01-14 23:34:12 +00:00
if command [ 0 ] == ' /players ' :
2020-01-18 13:41:11 +00:00
logging . info ( get_connected_players_string ( ctx ) )
2020-01-14 23:34:12 +00:00
if command [ 0 ] == ' /password ' :
set_password ( ctx , command [ 1 ] if len ( command ) > 1 else None )
if command [ 0 ] == ' /kick ' and len ( command ) > 1 :
team = int ( command [ 2 ] ) - 1 if len ( command ) > 2 and command [ 2 ] . isdigit ( ) else None
for client in ctx . clients :
if client . auth and client . name . lower ( ) == command [ 1 ] . lower ( ) and ( team is None or team == client . team ) :
if client . socket and not client . socket . closed :
await client . socket . close ( )
if command [ 0 ] == ' /forfeitslot ' and len ( command ) > 1 and command [ 1 ] . isdigit ( ) :
if len ( command ) > 2 and command [ 2 ] . isdigit ( ) :
team = int ( command [ 1 ] ) - 1
slot = int ( command [ 2 ] )
else :
team = 0
slot = int ( command [ 1 ] )
forfeit_player ( ctx , team , slot )
if command [ 0 ] == ' /forfeitplayer ' and len ( command ) > 1 :
2020-01-23 23:12:23 +00:00
seeked_player = command [ 1 ] . lower ( )
for ( team , slot ) , name in ctx . player_names . items ( ) :
if name . lower ( ) == seeked_player :
forfeit_player ( ctx , team , slot )
2020-01-14 23:34:12 +00:00
if command [ 0 ] == ' /senditem ' and len ( command ) > 2 :
[ ( player , item ) ] = re . findall ( r ' \ S* ( \ S*) (.*) ' , input )
if item in Items . item_table :
for client in ctx . clients :
if client . auth and client . name . lower ( ) == player . lower ( ) :
new_item = ReceivedItem ( Items . item_table [ item ] [ 3 ] , " cheat console " , client . slot )
get_received_items ( ctx , client . team , client . slot ) . append ( new_item )
notify_all ( ctx , ' Cheat console: sending " ' + item + ' " to ' + client . name )
send_new_items ( ctx )
else :
2020-01-18 13:41:11 +00:00
logging . warning ( " Unknown item: " + item )
2020-01-14 23:34:12 +00:00
if command [ 0 ] == ' /hint ' :
2020-02-09 04:28:48 +00:00
for ( team , slot ) , name in ctx . player_names . items ( ) :
2020-01-14 23:57:38 +00:00
if len ( command ) == 1 :
2020-02-09 04:28:48 +00:00
logging . info ( " Use /hint {Playername} {itemname} \n For example /hint Berserker Lamp " )
2020-01-14 23:57:38 +00:00
elif name . lower ( ) == command [ 1 ] . lower ( ) :
2020-01-14 23:34:12 +00:00
item = " " . join ( command [ 2 : ] )
if item in Items . item_table :
2020-02-09 04:28:48 +00:00
hint ( ctx , team , slot , item )
2020-01-14 23:34:12 +00:00
else :
2020-01-18 13:41:11 +00:00
logging . warning ( " Unknown item: " + item )
2020-01-14 23:34:12 +00:00
if command [ 0 ] [ 0 ] != ' / ' :
notify_all ( ctx , ' [Server]: ' + input )
except :
import traceback
traceback . print_exc ( )
2019-12-09 18:27:56 +00:00
async def main ( ) :
parser = argparse . ArgumentParser ( )
parser . add_argument ( ' --host ' , default = None )
parser . add_argument ( ' --port ' , default = 38281 , type = int )
parser . add_argument ( ' --password ' , default = None )
parser . add_argument ( ' --multidata ' , default = None )
parser . add_argument ( ' --savefile ' , default = None )
parser . add_argument ( ' --disable_save ' , default = False , action = ' store_true ' )
2020-01-18 11:21:57 +00:00
parser . add_argument ( ' --loglevel ' , default = ' info ' , choices = [ ' debug ' , ' info ' , ' warning ' , ' error ' , ' critical ' ] )
2020-02-09 04:28:48 +00:00
parser . add_argument ( ' --location_check_points ' , default = 1 , type = int )
parser . add_argument ( ' --hint_cost ' , default = 1000 , type = int )
2019-12-09 18:27:56 +00:00
args = parser . parse_args ( )
2020-02-09 04:28:48 +00:00
file_options = Utils . parse_yaml ( open ( " host.yaml " ) . read ( ) ) [ " server_options " ]
for key , value in file_options . items ( ) :
if value is not None :
setattr ( args , key , value )
2020-01-18 11:21:57 +00:00
logging . basicConfig ( format = ' [ %(asctime)s ] %(message)s ' , level = getattr ( logging , args . loglevel . upper ( ) , logging . INFO ) )
2020-02-09 04:28:48 +00:00
ctx = Context ( args . host , args . port , args . password , args . location_check_points , args . hint_cost )
2019-12-09 18:27:56 +00:00
ctx . data_filename = args . multidata
try :
if not ctx . data_filename :
import tkinter
import tkinter . filedialog
root = tkinter . Tk ( )
root . withdraw ( )
ctx . data_filename = tkinter . filedialog . askopenfilename ( filetypes = ( ( " Multiworld data " , " *multidata " ) , ) )
with open ( ctx . data_filename , ' rb ' ) as f :
2020-01-04 21:08:13 +00:00
jsonobj = json . loads ( zlib . decompress ( f . read ( ) ) . decode ( " utf-8 " ) )
2020-01-18 08:50:12 +00:00
for team , names in enumerate ( jsonobj [ ' names ' ] ) :
2020-01-14 09:42:27 +00:00
for player , name in enumerate ( names , 1 ) :
ctx . player_names [ ( team , player ) ] = name
2020-01-18 08:50:12 +00:00
ctx . rom_names = { tuple ( rom ) : ( team , slot ) for slot , team , rom in jsonobj [ ' roms ' ] }
ctx . remote_items = set ( jsonobj [ ' remote_items ' ] )
ctx . locations = { tuple ( k ) : tuple ( v ) for k , v in jsonobj [ ' locations ' ] }
2019-12-09 18:27:56 +00:00
except Exception as e :
2020-01-18 11:21:57 +00:00
logging . error ( ' Failed to read multiworld data ( %s ) ' % e )
2019-12-09 18:27:56 +00:00
return
2020-02-07 22:45:54 +00:00
import socket
ip = socket . gethostbyname ( socket . gethostname ( ) )
try :
ip = urllib . request . urlopen ( ' https://checkip.amazonaws.com/ ' ) . read ( ) . decode ( ' utf8 ' ) . strip ( )
except Exception as e :
try :
ip = urllib . request . urlopen ( ' https://v4.ident.me ' ) . read ( ) . decode ( ' utf8 ' ) . strip ( )
except :
logging . exception ( e )
pass # we could be offline, in a local game, so no point in erroring out
2019-12-09 18:27:56 +00:00
2020-01-18 11:21:57 +00:00
logging . info ( ' Hosting game at %s : %d ( %s ) ' % ( ip , ctx . port , ' No password ' if not ctx . password else ' Password: %s ' % ctx . password ) )
2019-12-09 18:27:56 +00:00
ctx . disable_save = args . disable_save
if not ctx . disable_save :
if not ctx . save_filename :
ctx . save_filename = ( ctx . data_filename [ : - 9 ] if ctx . data_filename [ - 9 : ] == ' multidata ' else ( ctx . data_filename + ' _ ' ) ) + ' multisave '
try :
with open ( ctx . save_filename , ' rb ' ) as f :
2020-01-04 21:08:13 +00:00
jsonobj = json . loads ( zlib . decompress ( f . read ( ) ) . decode ( " utf-8 " ) )
2020-02-09 04:28:48 +00:00
ctx . set_save ( jsonobj )
2019-12-09 18:27:56 +00:00
except FileNotFoundError :
2020-01-18 11:21:57 +00:00
logging . error ( ' No save data found, starting a new game ' )
2019-12-09 18:27:56 +00:00
except Exception as e :
2020-02-09 04:28:48 +00:00
logging . exception ( e )
2019-12-09 18:27:56 +00:00
ctx . server = websockets . serve ( functools . partial ( server , ctx = ctx ) , ctx . host , ctx . port , ping_timeout = None , ping_interval = None )
await ctx . server
await console ( ctx )
if __name__ == ' __main__ ' :
loop = asyncio . get_event_loop ( )
loop . run_until_complete ( main ( ) )
loop . run_until_complete ( asyncio . gather ( * asyncio . Task . all_tasks ( ) ) )
loop . close ( )