2021-07-22 01:08:44 +02:00
|
|
|
import os
|
2021-08-05 13:17:01 -05:00
|
|
|
import json
|
2023-07-05 22:39:35 +02:00
|
|
|
import settings
|
|
|
|
import typing
|
2021-08-05 13:17:01 -05:00
|
|
|
from base64 import b64encode, b64decode
|
2023-03-08 21:13:52 -07:00
|
|
|
from typing import Dict, Any
|
2021-07-12 13:54:47 +02:00
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification, Location
|
|
|
|
from worlds.AutoWorld import World, WebWorld
|
Minecraft Randomizer
Squash merge, original Commits:
* 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
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
2021-05-08 07:38:57 -04:00
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
from . import Constants
|
2024-08-19 15:58:30 -07:00
|
|
|
from .Options import MinecraftOptions
|
2023-03-08 21:13:52 -07:00
|
|
|
from .Structures import shuffle_structures
|
|
|
|
from .ItemPool import build_item_pool, get_junk_item_names
|
|
|
|
from .Rules import set_rules
|
2021-06-11 14:22:44 +02:00
|
|
|
|
2022-06-11 14:22:16 -07:00
|
|
|
client_version = 9
|
2022-04-12 14:37:05 -07:00
|
|
|
|
2023-07-05 22:39:35 +02:00
|
|
|
|
|
|
|
class MinecraftSettings(settings.Group):
|
|
|
|
class ForgeDirectory(settings.OptionalUserFolderPath):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class ReleaseChannel(str):
|
|
|
|
"""
|
|
|
|
release channel, currently "release", or "beta"
|
|
|
|
any games played on the "beta" channel have a high likelihood of no longer working on the "release" channel.
|
|
|
|
"""
|
|
|
|
|
2025-05-22 09:42:54 -04:00
|
|
|
class JavaExecutable(settings.OptionalUserFilePath):
|
|
|
|
"""
|
|
|
|
Path to Java executable. If not set, will attempt to fall back to Java system installation.
|
|
|
|
"""
|
|
|
|
|
|
|
|
forge_directory: ForgeDirectory = ForgeDirectory("Minecraft NeoForge server")
|
2023-07-05 22:39:35 +02:00
|
|
|
max_heap_size: str = "2G"
|
|
|
|
release_channel: ReleaseChannel = ReleaseChannel("release")
|
2025-05-22 09:42:54 -04:00
|
|
|
java: JavaExecutable = JavaExecutable("")
|
2023-07-05 22:39:35 +02:00
|
|
|
|
|
|
|
|
2022-04-12 14:37:05 -07:00
|
|
|
class MinecraftWebWorld(WebWorld):
|
|
|
|
theme = "jungle"
|
|
|
|
bug_report_page = "https://github.com/KonoTyran/Minecraft_AP_Randomizer/issues/new?assignees=&labels=bug&template=bug_report.yaml&title=%5BBug%5D%3A+Brief+Description+of+bug+here"
|
|
|
|
|
2022-05-11 13:05:53 -05:00
|
|
|
setup = Tutorial(
|
2024-02-20 11:22:32 -05:00
|
|
|
"Multiworld Setup Guide",
|
2022-05-11 13:05:53 -05:00
|
|
|
"A guide to setting up the Archipelago Minecraft software on your computer. This guide covers"
|
2022-05-19 12:15:23 -04:00
|
|
|
"single-player, multiworld, and related software.",
|
2022-05-11 13:05:53 -05:00
|
|
|
"English",
|
|
|
|
"minecraft_en.md",
|
|
|
|
"minecraft/en",
|
|
|
|
["Kono Tyran"]
|
|
|
|
)
|
|
|
|
|
|
|
|
setup_es = Tutorial(
|
|
|
|
setup.tutorial_name,
|
|
|
|
setup.description,
|
|
|
|
"Español",
|
|
|
|
"minecraft_es.md",
|
|
|
|
"minecraft/es",
|
|
|
|
["Edos"]
|
|
|
|
)
|
|
|
|
|
|
|
|
setup_sv = Tutorial(
|
|
|
|
setup.tutorial_name,
|
|
|
|
setup.description,
|
|
|
|
"Swedish",
|
|
|
|
"minecraft_sv.md",
|
|
|
|
"minecraft/sv",
|
|
|
|
["Albinum"]
|
|
|
|
)
|
|
|
|
|
2023-02-27 23:17:54 +01:00
|
|
|
setup_fr = Tutorial(
|
|
|
|
setup.tutorial_name,
|
|
|
|
setup.description,
|
|
|
|
"Français",
|
|
|
|
"minecraft_fr.md",
|
|
|
|
"minecraft/fr",
|
|
|
|
["TheLynk"]
|
|
|
|
)
|
|
|
|
|
|
|
|
tutorials = [setup, setup_es, setup_sv, setup_fr]
|
2022-05-11 13:05:53 -05:00
|
|
|
|
2022-04-12 14:37:05 -07:00
|
|
|
|
2021-06-11 14:22:44 +02:00
|
|
|
class MinecraftWorld(World):
|
2021-08-31 17:28:46 -04:00
|
|
|
"""
|
|
|
|
Minecraft is a game about creativity. In a world made entirely of cubes, you explore, discover, mine,
|
|
|
|
craft, and try not to explode. Delve deep into the earth and discover abandoned mines, ancient
|
|
|
|
structures, and materials to create a portal to another world. Defeat the Ender Dragon, and claim
|
|
|
|
victory!
|
|
|
|
"""
|
2024-08-19 15:58:30 -07:00
|
|
|
game = "Minecraft"
|
|
|
|
options_dataclass = MinecraftOptions
|
|
|
|
options: MinecraftOptions
|
2023-07-05 22:39:35 +02:00
|
|
|
settings: typing.ClassVar[MinecraftSettings]
|
2021-07-08 11:07:41 +02:00
|
|
|
topology_present = True
|
2022-04-12 14:37:05 -07:00
|
|
|
web = MinecraftWebWorld()
|
Minecraft Randomizer
Squash merge, original Commits:
* 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
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
2021-05-08 07:38:57 -04:00
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
item_name_to_id = Constants.item_name_to_id
|
|
|
|
location_name_to_id = Constants.location_name_to_id
|
2021-07-12 18:05:46 +02:00
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
def _get_mc_data(self) -> Dict[str, Any]:
|
|
|
|
exits = [connection[0] for connection in Constants.region_info["default_connections"]]
|
2021-06-15 18:15:05 -05:00
|
|
|
return {
|
2024-08-19 15:58:30 -07:00
|
|
|
'world_seed': self.random.getrandbits(32),
|
2022-10-31 21:41:21 -05:00
|
|
|
'seed_name': self.multiworld.seed_name,
|
2024-08-19 15:58:30 -07:00
|
|
|
'player_name': self.player_name,
|
2021-06-15 18:15:05 -05:00
|
|
|
'player_id': self.player,
|
|
|
|
'client_version': client_version,
|
2022-10-31 21:41:21 -05:00
|
|
|
'structures': {exit: self.multiworld.get_entrance(exit, self.player).connected_region.name for exit in exits},
|
2024-08-19 15:58:30 -07:00
|
|
|
'advancement_goal': self.options.advancement_goal.value,
|
|
|
|
'egg_shards_required': min(self.options.egg_shards_required.value,
|
|
|
|
self.options.egg_shards_available.value),
|
|
|
|
'egg_shards_available': self.options.egg_shards_available.value,
|
|
|
|
'required_bosses': self.options.required_bosses.current_key,
|
|
|
|
'MC35': bool(self.options.send_defeated_mobs.value),
|
|
|
|
'death_link': bool(self.options.death_link.value),
|
|
|
|
'starting_items': json.dumps(self.options.starting_items.value),
|
2022-10-31 21:41:21 -05:00
|
|
|
'race': self.multiworld.is_race,
|
2021-06-15 18:15:05 -05:00
|
|
|
}
|
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
def create_item(self, name: str) -> Item:
|
|
|
|
item_class = ItemClassification.filler
|
|
|
|
if name in Constants.item_info["progression_items"]:
|
|
|
|
item_class = ItemClassification.progression
|
|
|
|
elif name in Constants.item_info["useful_items"]:
|
|
|
|
item_class = ItemClassification.useful
|
|
|
|
elif name in Constants.item_info["trap_items"]:
|
|
|
|
item_class = ItemClassification.trap
|
|
|
|
|
|
|
|
return MinecraftItem(name, item_class, self.item_name_to_id.get(name, None), self.player)
|
|
|
|
|
|
|
|
def create_event(self, region_name: str, event_name: str) -> None:
|
|
|
|
region = self.multiworld.get_region(region_name, self.player)
|
|
|
|
loc = MinecraftLocation(self.player, event_name, None, region)
|
|
|
|
loc.place_locked_item(self.create_event_item(event_name))
|
|
|
|
region.locations.append(loc)
|
|
|
|
|
2024-08-19 15:58:30 -07:00
|
|
|
def create_event_item(self, name: str) -> Item:
|
2023-03-08 21:13:52 -07:00
|
|
|
item = self.create_item(name)
|
|
|
|
item.classification = ItemClassification.progression
|
|
|
|
return item
|
2021-06-15 18:15:05 -05:00
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
def create_regions(self) -> None:
|
|
|
|
# Create regions
|
|
|
|
for region_name, exits in Constants.region_info["regions"]:
|
|
|
|
r = Region(region_name, self.player, self.multiworld)
|
|
|
|
for exit_name in exits:
|
|
|
|
r.exits.append(Entrance(self.player, exit_name, r))
|
|
|
|
self.multiworld.regions.append(r)
|
|
|
|
|
|
|
|
# Bind mandatory connections
|
|
|
|
for entr_name, region_name in Constants.region_info["mandatory_connections"]:
|
|
|
|
e = self.multiworld.get_entrance(entr_name, self.player)
|
|
|
|
r = self.multiworld.get_region(region_name, self.player)
|
|
|
|
e.connect(r)
|
|
|
|
|
|
|
|
# Add locations
|
|
|
|
for region_name, locations in Constants.location_info["locations_by_region"].items():
|
|
|
|
region = self.multiworld.get_region(region_name, self.player)
|
|
|
|
for loc_name in locations:
|
|
|
|
loc = MinecraftLocation(self.player, loc_name,
|
|
|
|
self.location_name_to_id.get(loc_name, None), region)
|
|
|
|
region.locations.append(loc)
|
|
|
|
|
|
|
|
# Add events
|
|
|
|
self.create_event("Nether Fortress", "Blaze Rods")
|
|
|
|
self.create_event("The End", "Ender Dragon")
|
|
|
|
self.create_event("Nether Fortress", "Wither")
|
|
|
|
|
|
|
|
# Shuffle the connections
|
|
|
|
shuffle_structures(self)
|
|
|
|
|
|
|
|
def create_items(self) -> None:
|
|
|
|
self.multiworld.itempool += build_item_pool(self)
|
|
|
|
|
|
|
|
set_rules = set_rules
|
|
|
|
|
|
|
|
def generate_output(self, output_directory: str) -> None:
|
2021-06-15 18:15:05 -05:00
|
|
|
data = self._get_mc_data()
|
2023-10-28 21:43:09 +02:00
|
|
|
filename = f"{self.multiworld.get_out_file_name_base(self.player)}.apmc"
|
2021-07-22 01:08:44 +02:00
|
|
|
with open(os.path.join(output_directory, filename), 'wb') as f:
|
2021-06-15 18:22:12 -05:00
|
|
|
f.write(b64encode(bytes(json.dumps(data), 'utf-8')))
|
2021-06-15 18:15:05 -05:00
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
def fill_slot_data(self) -> dict:
|
2024-08-19 15:58:30 -07:00
|
|
|
return self._get_mc_data()
|
2021-07-12 13:54:47 +02:00
|
|
|
|
2023-03-08 21:13:52 -07:00
|
|
|
def get_filler_item_name(self) -> str:
|
2024-08-19 15:58:30 -07:00
|
|
|
return get_junk_item_names(self.random, 1)[0]
|
2023-03-08 21:13:52 -07:00
|
|
|
|
|
|
|
|
|
|
|
class MinecraftLocation(Location):
|
|
|
|
game = "Minecraft"
|
|
|
|
|
|
|
|
class MinecraftItem(Item):
|
|
|
|
game = "Minecraft"
|
2022-06-17 03:23:27 +02:00
|
|
|
|
2021-08-05 13:17:01 -05:00
|
|
|
|
2021-08-31 17:28:46 -04:00
|
|
|
def mc_update_output(raw_data, server, port):
|
2021-08-05 13:17:01 -05:00
|
|
|
data = json.loads(b64decode(raw_data))
|
|
|
|
data['server'] = server
|
|
|
|
data['port'] = port
|
|
|
|
return b64encode(bytes(json.dumps(data), 'utf-8'))
|