split Episode3::DataIndex into multiple structures
This commit is contained in:
@@ -133,7 +133,7 @@ uint16_t AssistServer::card_id_for_card_ref(uint16_t card_ref) const {
|
||||
return this->server()->card_id_for_card_ref(card_ref);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> AssistServer::definition_for_card_id(
|
||||
shared_ptr<const CardIndex::CardEntry> AssistServer::definition_for_card_id(
|
||||
uint16_t card_id) const {
|
||||
return this->server()->definition_for_card_id(card_id);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
#include "DeckState.hh"
|
||||
#include "PlayerState.hh"
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
std::shared_ptr<const Server> server() const;
|
||||
|
||||
uint16_t card_id_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
|
||||
uint32_t compute_num_assist_effects_for_client(uint16_t client_id);
|
||||
uint32_t compute_num_assist_effects_for_team(uint32_t team_id);
|
||||
@@ -40,11 +40,11 @@ private:
|
||||
|
||||
public:
|
||||
parray<AssistEffect, 4> assist_effects;
|
||||
std::shared_ptr<const DataIndex::CardEntry> assist_card_defs[4];
|
||||
std::shared_ptr<const CardIndex::CardEntry> assist_card_defs[4];
|
||||
uint32_t num_assist_cards_set;
|
||||
parray<uint8_t, 4> client_ids_with_assists;
|
||||
parray<AssistEffect, 4> active_assist_effects;
|
||||
std::shared_ptr<const DataIndex::CardEntry> active_assist_card_defs[4];
|
||||
std::shared_ptr<const CardIndex::CardEntry> active_assist_card_defs[4];
|
||||
uint32_t num_active_assists;
|
||||
std::shared_ptr<HandAndEquipState> hand_and_equip_states[4];
|
||||
std::shared_ptr<parray<CardShortStatus, 0x10>> card_short_statuses[4];
|
||||
|
||||
@@ -507,7 +507,7 @@ bool Card::get_attack_condition_value(
|
||||
cond_type, card_ref, def_effect_index, value, out_value);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> Card::get_definition() const {
|
||||
shared_ptr<const CardIndex::CardEntry> Card::get_definition() const {
|
||||
return this->def_entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "../CommandFormats.hh"
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
uint8_t def_effect_index,
|
||||
uint16_t value,
|
||||
uint16_t* out_value) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> get_definition() const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> get_definition() const;
|
||||
uint16_t get_card_ref() const;
|
||||
uint8_t get_client_id() const;
|
||||
uint8_t get_current_hp() const;
|
||||
@@ -99,12 +99,12 @@ private:
|
||||
public:
|
||||
int16_t max_hp;
|
||||
int16_t current_hp;
|
||||
std::shared_ptr<const DataIndex::CardEntry> def_entry;
|
||||
std::shared_ptr<const CardIndex::CardEntry> def_entry;
|
||||
uint8_t client_id;
|
||||
uint16_t card_id;
|
||||
uint16_t card_ref;
|
||||
uint16_t sc_card_ref;
|
||||
std::shared_ptr<const DataIndex::CardEntry> sc_def_entry;
|
||||
std::shared_ptr<const CardIndex::CardEntry> sc_def_entry;
|
||||
CardType sc_card_type;
|
||||
uint8_t team_id;
|
||||
uint32_t card_flags;
|
||||
|
||||
+14
-14
@@ -816,7 +816,7 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
|
||||
// the Gifoie card's ID (00D9) for compute_effective_range.
|
||||
// TODO: We should fix this so it doesn't rely on a fixed card definition.
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, 0x00D9, target_card_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, 0x00D9, target_card_loc, this->server()->base()->map_and_rules1);
|
||||
auto card_refs_in_parry_range = target_ps->get_all_cards_within_range(
|
||||
range, target_card_loc, 0xFF);
|
||||
|
||||
@@ -2526,7 +2526,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
if (ce && ps) {
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, ce->def.card_id, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
add_card_refs(ps->get_card_refs_within_range_from_all_players(range, card1_loc, CardType::ITEM));
|
||||
}
|
||||
}
|
||||
@@ -2566,7 +2566,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
if (ce && ps) {
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, ce->def.card_id, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
add_card_refs(ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id()));
|
||||
}
|
||||
}
|
||||
@@ -2631,7 +2631,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// should fix this eventually.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2655,7 +2655,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Again with the Gifoie hardcoding...
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2721,7 +2721,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Again with the Gifoie hardcoding...
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2775,7 +2775,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Yet another Gifoie hardcode location :(
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2802,7 +2802,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Sigh. Gifoie again.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2898,7 +2898,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// Slay instead of Gifoie
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x009C, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2927,7 +2927,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Sigh. Gifoie. Sigh.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2965,7 +2965,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: One more Gifoie here.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -3383,7 +3383,7 @@ void CardSpecial::check_for_defense_interference(
|
||||
shared_ptr<Card> target_card,
|
||||
int16_t* inout_unknown_p4) {
|
||||
// Note: This check is not part of the original implementation.
|
||||
if (this->server()->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
if (this->server()->base()->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4126,7 +4126,7 @@ vector<shared_ptr<const Card>> CardSpecial::filter_cards_by_range(
|
||||
// TODO: Remove hardcoded card ID here (Earthquake)
|
||||
uint16_t card_id = this->get_card_id_with_effective_range(card1, 0x00ED, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto card_refs_in_range = ps->get_card_refs_within_range_from_all_players(range, card1_loc, CardType::ITEM);
|
||||
|
||||
for (auto card : cards) {
|
||||
@@ -4342,7 +4342,7 @@ void CardSpecial::unknown_8024A9D8(const ActionState& pa, uint16_t action_card_r
|
||||
|
||||
void CardSpecial::check_for_attack_interference(shared_ptr<Card> unknown_p2) {
|
||||
// Note: This check is not part of the original implementation.
|
||||
if (this->server()->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
if (this->server()->base()->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -1056,6 +1056,33 @@ Card: %04" PRIX32 " \"%s\"\n\
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerConfig::decrypt() {
|
||||
if (!this->is_encrypted) {
|
||||
return;
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
this->basis);
|
||||
this->is_encrypted = 0;
|
||||
this->basis = 0;
|
||||
}
|
||||
|
||||
void PlayerConfig::encrypt(uint8_t basis) {
|
||||
if (this->is_encrypted) {
|
||||
if (this->basis == basis) {
|
||||
return;
|
||||
}
|
||||
this->decrypt();
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
basis);
|
||||
this->is_encrypted = 1;
|
||||
this->basis = basis;
|
||||
}
|
||||
|
||||
HPType hp_type_for_name(const char* name) {
|
||||
if (!strcmp(name, "DEFEAT_PLAYER")) {
|
||||
return HPType::DEFEAT_PLAYER;
|
||||
@@ -1351,7 +1378,7 @@ void StateFlags::clear_FF() {
|
||||
this->client_sc_card_types.clear(CardType::INVALID_FF);
|
||||
}
|
||||
|
||||
string MapDefinition::str(const DataIndex* data_index) const {
|
||||
string MapDefinition::str(const CardIndex* card_index) const {
|
||||
deque<string> lines;
|
||||
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
|
||||
for (size_t y = 0; y < 0x10; y++) {
|
||||
@@ -1456,10 +1483,10 @@ string MapDefinition::str(const DataIndex* data_index) const {
|
||||
lines.emplace_back(" name: " + string(this->npc_decks[z].name));
|
||||
for (size_t w = 0; w < 0x20; w++) {
|
||||
uint16_t card_id = this->npc_decks[z].card_ids[w];
|
||||
shared_ptr<const DataIndex::CardEntry> entry;
|
||||
if (data_index) {
|
||||
shared_ptr<const CardIndex::CardEntry> entry;
|
||||
if (card_index) {
|
||||
try {
|
||||
entry = data_index->definition_for_card_id(card_id);
|
||||
entry = card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
@@ -1496,10 +1523,10 @@ string MapDefinition::str(const DataIndex* data_index) const {
|
||||
}
|
||||
for (size_t z = 0; z < 0x10; z++) {
|
||||
uint16_t card_id = this->reward_card_ids[z];
|
||||
shared_ptr<const DataIndex::CardEntry> entry;
|
||||
if (data_index) {
|
||||
shared_ptr<const CardIndex::CardEntry> entry;
|
||||
if (card_index) {
|
||||
try {
|
||||
entry = data_index->definition_for_card_id(card_id);
|
||||
entry = card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
@@ -1671,14 +1698,12 @@ bool Rules::check_and_reset_invalid_fields() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
: behavior_flags(behavior_flags) {
|
||||
|
||||
CardIndex::CardIndex(const string& filename, const string& decompressed_filename, const string& text_filename) {
|
||||
unordered_map<uint32_t, vector<string>> card_tags;
|
||||
unordered_map<uint32_t, string> card_text;
|
||||
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
|
||||
if (!text_filename.empty()) {
|
||||
try {
|
||||
string data = prs_decompress(load_file(directory + "/card-text.mnr"));
|
||||
string data = prs_decompress(load_file(text_filename));
|
||||
StringReader r(data);
|
||||
|
||||
while (!r.eof()) {
|
||||
@@ -1769,20 +1794,20 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
|
||||
try {
|
||||
string decompressed_data;
|
||||
if (isfile(directory + "/card-definitions.mnrd")) {
|
||||
this->mtime_for_card_definitions = stat(directory + "/card-definitions.mnrd").st_mtime;
|
||||
decompressed_data = load_file(directory + "/card-definitions.mnrd");
|
||||
this->mtime_for_card_definitions = stat(filename).st_mtime;
|
||||
try {
|
||||
decompressed_data = load_file(decompressed_filename);
|
||||
this->compressed_card_definitions.clear();
|
||||
} else {
|
||||
this->mtime_for_card_definitions = stat(directory + "/card-definitions.mnr").st_mtime;
|
||||
this->compressed_card_definitions = load_file(directory + "/card-definitions.mnr");
|
||||
} catch (const cannot_open_file&) {
|
||||
this->compressed_card_definitions = load_file(filename);
|
||||
decompressed_data = prs_decompress(this->compressed_card_definitions);
|
||||
}
|
||||
if (decompressed_data.size() > 0x36EC0) {
|
||||
throw runtime_error("decompressed card list data is too long");
|
||||
}
|
||||
|
||||
// There's a footer after the card definitions, but we ignore it
|
||||
// There's a footer after the card definitions (it's a standard-format REL
|
||||
// file), but we ignore it
|
||||
if (decompressed_data.size() % sizeof(CardDefinition) != sizeof(CardDefinitionsFooter)) {
|
||||
throw runtime_error(string_printf(
|
||||
"decompressed card update file size %zX is not aligned with card definition size %zX (%zX extra bytes)",
|
||||
@@ -1792,10 +1817,9 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
size_t max_cards = decompressed_data.size() / sizeof(CardDefinition);
|
||||
for (size_t x = 0; x < max_cards; x++) {
|
||||
// The last card entry has the build date and some other metadata (and
|
||||
// isn't a real card, obviously), so skip it. Seems like the card ID is
|
||||
// always a large number that won't fit in a uint16_t, so we use that to
|
||||
// determine if the entry is a real card or not.
|
||||
if (defs[x].card_id & 0xFFFF0000) {
|
||||
// isn't a real card, obviously), so skip it. The game detects this by
|
||||
// checking for a negative value in type, which we also do here.
|
||||
if (static_cast<int8_t>(defs[x].type) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1816,7 +1840,7 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
entry->def.mv.decode_code();
|
||||
entry->def.decode_range();
|
||||
|
||||
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
|
||||
if (!text_filename.empty()) {
|
||||
try {
|
||||
entry->text = std::move(card_text.at(defs[x].card_id));
|
||||
} catch (const out_of_range&) {
|
||||
@@ -1836,6 +1860,25 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
"Compressed card definitions (%zu bytes -> %zu bytes) in %" PRIu64 "ms",
|
||||
decompressed_data.size(), this->compressed_card_definitions.size(), diff);
|
||||
}
|
||||
|
||||
if (this->compressed_card_definitions.size() > 0x7BF8) {
|
||||
// Try to reduce the compressed size by clearing out text
|
||||
static_game_data_log.info("Compressed card list data is too long; removing text");
|
||||
for (size_t x = 0; x < max_cards; x++) {
|
||||
if (static_cast<int8_t>(defs[x].type) < 0) {
|
||||
continue;
|
||||
}
|
||||
defs[x].jp_name.clear();
|
||||
}
|
||||
uint64_t start = now();
|
||||
this->compressed_card_definitions = prs_compress(decompressed_data);
|
||||
uint64_t diff = now() - start;
|
||||
static_game_data_log.info(
|
||||
"Compressed card definitions (%zu bytes -> %zu bytes) in %" PRIu64 "ms",
|
||||
decompressed_data.size(), this->compressed_card_definitions.size(), diff);
|
||||
print_data(stderr, this->compressed_card_definitions);
|
||||
}
|
||||
|
||||
if (this->compressed_card_definitions.size() > 0x7BF8) {
|
||||
throw runtime_error("compressed card list data is too long");
|
||||
}
|
||||
@@ -1844,7 +1887,36 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to load Episode 3 card update: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
const string& CardIndex::get_compressed_definitions() const {
|
||||
if (this->compressed_card_definitions.empty()) {
|
||||
throw runtime_error("card definitions are not available");
|
||||
}
|
||||
return this->compressed_card_definitions;
|
||||
}
|
||||
|
||||
shared_ptr<const CardIndex::CardEntry> CardIndex::definition_for_id(uint32_t id) const {
|
||||
return this->card_definitions.at(id);
|
||||
}
|
||||
|
||||
shared_ptr<const CardIndex::CardEntry> CardIndex::definition_for_name(const string& name) const {
|
||||
return this->card_definitions_by_name.at(name);
|
||||
}
|
||||
|
||||
set<uint32_t> CardIndex::all_ids() const {
|
||||
set<uint32_t> ret;
|
||||
for (const auto& it : this->card_definitions) {
|
||||
ret.emplace(it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t CardIndex::definitions_mtime() const {
|
||||
return this->mtime_for_card_definitions;
|
||||
}
|
||||
|
||||
MapIndex::MapIndex(const string& directory) {
|
||||
auto add_maps_from_dir = [&](const string& dir, bool is_quest) -> void {
|
||||
for (const auto& filename : list_directory(dir)) {
|
||||
try {
|
||||
@@ -1879,32 +1951,13 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
};
|
||||
add_maps_from_dir(directory + "/maps-free", false);
|
||||
add_maps_from_dir(directory + "/maps-quest", true);
|
||||
|
||||
try {
|
||||
auto json = JSONObject::parse(load_file(directory + "/com-decks.json"));
|
||||
for (const auto& def_json : json->as_list()) {
|
||||
auto& def = this->com_decks.emplace_back(new COMDeckDefinition());
|
||||
def->index = this->com_decks.size() - 1;
|
||||
def->player_name = def_json->at(0)->as_string();
|
||||
def->deck_name = def_json->at(1)->as_string();
|
||||
auto card_ids_json = def_json->at(2)->as_list();
|
||||
for (size_t z = 0; z < 0x1F; z++) {
|
||||
def->card_ids[z] = card_ids_json.at(z)->as_int();
|
||||
}
|
||||
if (!this->com_decks_by_name.emplace(def->deck_name, def).second) {
|
||||
throw runtime_error("duplicate COM deck name: " + def->deck_name);
|
||||
}
|
||||
}
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to load Episode 3 COM decks: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
DataIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest)
|
||||
MapIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest)
|
||||
: map(map),
|
||||
is_quest(is_quest) {}
|
||||
|
||||
DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
|
||||
MapIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
|
||||
: is_quest(is_quest),
|
||||
compressed_data(compressed) {
|
||||
string decompressed = prs_decompress(this->compressed_data);
|
||||
@@ -1916,43 +1969,14 @@ DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
|
||||
this->map = *reinterpret_cast<const MapDefinition*>(decompressed.data());
|
||||
}
|
||||
|
||||
string DataIndex::MapEntry::compressed() const {
|
||||
string MapIndex::MapEntry::compressed() const {
|
||||
if (this->compressed_data.empty()) {
|
||||
this->compressed_data = prs_compress(&this->map, sizeof(this->map));
|
||||
}
|
||||
return this->compressed_data;
|
||||
}
|
||||
|
||||
const string& DataIndex::get_compressed_card_definitions() const {
|
||||
if (this->compressed_card_definitions.empty()) {
|
||||
throw runtime_error("card definitions are not available");
|
||||
}
|
||||
return this->compressed_card_definitions;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> DataIndex::definition_for_card_id(
|
||||
uint32_t id) const {
|
||||
return this->card_definitions.at(id);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> DataIndex::definition_for_card_name(
|
||||
const string& name) const {
|
||||
return this->card_definitions_by_name.at(name);
|
||||
}
|
||||
|
||||
set<uint32_t> DataIndex::all_card_ids() const {
|
||||
set<uint32_t> ret;
|
||||
for (const auto& it : this->card_definitions) {
|
||||
ret.emplace(it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t DataIndex::card_definitions_mtime() const {
|
||||
return this->mtime_for_card_definitions;
|
||||
}
|
||||
|
||||
const string& DataIndex::get_compressed_map_list() const {
|
||||
const string& MapIndex::get_compressed_list() const {
|
||||
if (this->compressed_map_list.empty()) {
|
||||
StringWriter entries_w;
|
||||
StringWriter strings_w;
|
||||
@@ -2012,16 +2036,15 @@ const string& DataIndex::get_compressed_map_list() const {
|
||||
return this->compressed_map_list;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::MapEntry> DataIndex::definition_for_map_number(uint32_t id) const {
|
||||
shared_ptr<const MapIndex::MapEntry> MapIndex::definition_for_number(uint32_t id) const {
|
||||
return this->maps.at(id);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::MapEntry> DataIndex::definition_for_map_name(
|
||||
const string& name) const {
|
||||
shared_ptr<const MapIndex::MapEntry> MapIndex::definition_for_name(const string& name) const {
|
||||
return this->maps_by_name.at(name);
|
||||
}
|
||||
|
||||
set<uint32_t> DataIndex::all_map_ids() const {
|
||||
set<uint32_t> MapIndex::all_numbers() const {
|
||||
set<uint32_t> ret;
|
||||
for (const auto& it : this->maps) {
|
||||
ret.emplace(it.first);
|
||||
@@ -2029,47 +2052,41 @@ set<uint32_t> DataIndex::all_map_ids() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t DataIndex::num_com_decks() const {
|
||||
return this->com_decks.size();
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> DataIndex::com_deck(size_t which) const {
|
||||
return this->com_decks.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> DataIndex::com_deck(const string& which) const {
|
||||
return this->com_decks_by_name.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> DataIndex::random_com_deck() const {
|
||||
return this->com_decks[random_object<size_t>() % this->com_decks.size()];
|
||||
}
|
||||
|
||||
void PlayerConfig::decrypt() {
|
||||
if (!this->is_encrypted) {
|
||||
return;
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
this->basis);
|
||||
this->is_encrypted = 0;
|
||||
this->basis = 0;
|
||||
}
|
||||
|
||||
void PlayerConfig::encrypt(uint8_t basis) {
|
||||
if (this->is_encrypted) {
|
||||
if (this->basis == basis) {
|
||||
return;
|
||||
COMDeckIndex::COMDeckIndex(const string& filename) {
|
||||
try {
|
||||
auto json = JSONObject::parse(load_file(filename));
|
||||
for (const auto& def_json : json->as_list()) {
|
||||
auto& def = this->decks.emplace_back(new COMDeckDefinition());
|
||||
def->index = this->decks.size() - 1;
|
||||
def->player_name = def_json->at(0)->as_string();
|
||||
def->deck_name = def_json->at(1)->as_string();
|
||||
auto card_ids_json = def_json->at(2)->as_list();
|
||||
for (size_t z = 0; z < 0x1F; z++) {
|
||||
def->card_ids[z] = card_ids_json.at(z)->as_int();
|
||||
}
|
||||
if (!this->decks_by_name.emplace(def->deck_name, def).second) {
|
||||
throw runtime_error("duplicate COM deck name: " + def->deck_name);
|
||||
}
|
||||
}
|
||||
this->decrypt();
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to load Episode 3 COM decks: %s", e.what());
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
basis);
|
||||
this->is_encrypted = 1;
|
||||
this->basis = basis;
|
||||
}
|
||||
|
||||
size_t COMDeckIndex::num_decks() const {
|
||||
return this->decks.size();
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> COMDeckIndex::deck_for_index(size_t which) const {
|
||||
return this->decks.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> COMDeckIndex::deck_for_name(const string& which) const {
|
||||
return this->decks_by_name.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> COMDeckIndex::random_deck() const {
|
||||
return this->decks[random_object<size_t>() % this->decks.size()];
|
||||
}
|
||||
|
||||
} // namespace Episode3
|
||||
@@ -15,21 +15,22 @@
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
// The comment in Server.hh does not apply to this file (and DataIndex.cc).
|
||||
// The comment in Server.hh does not apply to this file (and DataIndexes.cc).
|
||||
// Except for the Location structure, these structures and functions are not
|
||||
// based on Sega's original implementation.
|
||||
|
||||
class DataIndex;
|
||||
class CardIndex;
|
||||
class MapIndex;
|
||||
class COMDeckIndex;
|
||||
|
||||
const char* name_for_link_color(uint8_t color);
|
||||
|
||||
enum BehaviorFlag {
|
||||
enum BehaviorFlag : uint32_t {
|
||||
SKIP_DECK_VERIFY = 0x00000001,
|
||||
IGNORE_CARD_COUNTS = 0x00000002,
|
||||
SKIP_D1_D2_REPLACE = 0x00000004,
|
||||
DISABLE_TIME_LIMITS = 0x00000008,
|
||||
ENABLE_STATUS_MESSAGES = 0x00000010,
|
||||
LOAD_CARD_TEXT = 0x00000020,
|
||||
ENABLE_RECORDING = 0x00000040,
|
||||
DISABLE_MASKING = 0x00000080,
|
||||
DISABLE_INTERFERENCE = 0x00000100,
|
||||
@@ -952,10 +953,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
uint8_t deck_type;
|
||||
} __attribute__((packed));
|
||||
/* 5A10 */ parray<EntryState, 4> entry_states;
|
||||
|
||||
/* 5A18 */
|
||||
|
||||
std::string str(const DataIndex* data_index = nullptr) const;
|
||||
std::string str(const CardIndex* card_index = nullptr) const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct COMDeckDefinition {
|
||||
@@ -965,9 +965,9 @@ struct COMDeckDefinition {
|
||||
parray<le_uint16_t, 0x1F> card_ids;
|
||||
};
|
||||
|
||||
class DataIndex {
|
||||
class CardIndex {
|
||||
public:
|
||||
DataIndex(const std::string& directory, uint32_t behavior_flags);
|
||||
CardIndex(const std::string& filename, const std::string& decompressed_filename, const std::string& text_filename = "");
|
||||
|
||||
struct CardEntry {
|
||||
CardDefinition def;
|
||||
@@ -975,6 +975,23 @@ public:
|
||||
std::vector<std::string> debug_tags; // Empty unless debug == true
|
||||
};
|
||||
|
||||
const std::string& get_compressed_definitions() const;
|
||||
std::shared_ptr<const CardEntry> definition_for_id(uint32_t id) const;
|
||||
std::shared_ptr<const CardEntry> definition_for_name(const std::string& name) const;
|
||||
std::set<uint32_t> all_ids() const;
|
||||
uint64_t definitions_mtime() const;
|
||||
|
||||
private:
|
||||
std::string compressed_card_definitions;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<CardEntry>> card_definitions;
|
||||
std::unordered_map<std::string, std::shared_ptr<CardEntry>> card_definitions_by_name;
|
||||
uint64_t mtime_for_card_definitions;
|
||||
};
|
||||
|
||||
class MapIndex {
|
||||
public:
|
||||
MapIndex(const std::string& directory);
|
||||
|
||||
class MapEntry {
|
||||
public:
|
||||
MapDefinition map;
|
||||
@@ -989,42 +1006,33 @@ public:
|
||||
mutable std::string compressed_data;
|
||||
};
|
||||
|
||||
const std::string& get_compressed_card_definitions() const;
|
||||
std::shared_ptr<const CardEntry> definition_for_card_id(uint32_t id) const;
|
||||
std::shared_ptr<const CardEntry> definition_for_card_name(
|
||||
const std::string& name) const;
|
||||
std::set<uint32_t> all_card_ids() const;
|
||||
uint64_t card_definitions_mtime() const;
|
||||
|
||||
const std::string& get_compressed_map_list() const;
|
||||
std::shared_ptr<const MapEntry> definition_for_map_number(uint32_t id) const;
|
||||
std::shared_ptr<const MapEntry> definition_for_map_name(
|
||||
const std::string& name) const;
|
||||
std::set<uint32_t> all_map_ids() const;
|
||||
|
||||
size_t num_com_decks() const;
|
||||
std::shared_ptr<const COMDeckDefinition> com_deck(size_t which) const;
|
||||
std::shared_ptr<const COMDeckDefinition> com_deck(const std::string& name) const;
|
||||
std::shared_ptr<const COMDeckDefinition> random_com_deck() const;
|
||||
|
||||
const uint32_t behavior_flags;
|
||||
const std::string& get_compressed_list() const;
|
||||
std::shared_ptr<const MapEntry> definition_for_number(uint32_t id) const;
|
||||
std::shared_ptr<const MapEntry> definition_for_name(const std::string& name) const;
|
||||
std::set<uint32_t> all_numbers() const;
|
||||
|
||||
private:
|
||||
std::string compressed_card_definitions;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<CardEntry>> card_definitions;
|
||||
std::unordered_map<std::string, std::shared_ptr<CardEntry>> card_definitions_by_name;
|
||||
uint64_t mtime_for_card_definitions;
|
||||
|
||||
// The compressed map list is generated on demand from the maps map below.
|
||||
// It's marked mutable because the logical consistency of the DataIndex object
|
||||
// It's marked mutable because the logical consistency of the MapIndex object
|
||||
// is not violated from the caller's perspective even if we don't generate the
|
||||
// compressed map list at load time.
|
||||
mutable std::string compressed_map_list;
|
||||
std::map<uint32_t, std::shared_ptr<MapEntry>> maps;
|
||||
std::unordered_map<std::string, std::shared_ptr<MapEntry>> maps_by_name;
|
||||
};
|
||||
|
||||
std::vector<std::shared_ptr<COMDeckDefinition>> com_decks;
|
||||
std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> com_decks_by_name;
|
||||
class COMDeckIndex {
|
||||
public:
|
||||
COMDeckIndex(const std::string& filename);
|
||||
|
||||
size_t num_decks() const;
|
||||
std::shared_ptr<const COMDeckDefinition> deck_for_index(size_t which) const;
|
||||
std::shared_ptr<const COMDeckDefinition> deck_for_name(const std::string& name) const;
|
||||
std::shared_ptr<const COMDeckDefinition> random_deck() const;
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<COMDeckDefinition>> decks;
|
||||
std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> decks_by_name;
|
||||
};
|
||||
|
||||
} // namespace Episode3
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "Card.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
#include "DeckState.hh"
|
||||
#include "PlayerStateSubordinates.hh"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Episode3 {
|
||||
|
||||
void compute_effective_range(
|
||||
parray<uint8_t, 9 * 9>& ret,
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const CardIndex> card_index,
|
||||
uint16_t card_id,
|
||||
const Location& loc,
|
||||
shared_ptr<const MapAndRulesState> map_and_rules) {
|
||||
@@ -19,9 +19,9 @@ void compute_effective_range(
|
||||
// Heavy Fog: one tile directly in front
|
||||
range_def[3] = 0x00000100;
|
||||
} else {
|
||||
shared_ptr<const DataIndex::CardEntry> ce;
|
||||
shared_ptr<const CardIndex::CardEntry> ce;
|
||||
try {
|
||||
ce = data_index->definition_for_card_id(card_id);
|
||||
ce = card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
return;
|
||||
}
|
||||
@@ -126,9 +126,9 @@ void compute_effective_range(
|
||||
}
|
||||
|
||||
bool card_linkage_is_valid(
|
||||
shared_ptr<const DataIndex::CardEntry> right_ce,
|
||||
shared_ptr<const DataIndex::CardEntry> left_ce,
|
||||
shared_ptr<const DataIndex::CardEntry> sc_ce,
|
||||
shared_ptr<const CardIndex::CardEntry> right_ce,
|
||||
shared_ptr<const CardIndex::CardEntry> left_ce,
|
||||
shared_ptr<const CardIndex::CardEntry> sc_ce,
|
||||
bool has_permission_effect) {
|
||||
if (!right_ce) {
|
||||
return false;
|
||||
@@ -1613,7 +1613,7 @@ bool RulerServer::defense_card_matches_any_attack_card_top_color(
|
||||
return false;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
|
||||
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
|
||||
uint16_t card_id = this->card_id_for_card_ref(card_ref);
|
||||
if (card_id == 0xFFFF) {
|
||||
return nullptr;
|
||||
@@ -1972,8 +1972,7 @@ uint16_t RulerServer::get_ally_sc_card_ref(uint16_t card_ref) const {
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> RulerServer::definition_for_card_id(
|
||||
uint32_t card_id) const {
|
||||
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_id(uint32_t card_id) const {
|
||||
return this->server()->definition_for_card_id(card_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "AssistServer.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
#include "DeckState.hh"
|
||||
#include "PlayerState.hh"
|
||||
|
||||
@@ -15,15 +15,15 @@ class Server;
|
||||
|
||||
void compute_effective_range(
|
||||
parray<uint8_t, 9 * 9>& ret,
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const CardIndex> card_index,
|
||||
uint16_t card_id,
|
||||
const Location& loc,
|
||||
std::shared_ptr<const MapAndRulesState> map_and_rules);
|
||||
|
||||
bool card_linkage_is_valid(
|
||||
std::shared_ptr<const DataIndex::CardEntry> right_def,
|
||||
std::shared_ptr<const DataIndex::CardEntry> left_def,
|
||||
std::shared_ptr<const DataIndex::CardEntry> sc_def,
|
||||
std::shared_ptr<const CardIndex::CardEntry> right_def,
|
||||
std::shared_ptr<const CardIndex::CardEntry> left_def,
|
||||
std::shared_ptr<const CardIndex::CardEntry> sc_def,
|
||||
bool has_permission_effect);
|
||||
|
||||
class RulerServer {
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
uint16_t attacker_sc_card_ref) const;
|
||||
bool defense_card_matches_any_attack_card_top_color(
|
||||
const ActionState& pa) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
int32_t error_code_for_client_setting_card(
|
||||
uint8_t client_id,
|
||||
uint16_t card_ref,
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
size_t num_occupied_tiles,
|
||||
size_t num_vacant_tiles) const;
|
||||
uint16_t get_ally_sc_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(
|
||||
uint32_t card_id) const;
|
||||
uint32_t get_card_id_with_effective_range(
|
||||
uint16_t card_ref, uint16_t card_id_override, TargetMode* out_target_mode) const;
|
||||
|
||||
+22
-20
@@ -26,11 +26,15 @@ void ServerBase::PresenceEntry::clear() {
|
||||
|
||||
ServerBase::ServerBase(
|
||||
shared_ptr<Lobby> lobby,
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const CardIndex> card_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
uint32_t behavior_flags,
|
||||
shared_ptr<PSOLFGEncryption> random_crypt,
|
||||
shared_ptr<const DataIndex::MapEntry> map_if_tournament)
|
||||
shared_ptr<const MapIndex::MapEntry> map_if_tournament)
|
||||
: lobby(lobby),
|
||||
data_index(data_index),
|
||||
card_index(card_index),
|
||||
map_index(map_index),
|
||||
behavior_flags(behavior_flags),
|
||||
log(lobby->log.prefix + "[Ep3::Server] "),
|
||||
random_crypt(random_crypt),
|
||||
is_tournament(!!map_if_tournament),
|
||||
@@ -182,7 +186,7 @@ void Server::send(const void* data, size_t size) const {
|
||||
}
|
||||
|
||||
string masked_data;
|
||||
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (!(this->base()->behavior_flags & BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (size >= 8) {
|
||||
masked_data.assign(reinterpret_cast<const char*>(data), size);
|
||||
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
|
||||
@@ -216,13 +220,12 @@ void Server::send_6xB4x46() const {
|
||||
|
||||
G_ServerVersionStrings_GC_Ep3_6xB4x46 cmd46;
|
||||
cmd46.version_signature = VERSION_SIGNATURE;
|
||||
cmd46.date_str1 = format_time(this->base()->data_index->card_definitions_mtime() * 1000000);
|
||||
cmd46.date_str1 = format_time(this->base()->card_index->definitions_mtime() * 1000000);
|
||||
cmd46.date_str2 = string_printf("Lobby/%08" PRIX32, l->lobby_id);
|
||||
this->send(cmd46);
|
||||
}
|
||||
|
||||
string Server::prepare_6xB6x41_map_definition(
|
||||
shared_ptr<const DataIndex::MapEntry> map) {
|
||||
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::MapEntry> map) {
|
||||
const auto& compressed = map->compressed();
|
||||
|
||||
StringWriter w;
|
||||
@@ -260,7 +263,7 @@ void Server::send_commands_for_joining_spectator(Channel& c) const {
|
||||
|
||||
__attribute__((format(printf, 2, 3))) void Server::log_debug(const char* fmt, ...) const {
|
||||
auto l = this->base()->lobby.lock();
|
||||
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
if (l && (this->base()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
this->base()->log.info_v(fmt, va);
|
||||
@@ -270,7 +273,7 @@ __attribute__((format(printf, 2, 3))) void Server::log_debug(const char* fmt, ..
|
||||
|
||||
__attribute__((format(printf, 2, 3))) void Server::send_debug_message_printf(const char* fmt, ...) const {
|
||||
auto l = this->base()->lobby.lock();
|
||||
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
if (l && (this->base()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
std::string buf = string_vprintf(fmt, va);
|
||||
@@ -367,10 +370,9 @@ void Server::draw_phase_before() {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
|
||||
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
|
||||
try {
|
||||
return this->base()->data_index->definition_for_card_id(
|
||||
this->card_id_for_card_ref(card_ref));
|
||||
return this->base()->card_index->definition_for_id(this->card_id_for_card_ref(card_ref));
|
||||
} catch (const out_of_range&) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -581,9 +583,9 @@ void Server::copy_player_states_to_prev_states() {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
|
||||
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
|
||||
try {
|
||||
return this->base()->data_index->definition_for_card_id(card_id);
|
||||
return this->base()->card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1839,7 +1841,7 @@ void Server::handle_6xB3x13_update_map_during_setup(const string& data) {
|
||||
*b->map_and_rules1 = in_cmd.map_and_rules_state;
|
||||
*b->map_and_rules2 = in_cmd.map_and_rules_state;
|
||||
b->overlay_state = in_cmd.overlay_state;
|
||||
if (b->data_index->behavior_flags & BehaviorFlag::DISABLE_TIME_LIMITS) {
|
||||
if (b->behavior_flags & BehaviorFlag::DISABLE_TIME_LIMITS) {
|
||||
b->map_and_rules1->rules.overall_time_limit = 0;
|
||||
b->map_and_rules1->rules.phase_time_limit = 0;
|
||||
b->map_and_rules2->rules.overall_time_limit = 0;
|
||||
@@ -1869,9 +1871,9 @@ void Server::handle_6xB3x14_update_deck_during_setup(const string& data) {
|
||||
}
|
||||
DeckEntry entry = in_cmd.entry;
|
||||
int32_t verify_error = 0;
|
||||
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
|
||||
if (!(this->base()->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
|
||||
// Note: Sega's original implementation doesn't use the card counts here
|
||||
if (this->base()->data_index->behavior_flags & BehaviorFlag::IGNORE_CARD_COUNTS) {
|
||||
if (this->base()->behavior_flags & BehaviorFlag::IGNORE_CARD_COUNTS) {
|
||||
verify_error = this->ruler_server->verify_deck(entry.card_ids);
|
||||
} else {
|
||||
verify_error = this->ruler_server->verify_deck(entry.card_ids,
|
||||
@@ -1881,7 +1883,7 @@ void Server::handle_6xB3x14_update_deck_during_setup(const string& data) {
|
||||
if (verify_error) {
|
||||
throw runtime_error(string_printf("invalid deck: -0x%" PRIX32, verify_error));
|
||||
}
|
||||
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
|
||||
if (!(this->base()->behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
|
||||
this->ruler_server->replace_D1_D2_rarity_cards_with_Attack(entry.card_ids);
|
||||
}
|
||||
*this->base()->deck_entries[in_cmd.client_id] = in_cmd.entry;
|
||||
@@ -2135,7 +2137,7 @@ void Server::handle_6xB3x40_map_list_request(const string& data) {
|
||||
throw runtime_error("lobby is deleted");
|
||||
}
|
||||
|
||||
const auto& list_data = this->base()->data_index->get_compressed_map_list();
|
||||
const auto& list_data = this->base()->map_index->get_compressed_list();
|
||||
|
||||
StringWriter w;
|
||||
uint32_t subcommand_size = (list_data.size() + sizeof(G_MapList_GC_Ep3_6xB6x40) + 3) & (~3);
|
||||
@@ -2164,7 +2166,7 @@ void Server::handle_6xB3x41_map_request(const string& data) {
|
||||
throw runtime_error("lobby is deleted");
|
||||
}
|
||||
|
||||
base->last_chosen_map = base->data_index->definition_for_map_number(cmd.map_number);
|
||||
base->last_chosen_map = base->map_index->definition_for_number(cmd.map_number);
|
||||
auto out_cmd = this->prepare_6xB6x41_map_definition(base->last_chosen_map);
|
||||
send_command(l, 0x6C, 0x00, out_cmd);
|
||||
for (auto watcher_l : l->watcher_lobbies) {
|
||||
|
||||
+13
-9
@@ -19,7 +19,7 @@ namespace Episode3 {
|
||||
|
||||
/**
|
||||
* This implementation of Episode 3 battles (contained in all files in the
|
||||
* src/Episode3 directory, except for DataIndex.hh/cc) is derived from Sega's
|
||||
* src/Episode3 directory, except for DataIndexes.hh/cc) is derived from Sega's
|
||||
* original server implementation, reverse-engineered from the Episode 3 client
|
||||
* executable. The control flow, function breakdown, and structure definitions
|
||||
* in these files map very closely to how their server implementation was
|
||||
@@ -49,7 +49,7 @@ namespace Episode3 {
|
||||
// - - - - - - - DeckState
|
||||
// - - - - - - - HandAndEquipState
|
||||
// - - - - - - - MapAndRulesState / OverlayState
|
||||
// - - - - - - - - Everything within DataIndex
|
||||
// - - - - - - - - Everything within DataIndexes
|
||||
|
||||
class Server;
|
||||
|
||||
@@ -57,9 +57,11 @@ class ServerBase : public std::enable_shared_from_this<ServerBase> {
|
||||
public:
|
||||
ServerBase(
|
||||
std::shared_ptr<Lobby> lobby,
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const CardIndex> card_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
uint32_t behavior_flags,
|
||||
std::shared_ptr<PSOLFGEncryption> random_crypt,
|
||||
std::shared_ptr<const DataIndex::MapEntry> map_if_tournament);
|
||||
std::shared_ptr<const MapIndex::MapEntry> map_if_tournament);
|
||||
void init();
|
||||
void reset();
|
||||
void recreate_server();
|
||||
@@ -73,11 +75,13 @@ public:
|
||||
} __attribute__((packed));
|
||||
|
||||
std::weak_ptr<Lobby> lobby;
|
||||
std::shared_ptr<const DataIndex> data_index;
|
||||
std::shared_ptr<const CardIndex> card_index;
|
||||
std::shared_ptr<const MapIndex> map_index;
|
||||
uint32_t behavior_flags;
|
||||
PrefixedLogger log;
|
||||
std::shared_ptr<PSOLFGEncryption> random_crypt;
|
||||
bool is_tournament;
|
||||
std::shared_ptr<const DataIndex::MapEntry> last_chosen_map;
|
||||
std::shared_ptr<const MapIndex::MapEntry> last_chosen_map;
|
||||
|
||||
std::shared_ptr<MapAndRulesState> map_and_rules1;
|
||||
std::shared_ptr<MapAndRulesState> map_and_rules2;
|
||||
@@ -135,7 +139,7 @@ public:
|
||||
bool advance_battle_phase();
|
||||
void action_phase_after();
|
||||
void draw_phase_before();
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<Card> card_for_set_card_ref(uint16_t card_ref);
|
||||
std::shared_ptr<const Card> card_for_set_card_ref(uint16_t card_ref) const;
|
||||
uint16_t card_id_for_card_ref(uint16_t card_ref) const;
|
||||
@@ -147,7 +151,7 @@ public:
|
||||
void compute_all_map_occupied_bits();
|
||||
void compute_team_dice_boost(uint8_t team_id);
|
||||
void copy_player_states_to_prev_states();
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
void destroy_cards_with_zero_hp();
|
||||
void determine_first_team_turn();
|
||||
void dice_phase_after();
|
||||
@@ -229,7 +233,7 @@ public:
|
||||
G_UpdateDecks_GC_Ep3_6xB4x07 prepare_6xB4x07_decks_update() const;
|
||||
G_SetPlayerNames_GC_Ep3_6xB4x1C prepare_6xB4x1C_names_update() const;
|
||||
static std::string prepare_6xB6x41_map_definition(
|
||||
std::shared_ptr<const DataIndex::MapEntry> map);
|
||||
std::shared_ptr<const MapIndex::MapEntry> map);
|
||||
G_SetTrapTileLocations_GC_Ep3_6xB4x50 prepare_6xB4x50_trap_tile_locations() const;
|
||||
|
||||
std::vector<std::shared_ptr<Card>> const_cast_set_cards_v(
|
||||
|
||||
+21
-20
@@ -280,15 +280,17 @@ shared_ptr<Tournament::Team> Tournament::Match::opponent_team_for_team(
|
||||
}
|
||||
|
||||
Tournament::Tournament(
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
const string& name,
|
||||
shared_ptr<const DataIndex::MapEntry> map,
|
||||
shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2)
|
||||
: log(string_printf("[Tournament/%02hhX] ", number)),
|
||||
data_index(data_index),
|
||||
map_index(map_index),
|
||||
com_deck_index(com_deck_index),
|
||||
number(number),
|
||||
name(name),
|
||||
map(map),
|
||||
@@ -308,11 +310,13 @@ Tournament::Tournament(
|
||||
}
|
||||
|
||||
Tournament::Tournament(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
std::shared_ptr<const JSONObject> json)
|
||||
: log(string_printf("[Tournament/%02hhX] ", number)),
|
||||
data_index(data_index),
|
||||
map_index(map_index),
|
||||
com_deck_index(com_deck_index),
|
||||
source_json(json),
|
||||
number(number),
|
||||
current_state(State::REGISTRATION) {}
|
||||
@@ -324,7 +328,7 @@ void Tournament::init() {
|
||||
if (this->source_json) {
|
||||
auto& dict = this->source_json->as_dict();
|
||||
this->name = dict.at("name")->as_string();
|
||||
this->map = this->data_index->definition_for_map_number(dict.at("map_number")->as_int());
|
||||
this->map = this->map_index->definition_for_number(dict.at("map_number")->as_int());
|
||||
this->rules = Rules(dict.at("rules"));
|
||||
this->is_2v2 = dict.at("is_2v2")->as_bool();
|
||||
is_registration_complete = dict.at("is_registration_complete")->as_bool();
|
||||
@@ -344,8 +348,7 @@ void Tournament::init() {
|
||||
team->players.emplace_back(serial_number);
|
||||
this->all_player_serial_numbers.emplace(serial_number);
|
||||
} else {
|
||||
team->players.emplace_back(this->data_index->com_deck(
|
||||
player_json->as_string()));
|
||||
team->players.emplace_back(this->com_deck_index->deck_for_name(player_json->as_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,10 +489,6 @@ std::shared_ptr<JSONObject> Tournament::json() const {
|
||||
return shared_ptr<JSONObject>(new JSONObject(std::move(dict)));
|
||||
}
|
||||
|
||||
std::shared_ptr<const DataIndex> Tournament::get_data_index() const {
|
||||
return this->data_index;
|
||||
}
|
||||
|
||||
uint8_t Tournament::get_number() const {
|
||||
return this->number;
|
||||
}
|
||||
@@ -498,7 +497,7 @@ const string& Tournament::get_name() const {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::MapEntry> Tournament::get_map() const {
|
||||
shared_ptr<const MapIndex::MapEntry> Tournament::get_map() const {
|
||||
return this->map;
|
||||
}
|
||||
|
||||
@@ -593,13 +592,13 @@ void Tournament::start() {
|
||||
throw logic_error("non-human player on team before tournament start");
|
||||
}
|
||||
}
|
||||
if (this->data_index->num_com_decks() < t->max_players - t->players.size()) {
|
||||
if (this->com_deck_index->num_decks() < t->max_players - t->players.size()) {
|
||||
throw runtime_error("not enough COM decks to complete team");
|
||||
}
|
||||
// TODO: Don't allow duplicate COM decks, nor duplicate COM SCs on the same
|
||||
// team
|
||||
while (t->players.size() < t->max_players) {
|
||||
t->players.emplace_back(this->data_index->random_com_deck());
|
||||
t->players.emplace_back(this->com_deck_index->random_deck());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,10 +653,12 @@ void Tournament::print_bracket(FILE* stream) const {
|
||||
}
|
||||
|
||||
TournamentIndex::TournamentIndex(
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
const string& state_filename,
|
||||
bool skip_load_state)
|
||||
: data_index(data_index),
|
||||
: map_index(map_index),
|
||||
com_deck_index(com_deck_index),
|
||||
state_filename(state_filename) {
|
||||
if (this->state_filename.empty() || skip_load_state) {
|
||||
return;
|
||||
@@ -671,7 +672,7 @@ TournamentIndex::TournamentIndex(
|
||||
}
|
||||
for (size_t z = 0; z < 0x20; z++) {
|
||||
if (!list.at(z)->is_null()) {
|
||||
this->tournaments[z].reset(new Tournament(this->data_index, z, list[z]));
|
||||
this->tournaments[z].reset(new Tournament(this->map_index, this->com_deck_index, z, list[z]));
|
||||
this->tournaments[z]->init();
|
||||
}
|
||||
}
|
||||
@@ -706,7 +707,7 @@ vector<shared_ptr<Tournament>> TournamentIndex::all_tournaments() const {
|
||||
|
||||
shared_ptr<Tournament> TournamentIndex::create_tournament(
|
||||
const string& name,
|
||||
shared_ptr<const DataIndex::MapEntry> map,
|
||||
shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2) {
|
||||
@@ -722,7 +723,7 @@ shared_ptr<Tournament> TournamentIndex::create_tournament(
|
||||
}
|
||||
|
||||
auto t = make_shared<Tournament>(
|
||||
this->data_index, number, name, map, rules, num_teams, is_2v2);
|
||||
this->map_index, this->com_deck_index, number, name, map, rules, num_teams, is_2v2);
|
||||
t->init();
|
||||
this->tournaments[number] = t;
|
||||
return t;
|
||||
|
||||
+14
-10
@@ -92,15 +92,17 @@ public:
|
||||
};
|
||||
|
||||
Tournament(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
const std::string& name,
|
||||
std::shared_ptr<const DataIndex::MapEntry> map,
|
||||
std::shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2);
|
||||
Tournament(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
std::shared_ptr<const JSONObject> json);
|
||||
~Tournament() = default;
|
||||
@@ -108,10 +110,9 @@ public:
|
||||
|
||||
std::shared_ptr<JSONObject> json() const;
|
||||
|
||||
std::shared_ptr<const DataIndex> get_data_index() const;
|
||||
uint8_t get_number() const;
|
||||
const std::string& get_name() const;
|
||||
std::shared_ptr<const DataIndex::MapEntry> get_map() const;
|
||||
std::shared_ptr<const MapIndex::MapEntry> get_map() const;
|
||||
const Rules& get_rules() const;
|
||||
bool get_is_2v2() const;
|
||||
State get_state() const;
|
||||
@@ -131,11 +132,12 @@ public:
|
||||
private:
|
||||
PrefixedLogger log;
|
||||
|
||||
std::shared_ptr<const DataIndex> data_index;
|
||||
std::shared_ptr<const MapIndex> map_index;
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index;
|
||||
std::shared_ptr<const JSONObject> source_json;
|
||||
uint8_t number;
|
||||
std::string name;
|
||||
std::shared_ptr<const DataIndex::MapEntry> map;
|
||||
std::shared_ptr<const MapIndex::MapEntry> map;
|
||||
Rules rules;
|
||||
size_t num_teams;
|
||||
bool is_2v2;
|
||||
@@ -160,7 +162,8 @@ private:
|
||||
class TournamentIndex {
|
||||
public:
|
||||
explicit TournamentIndex(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
const std::string& state_filename,
|
||||
bool skip_load_state = false);
|
||||
~TournamentIndex() = default;
|
||||
@@ -171,7 +174,7 @@ public:
|
||||
|
||||
std::shared_ptr<Tournament> create_tournament(
|
||||
const std::string& name,
|
||||
std::shared_ptr<const DataIndex::MapEntry> map,
|
||||
std::shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2);
|
||||
@@ -183,7 +186,8 @@ public:
|
||||
uint32_t serial_number) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const DataIndex> data_index;
|
||||
std::shared_ptr<const MapIndex> map_index;
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index;
|
||||
std::string state_filename;
|
||||
std::shared_ptr<Tournament> tournaments[0x20];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user