kimi gone wild
This commit is contained in:
397
WZ_EXPORT_GUIDE.md
Normal file
397
WZ_EXPORT_GUIDE.md
Normal file
@@ -0,0 +1,397 @@
|
||||
# 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`)
|
||||
|
||||
```json
|
||||
{
|
||||
"2000000": "Red Potion",
|
||||
"2000001": "Orange Potion",
|
||||
"1002000": "Blue Bandana",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Java Source:**
|
||||
```java
|
||||
// Use MapleItemInformationProvider
|
||||
// Iterate over all items, extract names from String.wz
|
||||
```
|
||||
|
||||
### 2. Items (`priv/data/items.json`)
|
||||
|
||||
```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:**
|
||||
```java
|
||||
// MapleItemInformationProvider.getAllItems()
|
||||
// Convert ItemInformation to JSON
|
||||
```
|
||||
|
||||
### 3. Equipment (`priv/data/equips.json`)
|
||||
|
||||
```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:**
|
||||
```java
|
||||
// MapleItemInformationProvider.getAllItems()
|
||||
// Filter by MapleInventoryType.EQUIP
|
||||
// Extract equip stats
|
||||
```
|
||||
|
||||
### 4. Monsters (`priv/data/monsters.json`)
|
||||
|
||||
```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:**
|
||||
```java
|
||||
// MapleLifeFactory.getAllMonster()
|
||||
// Convert MapleMonsterStats to JSON
|
||||
```
|
||||
|
||||
### 5. NPCs (`priv/data/npcs.json`)
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"npc_id": 1012000,
|
||||
"name": "Athena Pierce",
|
||||
"has_shop": false,
|
||||
"shop_id": null,
|
||||
"script": null
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
**Java Source:**
|
||||
```java
|
||||
// MapleLifeFactory.getAllNPC()
|
||||
// MapleNPC.npcShopIDs for shop data
|
||||
```
|
||||
|
||||
### 6. Maps (`priv/data/maps.json`)
|
||||
|
||||
```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:**
|
||||
```java
|
||||
// MapleMapFactory.loadAllFieldTemplates()
|
||||
// FieldTemplate contains all data
|
||||
// Extract portals from MaplePortal
|
||||
// Extract footholds from MapleFootholdTree
|
||||
```
|
||||
|
||||
## Implementation Guide
|
||||
|
||||
### Create Export Utility Class
|
||||
|
||||
```java
|
||||
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:
|
||||
|
||||
```elixir
|
||||
# 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:
|
||||
|
||||
```java
|
||||
// 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!
|
||||
Reference in New Issue
Block a user