Files
odinsea-elixir/WZ_EXPORT_GUIDE.md
2026-02-14 23:12:33 -07:00

7.9 KiB

WZ Data Export Utility Guide

Overview

The Elixir port uses JSON files for game data instead of parsing WZ files directly. This document describes how to create a Java utility to export WZ data to JSON format.

Required Exports

1. Item Strings (priv/data/item_strings.json)

{
  "2000000": "Red Potion",
  "2000001": "Orange Potion",
  "1002000": "Blue Bandana",
  ...
}

Java Source:

// Use MapleItemInformationProvider
// Iterate over all items, extract names from String.wz

2. Items (priv/data/items.json)

[
  {
    "item_id": 2000000,
    "name": "Red Potion",
    "slot_max": 100,
    "price": 50.0,
    "whole_price": 50,
    "req_level": 0,
    "tradeable": true,
    "cash": false,
    "recover_hp": 50,
    "recover_mp": 0
  },
  ...
]

Fields to Export:

  • item_id, name, desc
  • slot_max, price, whole_price
  • req_level, req_job, req_str, req_dex, req_int, req_luk
  • cash, tradeable, quest, time_limited
  • recover_hp, recover_mp, buff_time
  • meso, monster_book, mob_id
  • All flags and properties

Java Source:

// MapleItemInformationProvider.getAllItems()
// Convert ItemInformation to JSON

3. Equipment (priv/data/equips.json)

[
  {
    "item_id": 1002000,
    "str": 0,
    "dex": 0,
    "int": 0,
    "luk": 0,
    "hp": 0,
    "mp": 0,
    "watk": 0,
    "matk": 0,
    "wdef": 5,
    "mdef": 5,
    "acc": 0,
    "avoid": 0,
    "speed": 0,
    "jump": 0,
    "slots": 7,
    "tuc": 7,
    "item_level": 1
  },
  ...
]

Fields to Export:

  • All stat fields (str, dex, int, luk, hp, mp)
  • Attack/defense (watk, matk, wdef, mdef)
  • Accuracy/avoidance (acc, avoid)
  • Movement (speed, jump)
  • Upgrade slots (slots, tuc)
  • All equipment properties

Java Source:

// MapleItemInformationProvider.getAllItems()
// Filter by MapleInventoryType.EQUIP
// Extract equip stats

4. Monsters (priv/data/monsters.json)

[
  {
    "mob_id": 100100,
    "name": "Blue Snail",
    "level": 1,
    "hp": 50,
    "mp": 0,
    "exp": 3,
    "physical_attack": 8,
    "magic_attack": 8,
    "physical_defense": 10,
    "magic_defense": 10,
    "accuracy": 5,
    "evasion": 3,
    "speed": 50,
    "boss": false,
    "undead": false,
    "flying": false,
    "skills": [],
    "revives": []
  },
  ...
]

Fields to Export:

  • All stats from MapleMonsterStats
  • Behavioral flags (boss, undead, flying, friendly, etc.)
  • Skills, revives
  • All combat properties

Java Source:

// MapleLifeFactory.getAllMonster()
// Convert MapleMonsterStats to JSON

5. NPCs (priv/data/npcs.json)

[
  {
    "npc_id": 1012000,
    "name": "Athena Pierce",
    "has_shop": false,
    "shop_id": null,
    "script": null
  },
  ...
]

Java Source:

// MapleLifeFactory.getAllNPC()
// MapleNPC.npcShopIDs for shop data

6. Maps (priv/data/maps.json)

[
  {
    "map_id": 100000000,
    "map_name": "Henesys",
    "street_name": "Victoria Island",
    "return_map": 100000000,
    "forced_return": 100000000,
    "mob_rate": 1.0,
    "field_limit": 0,
    "time_limit": -1,
    "bgm": "Bgm04/PlayWithMe",
    "portals": [
      {
        "id": 0,
        "name": "sp",
        "type": "sp",
        "x": -1283,
        "y": 86,
        "target_map": 999999999,
        "target_portal": "",
        "script": null
      },
      {
        "id": 1,
        "name": "market00",
        "type": "pv",
        "x": -1183,
        "y": 86,
        "target_map": 910000000,
        "target_portal": "market01",
        "script": null
      }
    ],
    "footholds": [
      {
        "id": 1,
        "x1": -1400,
        "y1": 120,
        "x2": -1200,
        "y2": 120,
        "prev": 0,
        "next": 2
      }
    ],
    "top": -400,
    "bottom": 300,
    "left": -1600,
    "right": 1200
  },
  ...
]

