Beispiel #1
0
    def add_var(self, name: str, value: Resource) -> Resource:
        """
        Adds a variable to this context and assigns it the correct variable context.
        Note that there must be a valid variable context!
        Fails if the variable name does already exist.

        Args:
            name: the name of the variable
            value: the resource

        Returns:
            None

        Raises:
            ValueError: If the resource does already exist
        """
        if name in self.namespace:
            raise ValueError(f"Variable {name} does already exist!")

        # should an error be thrown when no history is found?
        variable_context = self.variable_context.get(name, None)
        if variable_context is None:
            Logger.debug(
                f"[Context] No context data available for variable '{name}' ({value})"
            )

        value.is_variable = True
        self.namespace[name] = self.Variable(value, variable_context)
        return value
Beispiel #2
0
    def toJson(self, markup: str, *args: Resource) -> List[dict]:
        """
        Converts a markup string to a minecraft json format string.

        Args:
            markup: the markup string
            *args: resources to insert into the placeholders

        Returns:
            A json string
        """
        self.state = dict(args=args, used_placeholders=set())
        try:
            Logger.debug(f"[MarkupParser] parsing '{markup}'")
            tree = get_json_markup_grammar().parse(markup)
            debug_log_text(tree.pretty(), "Parse tree: ")
            data = self.visit(tree)
        except UnexpectedToken as e:
            raise McScriptInvalidMarkupError(f"\nFailed to parse Markup string:\n"
                                             f"{e.get_context(markup, span=len(markup))}"
                                             f"Unexpected token: {e.token.type}('{e.token}')\n"
                                             f"Expected one of {e.expected}", self.compileState)

        all_args = set(range(len(args)))
        for used_arg in self.state["used_placeholders"]:
            all_args.remove(used_arg)

        if all_args:
            raise McScriptArgumentError(f"Not all arguments were used!\nunused indices: "
                                        f"{', '.join(str(i) for i in all_args)}", self.compileState)

        # remove duplicate text elements
        return self.compact_data(data)
Beispiel #3
0
def debug_log_text(text: str, message):
    """Debug logs a multi-line text and annotates it with line numbers."""
    text = text.strip().split("\n")
    padding = len(str(len(text)))
    debug_code = "\n\t".join(f"[{str(index + 1).zfill(padding)}] {i}"
                             for index, i in enumerate(text))
    Logger.debug(f"{message}\n\t{debug_code}")
Beispiel #4
0
    def __init__(self, msg: str):
        Logger.error(f"Exception occurred: {self.__class__.__name__}")

        debug_exception = ["\n".join(f"\t* {i}" for i in msg.split("\n"))]
        super().__init__(msg)
        debug_exception += self.get_stack()
        debug_exception = "\n".join(debug_exception)
        Logger.debug(f"Exception message:\n{debug_exception}")
Beispiel #5
0
 def __post_init__(self):
     if len(self.name) > 16:
         raise ValueError("Cannot create a scoreboard with a length > 16")
     if self.index >= len(VALID_OBJECTIVE_CHARACTERS)**3:
         raise ValueError(
             f"Maximum id exceeded: {self.index} expected at most {len(VALID_OBJECTIVE_CHARACTERS) ** 3 - 1}"
         )
     Logger.debug(
         f"[Scoreboard] created {self.name} with id {self.get_name()}")
Beispiel #6
0
def runDataGenerator(version: str, fpath: str) -> str:
    """ Downloads the versions, runs the data generator and returns the full path to the generated data"""
    path, file = split(download_minecraft_server(version, fpath))
    Logger.info("[Assets] generating minecraft data...")

    # test if java is installed
    process = run(["java", "-version"], cwd=path)
    Logger.info("Process java version result: {}".format(process.returncode))

    completedProcess = run(
        ["java", "-cp", file, "net.minecraft.data.Main", "-all"], cwd=path)
    completedProcess.check_returncode()

    return join(path, "generated")
