crissian/x4

[Request] Load savegame into the calculator

Closed this issue · 14 comments

Is it possible to add a client side function that reads a savegame (XML in a gzip file) for player owned stations and then gives the user a choice which one to load?
Would be helpful to load station that already exist in the game to see how they can be expanded or optimized further.

Hum, i don't think you can unzip Gzip client side and i think this app don't use server

The app does not have any server side code, however it is possible to read zip files in browser.

There is a library called pako (http://nodeca.github.io/pako/) which support also gzip.

But there is another nice option to import ingame stations without using savegames. Beside the save folder is a file called "constructionplans.xml". This files contains the saved plans of stations. So the only drawback would be to save the station under a specific name before importing it. Here is a sample station from my game.

<plans>
<plan id="player_1551723281" name="Huelle">
<entry index="1" macro="pier_par_harbor_03_macro">...</entry>
<entry index="2" macro="dockarea_arg_m_station_02_hightech_macro" connection="connectionsnap001">...</entry>
<entry index="3" macro="dockarea_arg_m_station_02_hightech_macro" connection="connectionsnap004">...</entry>
<entry index="4" macro="dockarea_arg_m_station_02_hightech_macro" connection="connectionsnap003">...</entry>
<entry index="5" macro="struct_par_base_02_macro" connection="connectionsnap002">...</entry>
<entry index="6" macro="struct_par_cross_01_macro" connection="connectionsnap005">...</entry>
<entry index="7" macro="struct_par_base_02_macro" connection="connectionsnap001">...</entry>
<entry index="8" macro="struct_par_base_02_macro" connection="connectionsnap002">...</entry>
<entry index="9" macro="struct_par_cross_01_macro" connection="connectionsnap002">...</entry>
<entry index="10" macro="struct_par_cross_01_macro" connection="connectionsnap001">...</entry>
<entry index="11" macro="prod_gen_refinedmetals_macro" connection="connectionsnap001">...</entry>
<entry index="12" macro="struct_par_cross_01_macro" connection="connectionsnap006">...</entry>
<entry index="13" macro="prod_gen_graphene_macro" connection="connectionsnap001">...</entry>
<entry index="14" macro="prod_gen_hullparts_macro" connection="connectionsnap001">...</entry>
<entry index="15" macro="prod_gen_energycells_macro" connection="connectionsnap002">...</entry>
<entry index="16" macro="dockarea_arg_m_station_02_hightech_macro" connection="connectionsnap001">...</entry>
</plan>
</plans>

WDYT?

I think using the "constructionplans.xml" is better because it only contains the saved layouts and not everything that is in the game. Which would make it easier on your end and would show the user only the stations they are interested in.

I gave this an initial try, it's super WIP at the moment... dev...luxflux:import-loader

The challenge thing I'm currently facing (besides learning Angular) is that constructionplans.xml contains macros instead of real module names (e.g. prod_gen_energycells_macro instead of module_gen_prod_energycells_01. So I'd need a mapping of all these... Any ideas about that?

I didn't look at the file recently but from what you're saying, you could either map each of the modules to its macro name or come up with some regex to extract the type of module.
If everything follow this order match (prod)_(gen)_([a-z]+)_macro to module_$2_$1_$3_[0-9]+.

But I have no idea how that relates to faction specific modules like the whole food production and housing.

Yeah, I think the issue is mostly about the last bit there: _01 - Should we assume it's just always _01? Maybe it's easier to just extend src/app/shared/services/data/modules-data.ts with an additional key macro or so?
Or are these id something specified by this calculator? Then we could probably also just change it to the macro version the game uses?

That I don't know. Maybe @crissian can elaborate on this.

I made some further progress and it's now working to load my test station. I added the macros from the game to the existing structure in modules-data.ts, but only the ones I needed to get it working: There are still a lot missing.

@crissian what are your thoughts on the approach? dev...luxflux:import-loader

The issue with save file is that it's huge (20MB zipped / 200MB unzipped).
The constructionplans.xml approach looks promising, I just need to generate the macro in modules-data.ts from game files.

Do you have an easy way of doing that? I don’t have a clue how this could be done 😳

Hi @luxflux, I'm trying to run your branch import-loader but I get the following error when I run ng serve --open:

ERROR in src/app/station/components/import-layout.component.ts(14,14): error TS2420: Class 'ImportLayoutComponent' incorrectly implements interface 'OnInit'.
  Property 'ngOnInit' is missing in type 'ImportLayoutComponent'.
src/app/station/components/import-layout.component.ts(51,62): error TS2339: Property 'macroName' does not exist on type '{ id: string; version: number; name: string; description: string; type: ModuleTypes; hull: number; makerRace: { id: string; name: string; description: string; icon: string; }; price: { min: number; max: number; avg: number; }; owners: { ...; }[]; production: { ...; }[]; } | { ...; } | { ...; } | { ...; } | { ...; }'.
  Property 'macroName' does not exist on type '{ id: string; version: number; name: string; description: string; type: ModuleTypes; hull: number; makerRace: { id: string; name: string; description: string; icon: string; }; price: { min: number; max: number; avg: number; }; owners: { ...; }[]; production: { ...; }[]; }'.

The dev branch runs correctly so it seems related to the new changes implemented on the branch. I'm completely new to angular so it could be something really trivial that I'm not seeing.

In case it could be helpful, I wrote a little Python scripts that parses a XML construction plan and generate a URL to the x4-game.com:

https://github.com/leinardi/x4-game-constructionplan-import

The code that maps the module names is the following:

    # Dict[module name, module count]
    modules: Dict[str, int] = {}

    for entry in root.iter("entry"):
        macro = re.sub('_macro$', '', entry.attrib["macro"]).split('_')
        # swapping to array indexes (prod_gen -> gen_prod)
        macro[0], macro[1] = macro[1], macro[0]

        if macro[1] == 'buildmodule':
            macro[1] = "build"
            del macro[2]
            if 'dockarea' in macro:
                macro[2], macro[3] = macro[3], macro[2]
            else:
                macro.append('01')
        elif macro[1] == 'defence':
            macro[1] = "def"
        elif macro[1] == 'dockarea':
            macro[1] = "dock"
            if 'tradestation' in macro:
                macro[5] = macro[3]
                del macro[2]
                del macro[2]
            else:
                del macro[3]
        elif macro[1] == 'hab':
            pass
        elif macro[1] == 'pier':
            if 'harbor' in macro:
                macro[2] = 'l'
        elif macro[1] == 'prod':
            macro.append('01')
            if 'scruffinfruit' in macro:
                macro[2] = macro[2] + 's'

        elif macro[1] == 'storage':
            macro[1] = "stor"
            macro[2], macro[3] = macro[3], macro[2]
        elif macro[1] == 'struct':
            macro[1] = "conn"
            continue

        macro.insert(0, 'module')

        if 'research' not in macro:
            module = '_'.join(macro)
            if module in modules:
                modules[module] += 1
            else:
                modules[module] = 1

I'm guess the same thing can be done in TypeScript. @luxflux maybe is something you want to try?

I've added ability to import the construction plans, however I have no plans to support save games. Those files are too big. Closing the issue.