#include "int_storage.hpp" // int_storage.cpp - Internal storage handling. // // Copyright © ????-2004 Athena Dev Teams // Copyright © 2004-2011 The Mana World Development Team // Copyright © 2011-2014 Ben Longbons // // This file is part of The Mana World (Athena server) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "../strings/mstring.hpp" #include "../strings/astring.hpp" #include "../strings/xstring.hpp" #include "../strings/literal.hpp" #include "../generic/db.hpp" #include "../io/cxxstdio.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" #include "../io/write.hpp" #include "../net/socket.hpp" #include "../mmo/extract.hpp" #include "../mmo/mmo.hpp" #include "../poison.hpp" // ファイル名のデフォルト // inter_config_read()で再設定される AString storage_txt = "save/storage.txt"_s; static Map storage_db; // 倉庫データを文字列に変換 static AString storage_tostr(struct storage *p) { MString str; str += STRPRINTF( "%d,%d\t"_fmt, p->account_id, p->storage_amount); int f = 0; for (int i = 0; i < MAX_STORAGE; i++) if (p->storage_[i].nameid && p->storage_[i].amount) { str += STRPRINTF( "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d "_fmt, 0 /*id*/, p->storage_[i].nameid, p->storage_[i].amount, p->storage_[i].equip, 0 /*identify*/, 0 /*refine*/, 0 /*attribute*/, 0 /*card[0]*/, 0 /*card[1]*/, 0 /*card[2]*/, 0 /*card[3]*/); // shouldn't that include 'broken' also? Oh, well ... f++; } str += '\t'; if (!f) return AString(); return AString(str); } // 文字列を倉庫データに変換 static bool extract(XString str, struct storage *p) { std::vector storage_items; if (!extract(str, record<'\t'>( record<','>( &p->account_id, &p->storage_amount), vrec<' '>(&storage_items)))) return false; if (!p->account_id) return false; if (storage_items.size() > MAX_STORAGE) return false; std::copy(storage_items.begin(), storage_items.end(), p->storage_.begin()); if (p->storage_amount != storage_items.size()) PRINTF("WARNING: storage desync for %d\n"_fmt, p->account_id); return true; } // アカウントから倉庫データインデックスを得る(新規倉庫追加可能) struct storage *account2storage(AccountId account_id) { struct storage *s = storage_db.search(account_id); if (s == NULL) { s = storage_db.init(account_id); s->account_id = account_id; } return s; } //--------------------------------------------------------- // 倉庫データを読み込む void inter_storage_init(void) { int c = 0; io::ReadFile in(storage_txt); if (!in.is_open()) { PRINTF("cant't read : %s\n"_fmt, storage_txt); return; } AString line; while (in.getline(line)) { struct storage s {}; if (extract(line, &s)) { storage_db.insert(s.account_id, s); } else { PRINTF("int_storage: broken data [%s] line %d\n"_fmt, storage_txt, c); } c++; } } static void inter_storage_save_sub(struct storage *data, io::WriteFile& fp) { AString line = storage_tostr(data); if (line) fp.put_line(line); } //--------------------------------------------------------- // 倉庫データを書き込む int inter_storage_save(void) { io::WriteLock fp(storage_txt); if (!fp.is_open()) { PRINTF("int_storage: cant write [%s] !!! data is lost !!!\n"_fmt, storage_txt); return 1; } for (auto& pair : storage_db) inter_storage_save_sub(&pair.second, fp); return 0; } // 倉庫データ削除 void inter_storage_delete(AccountId account_id) { storage_db.erase(account_id); } //--------------------------------------------------------- // map serverへの通信 // 倉庫データの送信 static void mapif_load_storage(Session *ss, AccountId account_id) { struct storage *st = account2storage(account_id); WFIFOW(ss, 0) = 0x3810; WFIFOW(ss, 2) = sizeof(struct storage) + 8; WFIFOL(ss, 4) = unwrap(account_id); WFIFO_STRUCT(ss, 8, *st); WFIFOSET(ss, WFIFOW(ss, 2)); } // 倉庫データ保存完了送信 static void mapif_save_storage_ack(Session *ss, AccountId account_id) { WFIFOW(ss, 0) = 0x3811; WFIFOL(ss, 2) = unwrap(account_id); WFIFOB(ss, 6) = 0; WFIFOSET(ss, 7); } //--------------------------------------------------------- // map serverからの通信 // 倉庫データ要求受信 static void mapif_parse_LoadStorage(Session *ss) { AccountId account_id = wrap(RFIFOL(ss, 2)); mapif_load_storage(ss, account_id); } // 倉庫データ受信&保存 static void mapif_parse_SaveStorage(Session *ss) { struct storage *st; AccountId account_id = wrap(RFIFOL(ss, 4)); int len = RFIFOW(ss, 2); if (sizeof(struct storage) != len - 8) { PRINTF("inter storage: data size error %zu %d\n"_fmt, sizeof(struct storage), len - 8); } else { st = account2storage(account_id); RFIFO_STRUCT(ss, 8, *st); mapif_save_storage_ack(ss, account_id); } } // map server からの通信 // ・1パケットのみ解析すること // ・パケット長データはinter.cにセットしておくこと // ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない // ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない int inter_storage_parse_frommap(Session *ms) { switch (RFIFOW(ms, 0)) { case 0x3010: mapif_parse_LoadStorage(ms); break; case 0x3011: mapif_parse_SaveStorage(ms); break; default: return 0; } return 1; }