Beispiel #7
0
def run_code(rcon: MCRcon, test_world: MCWorld,
             code: str) -> Tuple[Optional[int], str]:
    config = Config()
    config.output_dir = Path(test_world.getDatapackPath()) / "mcscript"
    config.input_string = TEST_TEMPLATE.format(code)
    datapack = compileMcScript(config)
    Logger.info(datapack.getMainDirectory().getPath("functions").files)
    generate_datapack(config, datapack)

    rcon.command("reload")
    result = rcon.command(f"scoreboard players get result mcscript_test")

    match = RESULT_PATTERN.match(result)
    if match is not None:
        value = int(match.group(1))
    else:
        value = None

    return value, result
Beispiel #8
0
def downloadVersionManifest() -> Dict:
    """
    Downloads the minecraft version manifest and returns it parsed as a dictionary

    Format:
        {
            "latest": {
                "release": str
                "snapshot": str
            },
            "versions": [
                {
                    "id": str,
                    "type": str,
                    "url": str,
                    "time": str,
                    "releaseTime": str
                }
            ]
        }

    Returns:
        The version manifest
    """
    Logger.info("[Assets] Fetching version manifest...")
    request = _get(VERSION_MANIFEST_URL)
    manifest = json.load(request)
    Logger.info("[Assets] Successfully fetched version manifest")
    Logger.debug(manifest)
    return manifest
Beispiel #9
0
def makeData(version: str) -> str:
    """
    writes all important minecraft data to a file and returns the path
    Note that this wil crash if the version is below 1.14.
    """

    version = version or get_latest_version()

    # first test if the file already exists
    directory = getVersionDir(version)
    file = join(directory, "data.json")
    if exists(file):
        with open(file) as f:
            try:
                data = json.load(f)
                if data.get("version", 0) != assets.__version__:
                    Logger.warn(
                        f"File '{file}' uses old version format {data.get('version', 0)} "
                        f"(Required {assets.__version__})")
                else:
                    Logger.debug(f"Already got data for version {version}")
                    return file
            except json.JSONDecodeError:
                pass

    contents = getDataJson(version)
    contents["version"] = assets.__version__
    with open(file, "w+") as f:
        json.dump(contents, f)
    Logger.info(f"Create data file {file}")
    return file
Beispiel #10
0
def compileMcScript(config: Config, callback: Callable = None) -> Datapack:
    """
    compiles a mcscript string and returns the generated datapack.

    Args:
        callback: a callback function that accepts the current state, the progress and the temporary object
        config: the config. Should contain the input file and the target path

    Returns:
        A datapack
    """
    steps = (
        (_parseCode, "Parsing"),
        (lambda tree: Analyzer().analyze(tree), "Analyzing context"),
        (lambda tree: get_compiler().compile(tree[0], tree[1], text, config), "Compiling"),
        (lambda ir_master: get_default_backend()(config, ir_master).generate(), "Running ir backend")
    )

    text = config.input_string

    if Logger.isEnabledFor(DEBUG):
        debug_log_text(text, "[Compile] parsing the following code: ")

    global_start_time = perf_counter()

    arg = text
    for index, step in enumerate(steps):
        if callback is not None:
            callback(step[1], index / len(steps), arg)
        start_time = perf_counter()
        try:
            arg = step[0](arg)
        except Exception as e:
            if not isinstance(e, McScriptError):
                Logger.critical(f"Internal compiler error occurred: {repr(e)}")
            raise e
        Logger.info(f"{step[1]} finished in {perf_counter() - start_time:.4f} seconds")
        if isinstance(arg, Tree):
            _debug_log_tree(arg)

    if callback is not None:
        callback("Done", 1, arg)

    Logger.info(f"Run all steps in {perf_counter() - global_start_time:.4f} seconds")

    # noinspection PyTypeChecker
    return arg
