| 
									
										
										
										
											2020-01-12 17:03:30 +01:00
										 |  |  | import os | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  | import threading | 
					
						
							|  |  |  | import concurrent.futures | 
					
						
							| 
									
										
										
										
											2020-04-21 00:02:03 -04:00
										 |  |  | import argparse | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2021-04-12 09:45:07 +02:00
										 |  |  | import random | 
					
						
							| 
									
										
										
										
											2020-01-12 17:03:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def feedback(text: str): | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |     logging.info(text) | 
					
						
							| 
									
										
										
										
											2020-01-12 17:03:30 +01:00
										 |  |  |     input("Press Enter to ignore and probably crash.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |     logging.basicConfig(format='%(message)s', level=logging.INFO) | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  |         import ModuleUpdate | 
					
						
							| 
									
										
										
										
											2020-03-06 00:48:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  |         ModuleUpdate.update() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 00:02:03 -04:00
										 |  |  |         parser = argparse.ArgumentParser(add_help=False) | 
					
						
							|  |  |  |         parser.add_argument('--disable_autohost', action='store_true') | 
					
						
							|  |  |  |         args = parser.parse_args() | 
					
						
							| 
									
										
										
										
											2020-04-02 05:47:46 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         from Utils import get_public_ipv4, get_options | 
					
						
							| 
									
										
										
										
											2021-04-12 09:45:07 +02:00
										 |  |  |         from Mystery import get_seed_name | 
					
						
							| 
									
										
										
										
											2020-03-06 00:48:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 11:21:33 +02:00
										 |  |  |         options = get_options() | 
					
						
							| 
									
										
										
										
											2020-02-09 05:28:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 00:48:23 +01:00
										 |  |  |         multi_mystery_options = options["multi_mystery_options"] | 
					
						
							| 
									
										
										
										
											2020-08-20 15:43:22 +02:00
										 |  |  |         output_path = options["general_options"]["output_path"] | 
					
						
							| 
									
										
										
										
											2020-02-09 05:28:48 +01:00
										 |  |  |         enemizer_path = multi_mystery_options["enemizer_path"] | 
					
						
							|  |  |  |         player_files_path = multi_mystery_options["player_files_path"] | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |         target_player_count = multi_mystery_options["players"] | 
					
						
							| 
									
										
										
										
											2021-03-22 13:14:19 -07:00
										 |  |  |         glitch_triforce = multi_mystery_options["glitch_triforce_room"] | 
					
						
							| 
									
										
										
										
											2020-02-09 05:28:48 +01:00
										 |  |  |         race = multi_mystery_options["race"] | 
					
						
							| 
									
										
										
										
											2021-01-02 12:49:43 +01:00
										 |  |  |         plando_options = multi_mystery_options["plando_options"] | 
					
						
							| 
									
										
										
										
											2020-02-09 05:28:48 +01:00
										 |  |  |         create_spoiler = multi_mystery_options["create_spoiler"] | 
					
						
							|  |  |  |         zip_roms = multi_mystery_options["zip_roms"] | 
					
						
							| 
									
										
										
										
											2020-03-06 00:48:23 +01:00
										 |  |  |         zip_diffs = multi_mystery_options["zip_diffs"] | 
					
						
							| 
									
										
											  
											
												Minecraft updates (#13)
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
* Minecraft logic updates
Wither kill now considers nether fortresses as a valid source of soul sand
A Furious Cocktail now requires beacons for resistance and village access for carrots
Uneasy Alliance now requires fishing rod to pull the ghast through the portal
On a Rail now requires iron pickaxe to make powered rails
Overkill now may require strength II without stone axe, which needs nether access
* embed all apmc info into slot_data
* updated MC tests for logic changes
* put apmc into zipfile
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
											
										 
											2021-05-15 18:49:58 -04:00
										 |  |  |         zip_apmcs = multi_mystery_options["zip_apmcs"] | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  |         zip_spoiler = multi_mystery_options["zip_spoiler"] | 
					
						
							|  |  |  |         zip_multidata = multi_mystery_options["zip_multidata"] | 
					
						
							| 
									
										
										
										
											2020-02-21 10:57:57 +01:00
										 |  |  |         zip_format = multi_mystery_options["zip_format"] | 
					
						
							| 
									
										
										
										
											2020-11-08 07:31:32 +01:00
										 |  |  |         # zip_password = multi_mystery_options["zip_password"] not at this time | 
					
						
							| 
									
										
										
										
											2020-02-09 05:28:48 +01:00
										 |  |  |         player_name = multi_mystery_options["player_name"] | 
					
						
							| 
									
										
										
										
											2020-02-18 09:14:31 +01:00
										 |  |  |         meta_file_path = multi_mystery_options["meta_file_path"] | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |         weights_file_path = multi_mystery_options["weights_file_path"] | 
					
						
							| 
									
										
										
										
											2021-03-03 02:20:37 -08:00
										 |  |  |         pre_roll = multi_mystery_options["pre_roll"] | 
					
						
							| 
									
										
										
										
											2020-02-23 17:06:44 +01:00
										 |  |  |         teams = multi_mystery_options["teams"] | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         rom_file = options["lttp_options"]["rom_file"] | 
					
						
							| 
									
										
										
										
											2020-04-02 10:40:38 -07:00
										 |  |  |         host = options["server_options"]["host"] | 
					
						
							| 
									
										
										
										
											2020-04-02 11:21:33 +02:00
										 |  |  |         port = options["server_options"]["port"] | 
					
						
							| 
									
										
										
										
											2020-02-09 05:28:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         py_version = f"{sys.version_info.major}.{sys.version_info.minor}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not os.path.exists(enemizer_path): | 
					
						
							| 
									
										
										
										
											2020-11-08 07:31:32 +01:00
										 |  |  |             feedback( | 
					
						
							|  |  |  |                 f"Enemizer not found at {enemizer_path}, please adjust the path in MultiMystery.py's config or put Enemizer in the default location.") | 
					
						
							| 
									
										
										
										
											2020-02-23 17:12:21 +01:00
										 |  |  |         if not os.path.exists(rom_file): | 
					
						
							|  |  |  |             feedback(f"Base rom is expected as {rom_file} in the Multiworld root folder please place/rename it there.") | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |         player_files = [] | 
					
						
							| 
									
										
										
										
											2020-02-09 05:28:48 +01:00
										 |  |  |         os.makedirs(player_files_path, exist_ok=True) | 
					
						
							|  |  |  |         for file in os.listdir(player_files_path): | 
					
						
							| 
									
										
										
										
											2020-02-18 09:14:31 +01:00
										 |  |  |             lfile = file.lower() | 
					
						
							| 
									
										
										
										
											2020-11-08 07:26:50 +01:00
										 |  |  |             if lfile.endswith(".yaml") and lfile != meta_file_path.lower() and lfile != weights_file_path.lower(): | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |                 player_files.append(file) | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |                 logging.info(f"Found player's file {file}.") | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         player_string = "" | 
					
						
							| 
									
										
										
										
											2020-02-23 17:06:44 +01:00
										 |  |  |         for i, file in enumerate(player_files, 1): | 
					
						
							| 
									
										
										
										
											2020-08-30 03:18:10 +02:00
										 |  |  |             player_string += f"--p{i} \"{os.path.join(player_files_path, file)}\" " | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-03 14:32:32 +01:00
										 |  |  |         if os.path.exists("ArchipelagoMystery.exe"): | 
					
						
							|  |  |  |             basemysterycommand = "ArchipelagoMystery.exe"  # compiled windows | 
					
						
							|  |  |  |         elif os.path.exists("ArchipelagoMystery"): | 
					
						
							|  |  |  |             basemysterycommand = "ArchipelagoMystery"  # compiled linux | 
					
						
							| 
									
										
										
										
											2020-02-02 22:36:55 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2020-08-26 22:28:48 +02:00
										 |  |  |             basemysterycommand = f"py -{py_version} Mystery.py"  # source | 
					
						
							| 
									
										
										
										
											2020-02-02 22:36:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-08 07:26:50 +01:00
										 |  |  |         weights_file_path = os.path.join(player_files_path, weights_file_path) | 
					
						
							|  |  |  |         if os.path.exists(weights_file_path): | 
					
						
							|  |  |  |             target_player_count = max(len(player_files), target_player_count) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             target_player_count = len(player_files) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if target_player_count == 0: | 
					
						
							|  |  |  |             feedback(f"No player files found. Please put them in a {player_files_path} folder.") | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |             logging.info(f"{target_player_count} Players found.") | 
					
						
							| 
									
										
										
										
											2021-04-12 09:45:07 +02:00
										 |  |  |         seed_name = get_seed_name(random) | 
					
						
							| 
									
										
										
										
											2020-11-08 07:26:50 +01:00
										 |  |  |         command = f"{basemysterycommand} --multi {target_player_count} {player_string} " \ | 
					
						
							| 
									
										
										
										
											2020-02-23 17:06:44 +01:00
										 |  |  |                   f"--rom \"{rom_file}\" --enemizercli \"{enemizer_path}\" " \ | 
					
						
							| 
									
										
										
										
											2021-04-12 09:45:07 +02:00
										 |  |  |                   f"--outputpath \"{output_path}\" --teams {teams} --plando \"{plando_options}\" " \ | 
					
						
							|  |  |  |                   f"--seed_name {seed_name}" | 
					
						
							| 
									
										
										
										
											2020-02-18 09:14:31 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if create_spoiler: | 
					
						
							| 
									
										
										
										
											2020-02-23 17:06:44 +01:00
										 |  |  |             command += " --create_spoiler" | 
					
						
							| 
									
										
										
										
											2020-05-31 00:28:03 +02:00
										 |  |  |             if create_spoiler == 2: | 
					
						
							|  |  |  |                 command += " --skip_playthrough" | 
					
						
							| 
									
										
										
										
											2020-08-26 22:28:48 +02:00
										 |  |  |         if zip_diffs: | 
					
						
							|  |  |  |             command += " --create_diff" | 
					
						
							| 
									
										
										
										
											2021-03-22 13:14:19 -07:00
										 |  |  |         if glitch_triforce: | 
					
						
							|  |  |  |             command += " --glitch_triforce" | 
					
						
							| 
									
										
										
										
											2020-02-18 09:14:31 +01:00
										 |  |  |         if race: | 
					
						
							|  |  |  |             command += " --race" | 
					
						
							|  |  |  |         if os.path.exists(os.path.join(player_files_path, meta_file_path)): | 
					
						
							|  |  |  |             command += f" --meta {os.path.join(player_files_path, meta_file_path)}" | 
					
						
							| 
									
										
										
										
											2020-11-08 07:26:50 +01:00
										 |  |  |         if os.path.exists(weights_file_path): | 
					
						
							|  |  |  |             command += f" --weights {weights_file_path}" | 
					
						
							| 
									
										
										
										
											2021-03-03 02:20:37 -08:00
										 |  |  |         if pre_roll: | 
					
						
							|  |  |  |             command += " --pre_roll" | 
					
						
							| 
									
										
										
										
											2020-02-18 09:14:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |         logging.info(command) | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |         import time | 
					
						
							| 
									
										
										
										
											2020-08-26 22:28:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |         start = time.perf_counter() | 
					
						
							|  |  |  |         text = subprocess.check_output(command, shell=True).decode() | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |         logging.info(f"Took {time.perf_counter() - start:.3f} seconds to generate multiworld.") | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-12 09:45:07 +02:00
										 |  |  |         multidataname = f"AP_{seed_name}.archipelago" | 
					
						
							|  |  |  |         spoilername = f"AP_{seed_name}_Spoiler.txt" | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |         romfilename = "" | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |         if player_name: | 
					
						
							| 
									
										
										
										
											2020-02-16 09:44:32 +01:00
										 |  |  |             for file in os.listdir(output_path): | 
					
						
							|  |  |  |                 if player_name in file: | 
					
						
							| 
									
										
										
										
											2020-07-15 17:19:16 +02:00
										 |  |  |                     import MultiClient | 
					
						
							|  |  |  |                     import asyncio | 
					
						
							| 
									
										
										
										
											2020-02-16 09:44:32 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-15 17:19:16 +02:00
										 |  |  |                     asyncio.run(MultiClient.run_game(os.path.join(output_path, file))) | 
					
						
							| 
									
										
										
										
											2020-02-16 09:44:32 +01:00
										 |  |  |                     break | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Minecraft updates (#13)
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
* Minecraft logic updates
Wither kill now considers nether fortresses as a valid source of soul sand
A Furious Cocktail now requires beacons for resistance and village access for carrots
Uneasy Alliance now requires fishing rod to pull the ghast through the portal
On a Rail now requires iron pickaxe to make powered rails
Overkill now may require strength II without stone axe, which needs nether access
* embed all apmc info into slot_data
* updated MC tests for logic changes
* put apmc into zipfile
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
											
										 
											2021-05-15 18:49:58 -04:00
										 |  |  |         if any((zip_roms, zip_multidata, zip_spoiler, zip_diffs, zip_apmcs)): | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  |             import zipfile | 
					
						
							| 
									
										
										
										
											2020-11-08 07:31:32 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             compression = {1: zipfile.ZIP_DEFLATED, | 
					
						
							|  |  |  |                            2: zipfile.ZIP_LZMA, | 
					
						
							|  |  |  |                            3: zipfile.ZIP_BZIP2}[zip_format] | 
					
						
							| 
									
										
										
										
											2020-02-21 10:57:57 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             typical_zip_ending = {1: "zip", | 
					
						
							|  |  |  |                                   2: "7z", | 
					
						
							|  |  |  |                                   3: "bz2"}[zip_format] | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  |             ziplock = threading.Lock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  |             def pack_file(file: str): | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  |                 with ziplock: | 
					
						
							|  |  |  |                     zf.write(os.path.join(output_path, file), file) | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |                     logging.info(f"Packed {file} into zipfile {zipname}") | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             def remove_zipped_file(file: str): | 
					
						
							|  |  |  |                 os.remove(os.path.join(output_path, file)) | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |                 logging.info(f"Removed {file} which is now present in the zipfile") | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-12 09:45:07 +02:00
										 |  |  |             zipname = os.path.join(output_path, f"AP_{seed_name}.{typical_zip_ending}") | 
					
						
							| 
									
										
										
										
											2020-02-21 10:57:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 14:51:13 -08:00
										 |  |  |             logging.info(f"Creating zipfile {zipname}") | 
					
						
							| 
									
										
										
										
											2020-04-02 10:40:38 -07:00
										 |  |  |             ipv4 = (host if host else get_public_ipv4()) + ":" + str(port) | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 22:28:48 +02:00
										 |  |  |             def _handle_sfc_file(file: str): | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  |                 if zip_roms: | 
					
						
							|  |  |  |                     pack_file(file) | 
					
						
							|  |  |  |                     if zip_roms == 2 and player_name.lower() not in file.lower(): | 
					
						
							|  |  |  |                         remove_zipped_file(file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 22:28:48 +02:00
										 |  |  |             def _handle_diff_file(file: str): | 
					
						
							|  |  |  |                 if zip_diffs > 0: | 
					
						
							|  |  |  |                     pack_file(file) | 
					
						
							|  |  |  |                     if zip_diffs == 2: | 
					
						
							|  |  |  |                         remove_zipped_file(file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 01:16:51 +02:00
										 |  |  |             def _handle_apmc_file(file: str): | 
					
						
							| 
									
										
											  
											
												Minecraft updates (#13)
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
* Minecraft logic updates
Wither kill now considers nether fortresses as a valid source of soul sand
A Furious Cocktail now requires beacons for resistance and village access for carrots
Uneasy Alliance now requires fishing rod to pull the ghast through the portal
On a Rail now requires iron pickaxe to make powered rails
Overkill now may require strength II without stone axe, which needs nether access
* embed all apmc info into slot_data
* updated MC tests for logic changes
* put apmc into zipfile
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
											
										 
											2021-05-15 18:49:58 -04:00
										 |  |  |                 if zip_apmcs: | 
					
						
							|  |  |  |                     pack_file(file) | 
					
						
							| 
									
										
										
										
											2021-05-16 01:16:51 +02:00
										 |  |  |                     if zip_apmcs == 2: | 
					
						
							| 
									
										
											  
											
												Minecraft updates (#13)
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
* Minecraft logic updates
Wither kill now considers nether fortresses as a valid source of soul sand
A Furious Cocktail now requires beacons for resistance and village access for carrots
Uneasy Alliance now requires fishing rod to pull the ghast through the portal
On a Rail now requires iron pickaxe to make powered rails
Overkill now may require strength II without stone axe, which needs nether access
* embed all apmc info into slot_data
* updated MC tests for logic changes
* put apmc into zipfile
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
											
										 
											2021-05-15 18:49:58 -04:00
										 |  |  |                         remove_zipped_file(file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  |             with concurrent.futures.ThreadPoolExecutor() as pool: | 
					
						
							|  |  |  |                 futures = [] | 
					
						
							| 
									
										
										
										
											2021-05-16 01:16:51 +02:00
										 |  |  |                 files = os.listdir(output_path) | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  |                 with zipfile.ZipFile(zipname, "w", compression=compression, compresslevel=9) as zf: | 
					
						
							| 
									
										
										
										
											2021-05-16 01:16:51 +02:00
										 |  |  |                     for file in files: | 
					
						
							| 
									
										
										
										
											2021-04-12 09:45:07 +02:00
										 |  |  |                         if seed_name in file: | 
					
						
							| 
									
										
										
										
											2020-08-26 22:28:48 +02:00
										 |  |  |                             if file.endswith(".sfc"): | 
					
						
							|  |  |  |                                 futures.append(pool.submit(_handle_sfc_file, file)) | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |                             elif file.endswith(".apbp"): | 
					
						
							| 
									
										
										
										
											2020-08-26 22:28:48 +02:00
										 |  |  |                                 futures.append(pool.submit(_handle_diff_file, file)) | 
					
						
							| 
									
										
											  
											
												Minecraft updates (#13)
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
* Minecraft logic updates
Wither kill now considers nether fortresses as a valid source of soul sand
A Furious Cocktail now requires beacons for resistance and village access for carrots
Uneasy Alliance now requires fishing rod to pull the ghast through the portal
On a Rail now requires iron pickaxe to make powered rails
Overkill now may require strength II without stone axe, which needs nether access
* embed all apmc info into slot_data
* updated MC tests for logic changes
* put apmc into zipfile
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
											
										 
											2021-05-15 18:49:58 -04:00
										 |  |  |                             elif file.endswith(".apmc"): | 
					
						
							|  |  |  |                                 futures.append(pool.submit(_handle_apmc_file, file)) | 
					
						
							| 
									
										
										
										
											2021-05-16 01:16:51 +02:00
										 |  |  |                             # just handle like a diff file for now | 
					
						
							|  |  |  |                             elif file.endswith(".zip"): | 
					
						
							|  |  |  |                                 futures.append(pool.submit(_handle_diff_file, file)) | 
					
						
							| 
									
										
										
										
											2020-04-10 14:27:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     if zip_multidata and os.path.exists(os.path.join(output_path, multidataname)): | 
					
						
							|  |  |  |                         pack_file(multidataname) | 
					
						
							|  |  |  |                         if zip_multidata == 2: | 
					
						
							|  |  |  |                             remove_zipped_file(multidataname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if zip_spoiler and create_spoiler: | 
					
						
							|  |  |  |                         pack_file(spoilername) | 
					
						
							|  |  |  |                         if zip_spoiler == 2: | 
					
						
							|  |  |  |                             remove_zipped_file(spoilername) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     for future in futures: | 
					
						
							|  |  |  |                         future.result()  # make sure we close the zip AFTER any packing is done | 
					
						
							| 
									
										
										
										
											2020-02-17 02:09:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 00:02:03 -04:00
										 |  |  |         if not args.disable_autohost: | 
					
						
							|  |  |  |             if os.path.exists(os.path.join(output_path, multidataname)): | 
					
						
							| 
									
										
										
										
											2021-01-03 14:32:32 +01:00
										 |  |  |                 if os.path.exists("ArchipelagoServer.exe"): | 
					
						
							|  |  |  |                     baseservercommand = "ArchipelagoServer.exe"  # compiled windows | 
					
						
							|  |  |  |                 elif os.path.exists("ArchipelagoServer"): | 
					
						
							|  |  |  |                     baseservercommand = "ArchipelagoServer"  # compiled linux | 
					
						
							| 
									
										
										
										
											2020-04-21 00:02:03 -04:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     baseservercommand = f"py -{py_version} MultiServer.py"  # source | 
					
						
							| 
									
										
										
										
											2020-11-08 07:31:32 +01:00
										 |  |  |                 # don't have a mac to test that. If you try to run compiled on mac, good luck. | 
					
						
							| 
									
										
										
										
											2020-04-21 00:02:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 subprocess.call(f"{baseservercommand} --multidata {os.path.join(output_path, multidataname)}") | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |     except: | 
					
						
							|  |  |  |         import traceback | 
					
						
							| 
									
										
										
										
											2020-11-08 07:31:32 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 20:24:21 +01:00
										 |  |  |         traceback.print_exc() | 
					
						
							|  |  |  |         input("Press enter to close") |