Fields to Export:

  • Map metadata (id, name, street_name)
  • Return maps (return_map, forced_return)
  • Rates (mob_rate, recovery_rate)
  • Limits (field_limit, time_limit)
  • All portals with full data
  • All footholds with connections
  • Bounds (top, bottom, left, right)
  • BGM, scripts, properties

Java Source:

// MapleMapFactory.loadAllFieldTemplates()
// FieldTemplate contains all data
// Extract portals from MaplePortal
// Extract footholds from MapleFootholdTree

Implementation Guide

Create Export Utility Class

package tools;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import server.*;
import server.life.*;
import server.maps.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;

public class WZExporter {

    private static final Gson gson = new GsonBuilder()
        .setPrettyPrinting()
        .create();

    private static final String OUTPUT_DIR = "../odinsea-elixir/priv/data/";

    public static void main(String[] args) {
        System.out.println("Starting WZ data export...");

        exportItemStrings();
        exportItems();
        exportEquips();
        exportMonsters();
        exportNPCs();
        exportMaps();

        System.out.println("Export complete!");
    }

    private static void exportItemStrings() {
        // Implementation here
    }

    private static void exportItems() {
        // Implementation here
    }

    // ... other export methods

    private static void writeJson(String filename, Object data) {
        try {
            Path path = Paths.get(OUTPUT_DIR + filename);
            Files.createDirectories(path.getParent());

            String json = gson.toJson(data);
            Files.write(path, json.getBytes());

            System.out.println("Wrote: " + filename);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Run Export

  1. Add WZExporter.java to Java project
  2. Ensure all providers are loaded (items, maps, life)
  3. Run: java tools.WZExporter
  4. Check output in ../odinsea-elixir/priv/data/
  5. Verify JSON files are valid
  6. Test in Elixir by reloading data providers

Data Validation

After export, validate data:

# In Elixir IEx console
Odinsea.Game.ItemInfo.reload()
Odinsea.Game.MapFactory.reload()
Odinsea.Game.LifeFactory.reload()

# Check counts
:ets.info(:odinsea_item_cache, :size)      # Should be 10000+
:ets.info(:odinsea_map_templates, :size)   # Should be 1000+
:ets.info(:odinsea_monster_stats, :size)   # Should be 5000+

# Test lookups
Odinsea.Game.ItemInfo.get_name(2000000)    # "Red Potion"
Odinsea.Game.MapFactory.get_map_name(100000000)  # "Henesys"
Odinsea.Game.LifeFactory.get_monster_name(100100)  # "Blue Snail"

Performance Considerations

  • Export can take 5-10 minutes for full WZ data
  • JSON files will be 50-100MB total
  • Consider compressing (gzip) for distribution
  • ETS loading takes <1 second with JSON
  • Memory usage: ~100MB for all cached data

Incremental Export

For development, export subsets:

// Export only common maps
if (mapId < 110000000 && mapId >= 100000000) {
    exportMap(mapId);
}

// Export only beginner monsters
if (mobId < 9999999 && level <= 30) {
    exportMonster(mobId);
}

Troubleshooting

Problem: JSON parsing fails in Elixir

  • Check JSON syntax with jq or online validator
  • Ensure UTF-8 encoding
  • Check for special characters in names

Problem: Missing data in exports

  • Verify Java providers are fully initialized
  • Check for null values
  • Add default values in Java export code

Problem: Export crashes or hangs

  • Add try/catch around each item
  • Log progress every 100 items
  • Export in smaller batches

Future Improvements

  1. Incremental Updates

    • Track WZ file changes
    • Only export modified data
    • Generate diff files
  2. Validation

    • Schema validation
    • Referential integrity checks
    • Detect missing required fields
  3. Compression

    • GZIP JSON files
    • Binary format (MessagePack)
    • Reduce file sizes 80%
  4. Automation

    • CI/CD integration
    • Auto-export on WZ updates
    • Version tracking

Next Step: Create WZExporter.java in Java project and run export!