Beispiel #11
0
    def __init__(self, path: str = None):
        if Config.currentConfig:
            Config.currentConfig = self
            Logger.warning("[Config] currentConfig already exists!")
        Config.currentConfig = self
        self.path = path
        self.config = configparser.ConfigParser()

        self._input_string: Optional[str] = None
        self._world: Optional[MCWorld] = None
        self._output_dir: Optional[str] = None
        self._data_manager: DataManager = DataManager()

        self.config["main"] = {
            "release": "False",
            "minecraft_version": "",
            "name": "mcscript"
        }

        self.config["scores"] = {
            "format_string": ".exp_{block}_{index}"
        }

        # maximum scoreboard name has 16 chars so `name` must contain 12 chars at most
        self.config["scoreboards"] = {
            "main": self["main"]["name"]
        }

        self.config["storage"] = {
            "name": "main",
            "stack": "state.stack",
            "temp": "state.temp"
        }

        if path:
            if not exists(path):
                Logger.info("[Config] Write default config")
                with open(path, "w+") as f:
                    self.config.write(f)
            self.config.read(path)
            Logger.info("[Config] loaded from file")

        if not self.checkData():
            raise ValueError("Invalid values for config detected!")
Beispiel #12
0
def download_minecraft_server(version_id: str, target: str) -> str:
    """
    Downloads the minecraft server for version `version_id` and returns the full path.

    Args:
        version_id: the id for the version
        target: the target directory

    Returns:
        The full file path to `server.jar`

    """
    url = getVersionUrl(version_id)

    Logger.info(f"[Assets] Downloading minecraft server json at {url}")

    version = json.load(_get(url))

    Logger.info("[Assets] Downloaded version.json")
    Logger.debug(version)

    server_url = version["downloads"]["server"]["url"]
    Logger.info(f"[Assets] Starting to download server {version['id']}...")
    with _get(server_url) as server:
        download_size = server.length
        with click.progressbar(length=download_size,
                               label="Downloading server jar") as bar:
            fpath = join(target, "server.jar")
            with open(fpath, "wb+") as f:
                while chunk := server.read(16384):
                    f.write(chunk)
                    bar.update(len(chunk))
        Logger.info(f"[Assets] Downloaded {download_size / 1000000:.2f} mb")
        Logger.info(f"[Assets] Created server.jar at {fpath}")
Beispiel #13
0
def _debug_log_tree(tree: Tree):
    if Logger.isEnabledFor(DEBUG):
        debug_log_text(tree.pretty(), "[Compile] Intermediate Tree:")
Beispiel #14
0
def import_sub_modules():
    for name in iterBuiltins():
        Logger.debug(f"[Resource] auto-importing {name}")
        import_module("." + name, __name__)
Beispiel #15
0
        identifier, *_ = accessor.children

        self._handle_variable(str(identifier), tree)

    def operation_ip(self, tree: Tree):
        accessor, operator, expression = tree.children
        self.visit(expression)

        identifier, *_ = accessor.children

        if var := getVar(self.stack, identifier):
            var.writes.append(VariableAccess(tree, self.stack[-1].definition))
        else:
            # otherwise the user tries to access an undefined variable
            Logger.error(
                f"[Analyzer] invalid variable access: '{identifier}' is not defined"
            )

    def index_setter(self, tree: Tree):
        accessor, index, expression = tree.children
        self.visit(expression)

        identifier, *not_implemented = accessor.children
        if not_implemented:
            return

        if var := getVar(self.stack, identifier):
            var.writes.append(VariableAccess(tree, self.stack[-1].definition))
        else:
            Logger.error(
                f"[Analyzer] invalid variable array setter: '{identifier}' is not defined"
Beispiel #16
0
                    current_x += x_step
                    j += 1.0
                }
                
                current_y += y_step
                i += 1.0
            }
        }
        
        execute("kill @e[tag=$marker_name]")
    }
}
"""

if __name__ == '__main__':
    mcDir = join(getcwd(), "server")
    world = getWorld("McScript", mcDir)

    config = Config("config.ini")
    config.world = world

    code = code_temp
    # code = getScript("mandelbrot")
    config.input_string = code

    datapack = compileMcScript(
        config,
        lambda a, b, c: Logger.info(f"[compile] {a}: {round(b * 100, 2)}%"))
    generate_datapack(config, datapack)
    rcon.send("reload")