mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-28 02:23:37 +01:00
feat(quests): support JSON quest files alongside .bin
Adds human-readable JSON as an alternative quest format for bin/quests/. The server tries .bin first (full backward compatibility), then falls back to .json and compiles it to the MHF binary wire format on the fly. JSON quests cover all documented fields: text (UTF-8 → Shift-JIS), objectives (all 12 types), monster spawns, reward tables, supply box, stages, rank requirements, variant flags, and forced equipment. Also adds ParseQuestBinary for the reverse direction, enabling tools to round-trip quests and verify that JSON-compiled output is bit-for-bit identical to a hand-authored .bin for the same quest data. 49 tests: compiler, parser, 13 round-trip scenarios, and golden byte- level assertions covering every section of the binary layout.
This commit is contained in:
@@ -126,9 +126,9 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt.Filename = seasonConversion(s, pkt.Filename)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename)))
|
||||
data, err := loadQuestBinary(s, pkt.Filename)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to open quest file", zap.String("binPath", s.server.erupeConfig.BinPath), zap.String("filename", pkt.Filename))
|
||||
s.logger.Error("Failed to open quest file", zap.String("binPath", s.server.erupeConfig.BinPath), zap.String("filename", pkt.Filename), zap.Error(err))
|
||||
doAckBufFail(s, pkt.AckHandle, nil)
|
||||
return
|
||||
}
|
||||
@@ -140,10 +140,34 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
func questFileExists(s *Session, filename string) bool {
|
||||
_, err := os.Stat(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", filename)))
|
||||
base := filepath.Join(s.server.erupeConfig.BinPath, "quests", filename)
|
||||
if _, err := os.Stat(base + ".bin"); err == nil {
|
||||
return true
|
||||
}
|
||||
_, err := os.Stat(base + ".json")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// loadQuestBinary loads a quest file by name, trying .bin first then .json.
|
||||
// For .json files it compiles the JSON to the MHF binary wire format.
|
||||
func loadQuestBinary(s *Session, filename string) ([]byte, error) {
|
||||
base := filepath.Join(s.server.erupeConfig.BinPath, "quests", filename)
|
||||
|
||||
if data, err := os.ReadFile(base + ".bin"); err == nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
jsonData, err := os.ReadFile(base + ".json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compiled, err := CompileQuestJSON(jsonData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("compile quest JSON %s: %w", filename, err)
|
||||
}
|
||||
return compiled, nil
|
||||
}
|
||||
|
||||
func seasonConversion(s *Session, questFile string) string {
|
||||
// Try the seasonal override file (e.g., 00001d2 for season 2)
|
||||
filename := fmt.Sprintf("%s%d", questFile[:6], s.server.Season())
|
||||
|
||||
Reference in New Issue
Block a user