mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-14 14:32:15 -07:00
Turn things in common into separate libs.
Took 2 hours 27 minutes
This commit is contained in:
parent
53d80efab8
commit
01378b8314
389 changed files with 336 additions and 233 deletions
|
|
@ -2,48 +2,12 @@
|
|||
#
|
||||
# provides the common library
|
||||
|
||||
add_subdirectory(pb)
|
||||
|
||||
set(common_SOURCES
|
||||
abstract_deck_list_card_node.cpp
|
||||
abstract_deck_list_node.cpp
|
||||
debug_pb_message.cpp
|
||||
deck_list_card_node.cpp
|
||||
deck_list.cpp
|
||||
expression.cpp
|
||||
featureset.cpp
|
||||
get_pb_extension.cpp
|
||||
inner_deck_list_node.cpp
|
||||
passwordhasher.cpp
|
||||
rng_abstract.cpp
|
||||
rng_sfmt.cpp
|
||||
server/game/server_abstract_participant.cpp
|
||||
server/game/server_arrow.cpp
|
||||
server/game/server_arrowtarget.cpp
|
||||
server/game/server_card.cpp
|
||||
server/game/server_cardzone.cpp
|
||||
server/game/server_counter.cpp
|
||||
server/game/server_game.cpp
|
||||
server/game/server_player.cpp
|
||||
server/game/server_spectator.cpp
|
||||
server/server.cpp
|
||||
server/server_abstractuserinterface.cpp
|
||||
server/server_database_interface.cpp
|
||||
server/server_protocolhandler.cpp
|
||||
server/server_remoteuserinterface.cpp
|
||||
server/server_response_containers.cpp
|
||||
server/server_room.cpp
|
||||
serverinfo_user_container.cpp
|
||||
sfmt/SFMT.c
|
||||
)
|
||||
|
||||
set(ORACLE_LIBS)
|
||||
|
||||
include_directories(pb)
|
||||
include_directories(sfmt)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIR})
|
||||
include_directories(${${COCKATRICE_QT_VERSION_NAME}Core_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_library(cockatrice_common ${common_SOURCES} ${common_MOC_SRCS})
|
||||
target_link_libraries(cockatrice_common PUBLIC cockatrice_protocol)
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
#include "abstract_deck_list_card_node.h"
|
||||
|
||||
bool AbstractDecklistCardNode::compare(AbstractDecklistNode *other) const
|
||||
{
|
||||
switch (sortMethod) {
|
||||
case ByNumber:
|
||||
return compareNumber(other);
|
||||
case ByName:
|
||||
return compareName(other);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractDecklistCardNode::compareNumber(AbstractDecklistNode *other) const
|
||||
{
|
||||
auto *other2 = dynamic_cast<AbstractDecklistCardNode *>(other);
|
||||
if (other2) {
|
||||
int n1 = getNumber();
|
||||
int n2 = other2->getNumber();
|
||||
return (n1 != n2) ? (n1 > n2) : compareName(other);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractDecklistCardNode::compareName(AbstractDecklistNode *other) const
|
||||
{
|
||||
auto *other2 = dynamic_cast<AbstractDecklistCardNode *>(other);
|
||||
if (other2) {
|
||||
return (getName() > other2->getName());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
if (xml->isEndElement() && xml->name().toString() == "card")
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml)
|
||||
{
|
||||
xml->writeEmptyElement("card");
|
||||
xml->writeAttribute("number", QString::number(getNumber()));
|
||||
xml->writeAttribute("name", getName());
|
||||
|
||||
if (!getCardSetShortName().isEmpty()) {
|
||||
xml->writeAttribute("setShortName", getCardSetShortName());
|
||||
}
|
||||
if (!getCardCollectorNumber().isEmpty()) {
|
||||
xml->writeAttribute("collectorNumber", getCardCollectorNumber());
|
||||
}
|
||||
if (!getCardProviderId().isEmpty()) {
|
||||
xml->writeAttribute("uuid", getCardProviderId());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
/**
|
||||
* @file abstract_deck_list_card_node.h
|
||||
* @brief Defines the AbstractDecklistCardNode base class, which adds
|
||||
* card-specific behavior on top of AbstractDecklistNode.
|
||||
*
|
||||
* This class is the intermediate abstract base between the generic
|
||||
* AbstractDecklistNode and concrete card entries such as DecklistCardNode
|
||||
* or DecklistModelCardNode.
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_ABSTRACT_DECK_LIST_CARD_NODE_H
|
||||
#define COCKATRICE_ABSTRACT_DECK_LIST_CARD_NODE_H
|
||||
|
||||
#include "abstract_deck_list_node.h"
|
||||
|
||||
/**
|
||||
* @class AbstractDecklistCardNode
|
||||
* @ingroup DeckModels
|
||||
* @brief Abstract base class for all deck list nodes that represent
|
||||
* actual card entries.
|
||||
*
|
||||
* While AbstractDecklistNode provides the general interface for all
|
||||
* nodes in the deck tree (zones, groups, cards), this subclass refines
|
||||
* the interface to cover properties specific to *cards*:
|
||||
* - Quantity (number of copies).
|
||||
* - Name.
|
||||
* - Set code and collector number.
|
||||
* - Provider ID.
|
||||
*
|
||||
* ### Role in the hierarchy:
|
||||
* - Leaf-oriented abstract class; no children of its own.
|
||||
* - Serves as the base for concrete implementations:
|
||||
* - @c DecklistCardNode: Stores real card data in the deck tree.
|
||||
* - @c DecklistModelCardNode: Wraps a DecklistCardNode for use
|
||||
* in the Qt model layer.
|
||||
*
|
||||
* ### Responsibilities:
|
||||
* - Defines getters/setters for all card-identifying attributes.
|
||||
* - Provides comparison logic for sorting by name or number.
|
||||
* - Implements XML serialization for saving/loading deck files.
|
||||
*
|
||||
* ### Ownership:
|
||||
* - As with all nodes, owned by its parent InnerDecklistNode.
|
||||
*/
|
||||
class AbstractDecklistCardNode : public AbstractDecklistNode
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new AbstractDecklistCardNode.
|
||||
*
|
||||
* @param _parent Optional parent node. If provided, this node
|
||||
* will be inserted into the parent’s children list.
|
||||
* @param position Index at which to insert into parent’s children.
|
||||
* If -1, the node is appended to the end.
|
||||
*/
|
||||
explicit AbstractDecklistCardNode(InnerDecklistNode *_parent = nullptr, int position = -1)
|
||||
: AbstractDecklistNode(_parent, position)
|
||||
{
|
||||
}
|
||||
|
||||
/// @return The number of copies of this card in the deck.
|
||||
virtual int getNumber() const = 0;
|
||||
|
||||
/// @param _number Set the number of copies of this card.
|
||||
virtual void setNumber(int _number) = 0;
|
||||
|
||||
/// @return The display name of this card.
|
||||
QString getName() const override = 0;
|
||||
|
||||
/// @param _name Set the display name of this card.
|
||||
virtual void setName(const QString &_name) = 0;
|
||||
|
||||
/// @return The provider identifier for this card (e.g., UUID).
|
||||
virtual QString getCardProviderId() const override = 0;
|
||||
|
||||
/// @param _cardProviderId Set the provider identifier for this card.
|
||||
virtual void setCardProviderId(const QString &_cardProviderId) = 0;
|
||||
|
||||
/// @return The abbreviated set code (e.g., "NEO").
|
||||
virtual QString getCardSetShortName() const override = 0;
|
||||
|
||||
/// @param _cardSetShortName Set the abbreviated set code.
|
||||
virtual void setCardSetShortName(const QString &_cardSetShortName) = 0;
|
||||
|
||||
/// @return The collector number of the card within its set.
|
||||
virtual QString getCardCollectorNumber() const override = 0;
|
||||
|
||||
/// @param _cardSetNumber Set the collector number.
|
||||
virtual void setCardCollectorNumber(const QString &_cardSetNumber) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the height of this node in the tree.
|
||||
*
|
||||
* For card nodes, height is always 0 because they are leaf nodes
|
||||
* and do not contain children.
|
||||
*
|
||||
* @return 0
|
||||
*/
|
||||
int height() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this card node against another for sorting.
|
||||
*
|
||||
* Uses the node’s current @c sortMethod to determine how to compare:
|
||||
* - ByName: Alphabetical comparison.
|
||||
* - ByNumber: Numerical comparison.
|
||||
* - Default: Falls back to implementation-defined behavior.
|
||||
*
|
||||
* @param other Another node to compare against.
|
||||
* @return true if this node should sort before @p other.
|
||||
*/
|
||||
bool compare(AbstractDecklistNode *other) const override;
|
||||
|
||||
/**
|
||||
* @brief Compare this card node to another by quantity.
|
||||
* @param other Node to compare against.
|
||||
* @return true if this node’s number < other’s number.
|
||||
*/
|
||||
bool compareNumber(AbstractDecklistNode *other) const;
|
||||
|
||||
/**
|
||||
* @brief Compare this card node to another by name.
|
||||
* @param other Node to compare against.
|
||||
* @return true if this node’s name comes before other’s name.
|
||||
*/
|
||||
bool compareName(AbstractDecklistNode *other) const;
|
||||
|
||||
/**
|
||||
* @brief Deserialize this node’s properties from XML.
|
||||
* @param xml QXmlStreamReader positioned at the element.
|
||||
* @return true if parsing succeeded.
|
||||
*
|
||||
* This supports loading deck files from Cockatrice’s XML format.
|
||||
*/
|
||||
bool readElement(QXmlStreamReader *xml) override;
|
||||
|
||||
/**
|
||||
* @brief Serialize this node’s properties to XML.
|
||||
* @param xml Writer to append this node’s XML element.
|
||||
*
|
||||
* This supports saving deck files to Cockatrice’s XML format.
|
||||
*/
|
||||
void writeElement(QXmlStreamWriter *xml) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_ABSTRACT_DECK_LIST_CARD_NODE_H
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#include "abstract_deck_list_node.h"
|
||||
|
||||
#include "inner_deck_list_node.h"
|
||||
|
||||
AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent, int position)
|
||||
: parent(_parent), sortMethod(Default)
|
||||
{
|
||||
if (parent) {
|
||||
if (position == -1) {
|
||||
parent->append(this);
|
||||
} else {
|
||||
parent->insert(position, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int AbstractDecklistNode::depth() const
|
||||
{
|
||||
if (parent) {
|
||||
return parent->depth() + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
/**
|
||||
* @file abstract_deck_list_node.h
|
||||
* @brief Defines the AbstractDecklistNode base class used as the foundation
|
||||
* for all nodes in the deck list tree (zones, groups, and cards).
|
||||
*
|
||||
* The deck list is modeled as a tree:
|
||||
* - The invisible root node is managed by DeckListModel.
|
||||
* - Top-level children are zones (e.g. Mainboard, Sideboard).
|
||||
* - Zones contain grouping nodes (e.g. by type, color, or mana cost).
|
||||
* - Grouping nodes contain card nodes.
|
||||
*
|
||||
* This abstract base class provides the interface and shared functionality
|
||||
* for all node types. Concrete subclasses (InnerDecklistNode,
|
||||
* DecklistCardNode, DecklistModelCardNode, etc.) implement the specifics.
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_ABSTRACT_DECK_LIST_NODE_H
|
||||
#define COCKATRICE_ABSTRACT_DECK_LIST_NODE_H
|
||||
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <QtCore/QXmlStreamWriter>
|
||||
|
||||
/**
|
||||
* @enum DeckSortMethod
|
||||
* @ingroup DeckModels
|
||||
* @brief Defines the different sort strategies a node may use
|
||||
* to order its children.
|
||||
*
|
||||
* Sorting behavior is typically set by the DeckListModel when the user
|
||||
* requests sorting in the UI.
|
||||
*
|
||||
* - ByNumber: Sort numerically (often by collector number).
|
||||
* - ByName: Sort alphabetically by card name.
|
||||
* - Default: No explicit sorting; insertion order is preserved.
|
||||
*/
|
||||
enum DeckSortMethod
|
||||
{
|
||||
ByNumber, ///< Sort by numeric properties (e.g. collector number).
|
||||
ByName, ///< Sort by card name (locale-aware comparison).
|
||||
Default ///< Leave in insertion order.
|
||||
};
|
||||
|
||||
class InnerDecklistNode;
|
||||
|
||||
/**
|
||||
* @class AbstractDecklistNode
|
||||
* @ingroup DeckModels
|
||||
* @brief Base class for all nodes in the deck list tree.
|
||||
*
|
||||
* This class defines the common interface for every node in the
|
||||
* deck representation: zones, groupings, and cards.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Maintain a pointer to its parent (if any).
|
||||
* - Track the sorting method to be used for child nodes.
|
||||
* - Provide a consistent interface for retrieving basic identifying
|
||||
* properties (name, set, collector number, provider ID).
|
||||
* - Define abstract methods for XML serialization, used when saving
|
||||
* or loading deck files.
|
||||
*
|
||||
* Lifetime / Ownership:
|
||||
* - Nodes are arranged hierarchically under @c InnerDecklistNode parents.
|
||||
* - The parent takes ownership of its children; destruction cascades.
|
||||
* - The DeckListModel holds the invisible root node, which in turn
|
||||
* owns the entire hierarchy.
|
||||
*
|
||||
* Extension:
|
||||
* - @c InnerDecklistNode is the concrete subclass representing
|
||||
* "folders" in the tree (zones, groups).
|
||||
* - @c DecklistCardNode and @c DecklistModelCardNode represent
|
||||
* actual card entries.
|
||||
*/
|
||||
class AbstractDecklistNode
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* @brief Pointer to the parent node, or nullptr if this is the root.
|
||||
*
|
||||
* Ownership note: The parent is responsible for destroying this node
|
||||
* when it is removed from the tree.
|
||||
*/
|
||||
InnerDecklistNode *parent;
|
||||
|
||||
/**
|
||||
* @brief Current sorting strategy for this node's children.
|
||||
*
|
||||
* Sorting is applied recursively by the DeckListModel when
|
||||
* the view requests it.
|
||||
*/
|
||||
DeckSortMethod sortMethod;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new AbstractDecklistNode and insert it into its parent.
|
||||
*
|
||||
* @param _parent Parent node. May be nullptr if this is the root.
|
||||
* @param position Optional index at which to insert into the parent's
|
||||
* children. If -1, the node is appended to the end.
|
||||
*
|
||||
* If a parent is provided, the constructor automatically appends
|
||||
* or inserts this node into the parent’s child list.
|
||||
*/
|
||||
explicit AbstractDecklistNode(InnerDecklistNode *_parent = nullptr, int position = -1);
|
||||
|
||||
/// Virtual destructor. Child classes must clean up their resources.
|
||||
virtual ~AbstractDecklistNode() = default;
|
||||
|
||||
/**
|
||||
* @brief Set the sort method for this node’s children.
|
||||
* @param method The sorting strategy to use.
|
||||
*
|
||||
* Subclasses may override if they need to apply additional logic.
|
||||
*/
|
||||
virtual void setSortMethod(DeckSortMethod method)
|
||||
{
|
||||
sortMethod = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Core identification properties
|
||||
*
|
||||
* These methods provide a standard way for the model to retrieve
|
||||
* identifying information about a node, regardless of type.
|
||||
* @{
|
||||
*/
|
||||
virtual QString getName() const = 0;
|
||||
virtual QString getCardProviderId() const = 0;
|
||||
virtual QString getCardSetShortName() const = 0;
|
||||
virtual QString getCardCollectorNumber() const = 0;
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @brief Whether this node is the "deck header" (deck metadata).
|
||||
*
|
||||
* This distinguishes special nodes that represent deck-level
|
||||
* information rather than cards or groupings.
|
||||
*/
|
||||
[[nodiscard]] virtual bool isDeckHeader() const = 0;
|
||||
|
||||
/// @return The parent node, or nullptr if this is the root.
|
||||
InnerDecklistNode *getParent() const
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute the depth of this node in the tree.
|
||||
* @return Distance from the root (root = 0, children = 1, etc.).
|
||||
*/
|
||||
int depth() const;
|
||||
|
||||
/**
|
||||
* @brief Compute the "height" of this node.
|
||||
*
|
||||
* Height is defined by subclasses; it usually represents how
|
||||
* many levels of descendants this node spans.
|
||||
*
|
||||
* For example:
|
||||
* - A card node has height 1.
|
||||
* - A group node containing cards has height 2.
|
||||
*/
|
||||
virtual int height() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Compare this node against another for sorting.
|
||||
*
|
||||
* The semantics of comparison depend on the node type and the
|
||||
* current @c sortMethod.
|
||||
*
|
||||
* @param other The node to compare against.
|
||||
* @return true if this node should come before @p other.
|
||||
*/
|
||||
virtual bool compare(AbstractDecklistNode *other) const = 0;
|
||||
|
||||
/**
|
||||
* @name XML serialization
|
||||
* These methods support reading and writing decks from/to
|
||||
* Cockatrice deck XML format.
|
||||
* @{
|
||||
*/
|
||||
virtual bool readElement(QXmlStreamReader *xml) = 0;
|
||||
virtual void writeElement(QXmlStreamWriter *xml) = 0;
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_ABSTRACT_DECK_LIST_NODE_H
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef CARD_REF_H
|
||||
#define CARD_REF_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* The information passed over the server that is required to identify the exact card to display.
|
||||
*
|
||||
* @param name The name of the card. Should not be empty, unless to indicate the lack of a card.
|
||||
* @param providerId Determines which printing of the card to use. Can be empty, in which case Cockatrice should default
|
||||
* to using the preferred set.
|
||||
*/
|
||||
struct CardRef
|
||||
{
|
||||
QString name;
|
||||
QString providerId = QString();
|
||||
|
||||
bool operator==(const CardRef &other) const
|
||||
{
|
||||
return name == other.name && providerId == other.providerId;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CARD_REF_H
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#ifndef COLOR_H
|
||||
#define COLOR_H
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
#include <QColor>
|
||||
#endif
|
||||
|
||||
#include "pb/color.pb.h"
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
inline QColor convertColorToQColor(const color &c)
|
||||
{
|
||||
return QColor(c.r(), c.g(), c.b());
|
||||
}
|
||||
|
||||
inline color convertQColorToColor(const QColor &c)
|
||||
{
|
||||
color result;
|
||||
result.set_r(c.red());
|
||||
result.set_g(c.green());
|
||||
result.set_b(c.blue());
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline color makeColor(int r, int g, int b)
|
||||
{
|
||||
color result;
|
||||
result.set_r(r);
|
||||
result.set_g(g);
|
||||
result.set_b(b);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
#include "debug_pb_message.h"
|
||||
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include <google/protobuf/text_format.h>
|
||||
|
||||
// FastFieldValuePrinter is added in protobuf 3.4, going out of our way to add the old FieldValuePrinter is not worth it
|
||||
#if GOOGLE_PROTOBUF_VERSION > 3004000
|
||||
|
||||
// value printer to use for all values, will snip too long contents
|
||||
class LimitedPrinter : public ::google::protobuf::TextFormat::FastFieldValuePrinter
|
||||
{
|
||||
public:
|
||||
void PrintString(const std::string &val,
|
||||
::google::protobuf::TextFormat::BaseTextGenerator *generator) const override;
|
||||
};
|
||||
|
||||
// value printer to use for specifc values, will expunge sensitive info
|
||||
class SafePrinter : public ::google::protobuf::TextFormat::FastFieldValuePrinter
|
||||
{
|
||||
public:
|
||||
void PrintString(const std::string &val,
|
||||
::google::protobuf::TextFormat::BaseTextGenerator *generator) const override;
|
||||
|
||||
static void applySafePrinter(const ::google::protobuf::Message &message,
|
||||
::google::protobuf::TextFormat::Printer &printer);
|
||||
};
|
||||
|
||||
void LimitedPrinter::PrintString(const std::string &val,
|
||||
::google::protobuf::TextFormat::BaseTextGenerator *generator) const
|
||||
{
|
||||
auto length = val.length();
|
||||
if (length > MAX_TEXT_LENGTH) {
|
||||
::google::protobuf::TextFormat::FastFieldValuePrinter::PrintString(
|
||||
val.substr(0, MAX_NAME_LENGTH) + "... ---snip--- (" + std::to_string(length) + " bytes total", generator);
|
||||
} else {
|
||||
::google::protobuf::TextFormat::FastFieldValuePrinter::PrintString(val, generator);
|
||||
}
|
||||
}
|
||||
|
||||
void SafePrinter::PrintString(const std::string & /*val*/,
|
||||
::google::protobuf::TextFormat::BaseTextGenerator *generator) const
|
||||
{
|
||||
generator->PrintLiteral("\" ---value expunged--- \"");
|
||||
}
|
||||
|
||||
void SafePrinter::applySafePrinter(const ::google::protobuf::Message &message,
|
||||
::google::protobuf::TextFormat::Printer &printer)
|
||||
{
|
||||
const auto *reflection = message.GetReflection();
|
||||
std::vector<const google::protobuf::FieldDescriptor *> fields;
|
||||
reflection->ListFields(message, &fields);
|
||||
for (const auto *field : fields) {
|
||||
switch (field->cpp_type()) {
|
||||
case ::google::protobuf::FieldDescriptor::CPPTYPE_STRING:
|
||||
if (field->name().find("password") != std::string::npos) { // name contains password
|
||||
auto *safePrinter = new SafePrinter();
|
||||
if (!printer.RegisterFieldValuePrinter(field, safePrinter))
|
||||
delete safePrinter; // in case safePrinter has not been taken ownership of
|
||||
}
|
||||
break;
|
||||
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
|
||||
if (field->is_repeated()) {
|
||||
for (int i = 0; i < reflection->FieldSize(message, field); ++i) {
|
||||
applySafePrinter(reflection->GetRepeatedMessage(message, field, i), printer);
|
||||
}
|
||||
} else {
|
||||
applySafePrinter(reflection->GetMessage(message, field), printer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // GOOGLE_PROTOBUF_VERSION > 3004000
|
||||
|
||||
QString getSafeDebugString(const ::google::protobuf::Message &message)
|
||||
{
|
||||
#if GOOGLE_PROTOBUF_VERSION > 3001000
|
||||
auto size = message.ByteSizeLong();
|
||||
#else
|
||||
auto size = message.ByteSize();
|
||||
#endif
|
||||
|
||||
::google::protobuf::TextFormat::Printer printer;
|
||||
printer.SetSingleLineMode(true); // compact mode
|
||||
printer.SetExpandAny(true); // prints all fields
|
||||
|
||||
#if GOOGLE_PROTOBUF_VERSION > 3004000
|
||||
// printer takes ownership of the LimitedPrinter and will delete it
|
||||
printer.SetDefaultFieldValuePrinter(new LimitedPrinter());
|
||||
// check field names an create SafePrinters for necessary fields
|
||||
SafePrinter::applySafePrinter(message, printer);
|
||||
#else
|
||||
// removing passwords from debug output will only be supported on newer protobuf versions
|
||||
printer.SetTruncateStringFieldLongerThan(MAX_TEXT_LENGTH);
|
||||
#endif // GOOGLE_PROTOBUF_VERSION > 3004000
|
||||
|
||||
std::string debug_string;
|
||||
printer.PrintToString(message, &debug_string);
|
||||
return QString::number(size) + " bytes " + QString::fromStdString(debug_string);
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef DEBUG_PB_MESSAGE_H
|
||||
#define DEBUG_PB_MESSAGE_H
|
||||
|
||||
class QString;
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
} // namespace google
|
||||
|
||||
QString getSafeDebugString(const ::google::protobuf::Message &message);
|
||||
|
||||
#endif // DEBUG_PB_MESSAGE_H
|
||||
|
|
@ -1,713 +0,0 @@
|
|||
#include "deck_list.h"
|
||||
|
||||
#include "abstract_deck_list_node.h"
|
||||
#include "deck_list_card_node.h"
|
||||
#include "inner_deck_list_node.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
#include <algorithm>
|
||||
|
||||
#if QT_VERSION < 0x050600
|
||||
// qHash on QRegularExpression was added in 5.6, FIX IT
|
||||
uint qHash(const QRegularExpression &key, uint seed) noexcept
|
||||
{
|
||||
return qHash(key.pattern(), seed); // call qHash on pattern QString instead
|
||||
}
|
||||
#endif
|
||||
|
||||
SideboardPlan::SideboardPlan(const QString &_name, const QList<MoveCard_ToZone> &_moveList)
|
||||
: name(_name), moveList(_moveList)
|
||||
{
|
||||
}
|
||||
|
||||
void SideboardPlan::setMoveList(const QList<MoveCard_ToZone> &_moveList)
|
||||
{
|
||||
moveList = _moveList;
|
||||
}
|
||||
|
||||
bool SideboardPlan::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
const QString childName = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName == "name")
|
||||
name = xml->readElementText();
|
||||
else if (childName == "move_card_to_zone") {
|
||||
MoveCard_ToZone m;
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
const QString childName2 = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName2 == "card_name")
|
||||
m.set_card_name(xml->readElementText().toStdString());
|
||||
else if (childName2 == "start_zone")
|
||||
m.set_start_zone(xml->readElementText().toStdString());
|
||||
else if (childName2 == "target_zone")
|
||||
m.set_target_zone(xml->readElementText().toStdString());
|
||||
} else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) {
|
||||
moveList.append(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (xml->isEndElement() && (childName == "sideboard_plan"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SideboardPlan::write(QXmlStreamWriter *xml)
|
||||
{
|
||||
xml->writeStartElement("sideboard_plan");
|
||||
xml->writeTextElement("name", name);
|
||||
for (auto &i : moveList) {
|
||||
xml->writeStartElement("move_card_to_zone");
|
||||
xml->writeTextElement("card_name", QString::fromStdString(i.card_name()));
|
||||
xml->writeTextElement("start_zone", QString::fromStdString(i.start_zone()));
|
||||
xml->writeTextElement("target_zone", QString::fromStdString(i.target_zone()));
|
||||
xml->writeEndElement();
|
||||
}
|
||||
xml->writeEndElement();
|
||||
}
|
||||
|
||||
DeckList::DeckList()
|
||||
{
|
||||
root = new InnerDecklistNode;
|
||||
}
|
||||
|
||||
// TODO: https://qt-project.org/doc/qt-4.8/qobject.html#no-copy-constructor-or-assignment-operator
|
||||
DeckList::DeckList(const DeckList &other)
|
||||
: QObject(), name(other.name), comments(other.comments), bannerCard(other.bannerCard),
|
||||
lastLoadedTimestamp(other.lastLoadedTimestamp), tags(other.tags), cachedDeckHash(other.cachedDeckHash)
|
||||
{
|
||||
root = new InnerDecklistNode(other.getRoot());
|
||||
|
||||
QMapIterator<QString, SideboardPlan *> spIterator(other.getSideboardPlans());
|
||||
while (spIterator.hasNext()) {
|
||||
spIterator.next();
|
||||
sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList()));
|
||||
}
|
||||
}
|
||||
|
||||
DeckList::DeckList(const QString &nativeString)
|
||||
{
|
||||
root = new InnerDecklistNode;
|
||||
loadFromString_Native(nativeString);
|
||||
}
|
||||
|
||||
DeckList::~DeckList()
|
||||
{
|
||||
delete root;
|
||||
|
||||
QMapIterator<QString, SideboardPlan *> i(sideboardPlans);
|
||||
while (i.hasNext())
|
||||
delete i.next().value();
|
||||
}
|
||||
|
||||
QList<MoveCard_ToZone> DeckList::getCurrentSideboardPlan()
|
||||
{
|
||||
SideboardPlan *current = sideboardPlans.value(QString(), 0);
|
||||
if (!current)
|
||||
return QList<MoveCard_ToZone>();
|
||||
else
|
||||
return current->getMoveList();
|
||||
}
|
||||
|
||||
void DeckList::setCurrentSideboardPlan(const QList<MoveCard_ToZone> &plan)
|
||||
{
|
||||
SideboardPlan *current = sideboardPlans.value(QString(), 0);
|
||||
if (!current) {
|
||||
current = new SideboardPlan;
|
||||
sideboardPlans.insert(QString(), current);
|
||||
}
|
||||
|
||||
current->setMoveList(plan);
|
||||
}
|
||||
|
||||
bool DeckList::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
const QString childName = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName == "lastLoadedTimestamp") {
|
||||
lastLoadedTimestamp = xml->readElementText();
|
||||
} else if (childName == "deckname") {
|
||||
name = xml->readElementText();
|
||||
} else if (childName == "comments") {
|
||||
comments = xml->readElementText();
|
||||
} else if (childName == "bannerCard") {
|
||||
QString providerId = xml->attributes().value("providerId").toString();
|
||||
QString cardName = xml->readElementText();
|
||||
bannerCard = {cardName, providerId};
|
||||
} else if (childName == "tags") {
|
||||
tags.clear(); // Clear existing tags
|
||||
while (xml->readNextStartElement()) {
|
||||
if (xml->name().toString() == "tag") {
|
||||
tags.append(xml->readElementText());
|
||||
}
|
||||
}
|
||||
} else if (childName == "zone") {
|
||||
InnerDecklistNode *newZone = getZoneObjFromName(xml->attributes().value("name").toString());
|
||||
newZone->readElement(xml);
|
||||
} else if (childName == "sideboard_plan") {
|
||||
SideboardPlan *newSideboardPlan = new SideboardPlan;
|
||||
if (newSideboardPlan->readElement(xml)) {
|
||||
sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan);
|
||||
} else {
|
||||
delete newSideboardPlan;
|
||||
}
|
||||
}
|
||||
} else if (xml->isEndElement() && (childName == "cockatrice_deck")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeckList::write(QXmlStreamWriter *xml) const
|
||||
{
|
||||
xml->writeStartElement("cockatrice_deck");
|
||||
xml->writeAttribute("version", "1");
|
||||
xml->writeTextElement("lastLoadedTimestamp", lastLoadedTimestamp);
|
||||
xml->writeTextElement("deckname", name);
|
||||
xml->writeStartElement("bannerCard");
|
||||
xml->writeAttribute("providerId", bannerCard.providerId);
|
||||
xml->writeCharacters(bannerCard.name);
|
||||
xml->writeEndElement();
|
||||
xml->writeTextElement("comments", comments);
|
||||
|
||||
// Write tags
|
||||
xml->writeStartElement("tags");
|
||||
for (const QString &tag : tags) {
|
||||
xml->writeTextElement("tag", tag);
|
||||
}
|
||||
xml->writeEndElement();
|
||||
|
||||
// Write zones
|
||||
for (int i = 0; i < root->size(); i++) {
|
||||
root->at(i)->writeElement(xml);
|
||||
}
|
||||
|
||||
// Write sideboard plans
|
||||
QMapIterator<QString, SideboardPlan *> i(sideboardPlans);
|
||||
while (i.hasNext()) {
|
||||
i.next().value()->write(xml);
|
||||
}
|
||||
|
||||
xml->writeEndElement(); // Close "cockatrice_deck"
|
||||
}
|
||||
|
||||
bool DeckList::loadFromXml(QXmlStreamReader *xml)
|
||||
{
|
||||
if (xml->error()) {
|
||||
qDebug() << "Error loading deck from xml: " << xml->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
cleanList();
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
if (xml->isStartElement()) {
|
||||
if (xml->name().toString() != "cockatrice_deck")
|
||||
return false;
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
if (!readElement(xml))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshDeckHash();
|
||||
if (xml->error()) {
|
||||
qDebug() << "Error loading deck from xml: " << xml->errorString();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeckList::loadFromString_Native(const QString &nativeString)
|
||||
{
|
||||
QXmlStreamReader xml(nativeString);
|
||||
return loadFromXml(&xml);
|
||||
}
|
||||
|
||||
QString DeckList::writeToString_Native() const
|
||||
{
|
||||
QString result;
|
||||
QXmlStreamWriter xml(&result);
|
||||
xml.writeStartDocument();
|
||||
write(&xml);
|
||||
xml.writeEndDocument();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DeckList::loadFromFile_Native(QIODevice *device)
|
||||
{
|
||||
QXmlStreamReader xml(device);
|
||||
return loadFromXml(&xml);
|
||||
}
|
||||
|
||||
bool DeckList::saveToFile_Native(QIODevice *device)
|
||||
{
|
||||
QXmlStreamWriter xml(device);
|
||||
xml.setAutoFormatting(true);
|
||||
xml.writeStartDocument();
|
||||
|
||||
write(&xml);
|
||||
|
||||
xml.writeEndDocument();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the decklist and loads in a new deck from text
|
||||
*
|
||||
* @param in The text to load
|
||||
* @param preserveMetadata If true, don't clear the existing metadata
|
||||
* @return False if the input was empty, true otherwise.
|
||||
*/
|
||||
bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
|
||||
{
|
||||
const QRegularExpression reCardLine(R"(^\s*[\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression reEmpty("^\\s*$");
|
||||
const QRegularExpression reComment(R"([\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression reSBMark("^\\s*sb:\\s*(.+)", QRegularExpression::CaseInsensitiveOption);
|
||||
const QRegularExpression reSBComment("^sideboard\\b.*$", QRegularExpression::CaseInsensitiveOption);
|
||||
const QRegularExpression reDeckComment("^((main)?deck(list)?|mainboard)\\b",
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
// Regex for advanced card parsing
|
||||
const QRegularExpression reMultiplier(R"(^[xX\(\[]*(\d+)[xX\*\)\]]* ?(.+))");
|
||||
const QRegularExpression reSplitCard(R"( ?\/\/ ?)");
|
||||
const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested
|
||||
const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string
|
||||
const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits
|
||||
const QRegularExpression reBraceDigit(
|
||||
R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number
|
||||
const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)");
|
||||
|
||||
// Regex for extracting set code and collector number with attached symbols
|
||||
const QRegularExpression reHyphenFormat(R"(\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*))");
|
||||
const QRegularExpression reRegularFormat(R"(\((\w{3,})\)\s+(\d+[^\w\s]*))");
|
||||
|
||||
const QHash<QRegularExpression, QString> differences{{QRegularExpression("’"), QString("'")},
|
||||
{QRegularExpression("Æ"), QString("Ae")},
|
||||
{QRegularExpression("æ"), QString("ae")},
|
||||
{QRegularExpression(" ?[|/]+ ?"), QString(" // ")}};
|
||||
|
||||
cleanList(preserveMetadata);
|
||||
|
||||
auto inputs = in.readAll().trimmed().split('\n');
|
||||
auto max_line = inputs.size();
|
||||
|
||||
// Start at the first empty line before the first card line
|
||||
auto deckStart = inputs.indexOf(reCardLine);
|
||||
if (deckStart == -1) {
|
||||
if (inputs.indexOf(reComment) == -1) {
|
||||
return false; // Input is empty
|
||||
}
|
||||
deckStart = max_line;
|
||||
} else {
|
||||
deckStart = inputs.lastIndexOf(reEmpty, deckStart);
|
||||
if (deckStart == -1) {
|
||||
deckStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// find sideboard position, if marks are used this won't be needed
|
||||
int sBStart = -1;
|
||||
if (inputs.indexOf(reSBMark, deckStart) == -1) {
|
||||
sBStart = inputs.indexOf(reSBComment, deckStart);
|
||||
if (sBStart == -1) {
|
||||
sBStart = inputs.indexOf(reEmpty, deckStart + 1);
|
||||
if (sBStart == -1) {
|
||||
sBStart = max_line;
|
||||
}
|
||||
auto nextCard = inputs.indexOf(reCardLine, sBStart + 1);
|
||||
if (inputs.indexOf(reEmpty, nextCard + 1) != -1) {
|
||||
sBStart = max_line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
QRegularExpressionMatch match;
|
||||
|
||||
// Parse name and comments
|
||||
while (index < deckStart) {
|
||||
const auto ¤t = inputs.at(index++);
|
||||
if (!current.contains(reEmpty)) {
|
||||
match = reComment.match(current);
|
||||
name = match.captured();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (index < deckStart) {
|
||||
const auto ¤t = inputs.at(index++);
|
||||
if (!current.contains(reEmpty)) {
|
||||
match = reComment.match(current);
|
||||
comments += match.captured() + '\n';
|
||||
}
|
||||
}
|
||||
comments.chop(1);
|
||||
|
||||
// Discard empty lines
|
||||
while (index < max_line && inputs.at(index).contains(reEmpty)) {
|
||||
++index;
|
||||
}
|
||||
|
||||
// Discard line if it starts with deck or mainboard, all cards until the sideboard starts are in the mainboard
|
||||
if (inputs.at(index).contains(reDeckComment)) {
|
||||
++index;
|
||||
}
|
||||
|
||||
// Parse decklist
|
||||
for (; index < max_line; ++index) {
|
||||
// check if line is a card
|
||||
match = reCardLine.match(inputs.at(index));
|
||||
if (!match.hasMatch())
|
||||
continue;
|
||||
|
||||
QString cardName = match.captured().simplified();
|
||||
bool sideboard = false;
|
||||
|
||||
// Sideboard detection
|
||||
if (sBStart < 0) {
|
||||
match = reSBMark.match(cardName);
|
||||
if (match.hasMatch()) {
|
||||
sideboard = true;
|
||||
cardName = match.captured(1);
|
||||
}
|
||||
} else {
|
||||
if (index == sBStart)
|
||||
continue;
|
||||
sideboard = index > sBStart;
|
||||
}
|
||||
|
||||
// Extract set code, collector number, and foil
|
||||
QString setCode;
|
||||
QString collectorNumber;
|
||||
bool isFoil = false;
|
||||
|
||||
// Check for foil status at the end of the card name
|
||||
if (cardName.endsWith("*F*", Qt::CaseInsensitive)) {
|
||||
isFoil = true;
|
||||
cardName.chop(3); // Remove the "*F*" from the card name
|
||||
}
|
||||
Q_UNUSED(isFoil);
|
||||
|
||||
// Attempt to match the hyphen-separated format (PLST-2094)
|
||||
match = reHyphenFormat.match(cardName);
|
||||
if (match.hasMatch()) {
|
||||
setCode = match.captured(2).toUpper();
|
||||
collectorNumber = match.captured(3);
|
||||
cardName = cardName.left(match.capturedStart()).trimmed();
|
||||
} else {
|
||||
// Attempt to match the regular format (PLST) 2094
|
||||
match = reRegularFormat.match(cardName);
|
||||
if (match.hasMatch()) {
|
||||
setCode = match.captured(1).toUpper();
|
||||
collectorNumber = match.captured(2);
|
||||
cardName = cardName.left(match.capturedStart()).trimmed();
|
||||
}
|
||||
}
|
||||
|
||||
// check if a specific amount is mentioned
|
||||
int amount = 1;
|
||||
match = reMultiplier.match(cardName);
|
||||
if (match.hasMatch()) {
|
||||
amount = match.captured(1).toInt();
|
||||
cardName = match.captured(2);
|
||||
}
|
||||
|
||||
// Handle advanced card types
|
||||
if (cardName.contains(reSplitCard)) {
|
||||
cardName = cardName.split(reSplitCard).join(" // ");
|
||||
}
|
||||
|
||||
if (cardName.contains(reDoubleFacedMarker)) {
|
||||
QStringList faces = cardName.split(reDoubleFacedMarker);
|
||||
cardName = faces.first().trimmed();
|
||||
}
|
||||
|
||||
// Remove unnecessary characters
|
||||
cardName.remove(reBrace);
|
||||
cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards
|
||||
cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end
|
||||
cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after
|
||||
|
||||
// Normalize names
|
||||
for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) {
|
||||
cardName.replace(diff.key(), diff.value());
|
||||
}
|
||||
|
||||
// Resolve complete card name, this function does nothing if the name is not found
|
||||
cardName = getCompleteCardName(cardName);
|
||||
|
||||
// Determine the zone (mainboard/sideboard)
|
||||
QString zoneName = getCardZoneFromName(cardName, sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN);
|
||||
|
||||
// make new entry in decklist
|
||||
new DecklistCardNode(cardName, amount, getZoneObjFromName(zoneName), -1, setCode, collectorNumber);
|
||||
}
|
||||
|
||||
refreshDeckHash();
|
||||
return true;
|
||||
}
|
||||
|
||||
InnerDecklistNode *DeckList::getZoneObjFromName(const QString &zoneName)
|
||||
{
|
||||
for (int i = 0; i < root->size(); i++) {
|
||||
auto *node = dynamic_cast<InnerDecklistNode *>(root->at(i));
|
||||
if (node->getName() == zoneName) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return new InnerDecklistNode(zoneName, root);
|
||||
}
|
||||
|
||||
bool DeckList::loadFromFile_Plain(QIODevice *device)
|
||||
{
|
||||
QTextStream in(device);
|
||||
return loadFromStream_Plain(in, false);
|
||||
}
|
||||
|
||||
bool DeckList::saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards)
|
||||
{
|
||||
auto writeToStream = [&stream, prefixSideboardCards, slashTappedOutSplitCards](const auto node, const auto card) {
|
||||
if (prefixSideboardCards && node->getName() == DECK_ZONE_SIDE) {
|
||||
stream << "SB: ";
|
||||
}
|
||||
if (!slashTappedOutSplitCards) {
|
||||
stream << QString("%1 %2\n").arg(card->getNumber()).arg(card->getName());
|
||||
} else {
|
||||
stream << QString("%1 %2\n").arg(card->getNumber()).arg(card->getName().replace("//", "/"));
|
||||
}
|
||||
};
|
||||
|
||||
forEachCard(writeToStream);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeckList::saveToFile_Plain(QIODevice *device, bool prefixSideboardCards, bool slashTappedOutSplitCards)
|
||||
{
|
||||
QTextStream out(device);
|
||||
return saveToStream_Plain(out, prefixSideboardCards, slashTappedOutSplitCards);
|
||||
}
|
||||
|
||||
QString DeckList::writeToString_Plain(bool prefixSideboardCards, bool slashTappedOutSplitCards)
|
||||
{
|
||||
QString result;
|
||||
QTextStream out(&result);
|
||||
saveToStream_Plain(out, prefixSideboardCards, slashTappedOutSplitCards);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cards and other data from the decklist
|
||||
*
|
||||
* @param preserveMetadata If true, only clear the cards
|
||||
*/
|
||||
void DeckList::cleanList(bool preserveMetadata)
|
||||
{
|
||||
root->clearTree();
|
||||
if (!preserveMetadata) {
|
||||
setName();
|
||||
setComments();
|
||||
setTags();
|
||||
}
|
||||
refreshDeckHash();
|
||||
}
|
||||
|
||||
void DeckList::getCardListHelper(InnerDecklistNode *item, QSet<QString> &result)
|
||||
{
|
||||
for (int i = 0; i < item->size(); ++i) {
|
||||
auto *node = dynamic_cast<DecklistCardNode *>(item->at(i));
|
||||
|
||||
if (node) {
|
||||
result.insert(node->getName());
|
||||
} else {
|
||||
getCardListHelper(dynamic_cast<InnerDecklistNode *>(item->at(i)), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeckList::getCardRefListHelper(InnerDecklistNode *item, QList<CardRef> &result)
|
||||
{
|
||||
for (int i = 0; i < item->size(); ++i) {
|
||||
auto *node = dynamic_cast<DecklistCardNode *>(item->at(i));
|
||||
|
||||
if (node) {
|
||||
result.append(node->toCardRef());
|
||||
} else {
|
||||
getCardRefListHelper(dynamic_cast<InnerDecklistNode *>(item->at(i)), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList DeckList::getCardList() const
|
||||
{
|
||||
QSet<QString> result;
|
||||
getCardListHelper(root, result);
|
||||
return result.values();
|
||||
}
|
||||
|
||||
QList<CardRef> DeckList::getCardRefList() const
|
||||
{
|
||||
QList<CardRef> result;
|
||||
getCardRefListHelper(root, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int DeckList::getSideboardSize() const
|
||||
{
|
||||
int size = 0;
|
||||
for (int i = 0; i < root->size(); ++i) {
|
||||
auto *node = dynamic_cast<InnerDecklistNode *>(root->at(i));
|
||||
if (node->getName() != DECK_ZONE_SIDE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < node->size(); j++) {
|
||||
auto *card = dynamic_cast<DecklistCardNode *>(node->at(j));
|
||||
size += card->getNumber();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
DecklistCardNode *DeckList::addCard(const QString &cardName,
|
||||
const QString &zoneName,
|
||||
const int position,
|
||||
const QString &cardSetName,
|
||||
const QString &cardSetCollectorNumber,
|
||||
const QString &cardProviderId)
|
||||
{
|
||||
auto *zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
|
||||
if (zoneNode == nullptr) {
|
||||
zoneNode = new InnerDecklistNode(zoneName, root);
|
||||
}
|
||||
|
||||
auto *node =
|
||||
new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber, cardProviderId);
|
||||
refreshDeckHash();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode)
|
||||
{
|
||||
if (node == root) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateHash = false;
|
||||
if (rootNode == nullptr) {
|
||||
rootNode = root;
|
||||
updateHash = true;
|
||||
}
|
||||
|
||||
int index = rootNode->indexOf(node);
|
||||
if (index != -1) {
|
||||
delete rootNode->takeAt(index);
|
||||
|
||||
if (rootNode->empty()) {
|
||||
deleteNode(rootNode, rootNode->getParent());
|
||||
}
|
||||
|
||||
if (updateHash) {
|
||||
refreshDeckHash();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rootNode->size(); i++) {
|
||||
auto *inner = dynamic_cast<InnerDecklistNode *>(rootNode->at(i));
|
||||
if (inner) {
|
||||
if (deleteNode(node, inner)) {
|
||||
if (updateHash) {
|
||||
refreshDeckHash();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static QString computeDeckHash(const InnerDecklistNode *root)
|
||||
{
|
||||
QStringList cardList;
|
||||
QSet<QString> hashZones, optionalZones;
|
||||
|
||||
hashZones << DECK_ZONE_MAIN << DECK_ZONE_SIDE; // Zones in deck to be included in hashing process
|
||||
optionalZones << DECK_ZONE_TOKENS; // Optional zones in deck not included in hashing process
|
||||
|
||||
for (int i = 0; i < root->size(); i++) {
|
||||
auto *node = dynamic_cast<InnerDecklistNode *>(root->at(i));
|
||||
for (int j = 0; j < node->size(); j++) {
|
||||
if (hashZones.contains(node->getName())) // Mainboard or Sideboard
|
||||
{
|
||||
auto *card = dynamic_cast<DecklistCardNode *>(node->at(j));
|
||||
for (int k = 0; k < card->getNumber(); ++k) {
|
||||
cardList.append((node->getName() == DECK_ZONE_SIDE ? "SB:" : "") + card->getName().toLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cardList.sort();
|
||||
QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1);
|
||||
quint64 number = (((quint64)(unsigned char)deckHashArray[0]) << 32) +
|
||||
(((quint64)(unsigned char)deckHashArray[1]) << 24) +
|
||||
(((quint64)(unsigned char)deckHashArray[2] << 16)) +
|
||||
(((quint64)(unsigned char)deckHashArray[3]) << 8) + (quint64)(unsigned char)deckHashArray[4];
|
||||
return QString::number(number, 32).rightJustified(8, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the deck hash.
|
||||
* The hash is computed on the first call to this method, and is cached until the decklist is modified.
|
||||
*
|
||||
* @return The deck hash
|
||||
*/
|
||||
QString DeckList::getDeckHash() const
|
||||
{
|
||||
if (!cachedDeckHash.isEmpty()) {
|
||||
return cachedDeckHash;
|
||||
}
|
||||
|
||||
cachedDeckHash = computeDeckHash(root);
|
||||
return cachedDeckHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the cached deckHash and emits the deckHashChanged signal.
|
||||
*/
|
||||
void DeckList::refreshDeckHash()
|
||||
{
|
||||
cachedDeckHash = QString();
|
||||
emit deckHashChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a given function on each card in the deck.
|
||||
*/
|
||||
void DeckList::forEachCard(const std::function<void(InnerDecklistNode *, DecklistCardNode *)> &func)
|
||||
{
|
||||
// Support for this is only possible if the internal structure
|
||||
// doesn't get more complicated.
|
||||
for (int i = 0; i < root->size(); i++) {
|
||||
InnerDecklistNode *node = dynamic_cast<InnerDecklistNode *>(root->at(i));
|
||||
for (int j = 0; j < node->size(); j++) {
|
||||
DecklistCardNode *card = dynamic_cast<DecklistCardNode *>(node->at(j));
|
||||
func(node, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
/**
|
||||
* @file decklist.h
|
||||
* @brief Defines the DeckList class and supporting types for managing a full
|
||||
* deck structure including cards, zones, sideboard plans, and
|
||||
* serialization to/from multiple formats. This is a logic class which
|
||||
* does not care about Qt or user facing views.
|
||||
* See @c DeckListModel for the actual Qt Model to be used for views
|
||||
*/
|
||||
|
||||
#ifndef DECKLIST_H
|
||||
#define DECKLIST_H
|
||||
|
||||
#include "card_ref.h"
|
||||
#include "inner_deck_list_node.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QVector>
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <QtCore/QXmlStreamWriter>
|
||||
#include <common/pb/move_card_to_zone.pb.h>
|
||||
|
||||
class AbstractDecklistNode;
|
||||
class DecklistCardNode;
|
||||
class CardDatabase;
|
||||
class QIODevice;
|
||||
class QTextStream;
|
||||
class InnerDecklistNode;
|
||||
|
||||
/**
|
||||
* @class SideboardPlan
|
||||
* @ingroup Decks
|
||||
* @brief Represents a predefined sideboarding strategy for a deck.
|
||||
*
|
||||
* Sideboard plans store a named list of card movements that should be applied
|
||||
* between the mainboard and sideboard for a specific matchup. Each movement
|
||||
* is expressed using a `MoveCard_ToZone` protobuf message.
|
||||
*
|
||||
* ### Responsibilities:
|
||||
* - Store the plan name and list of moves.
|
||||
* - Support XML serialization/deserialization.
|
||||
*
|
||||
* ### Typical usage:
|
||||
* A deck can contain multiple sideboard plans (e.g., "vs Aggro", "vs Control"),
|
||||
* each describing how to transform the main deck into its intended configuration.
|
||||
*/
|
||||
class SideboardPlan
|
||||
{
|
||||
private:
|
||||
QString name; ///< Human-readable name of this plan.
|
||||
QList<MoveCard_ToZone> moveList; ///< List of move instructions for this plan.
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new SideboardPlan.
|
||||
* @param _name The plan name.
|
||||
* @param _moveList Initial list of card move instructions.
|
||||
*/
|
||||
explicit SideboardPlan(const QString &_name = QString(),
|
||||
const QList<MoveCard_ToZone> &_moveList = QList<MoveCard_ToZone>());
|
||||
|
||||
/**
|
||||
* @brief Read a SideboardPlan from an XML stream.
|
||||
* @param xml XML reader positioned at the plan element.
|
||||
* @return true if parsing succeeded.
|
||||
*/
|
||||
bool readElement(QXmlStreamReader *xml);
|
||||
|
||||
/**
|
||||
* @brief Write this SideboardPlan to XML.
|
||||
* @param xml Stream to append the serialized element to.
|
||||
*/
|
||||
void write(QXmlStreamWriter *xml);
|
||||
|
||||
/// @return The plan name.
|
||||
QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/// @return Const reference to the move list.
|
||||
const QList<MoveCard_ToZone> &getMoveList() const
|
||||
{
|
||||
return moveList;
|
||||
}
|
||||
|
||||
/// @brief Replace the move list with a new one.
|
||||
void setMoveList(const QList<MoveCard_ToZone> &_moveList);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class DeckList
|
||||
* @ingroup Decks
|
||||
* @brief Represents a complete deck, including metadata, zones, cards,
|
||||
* and sideboard plans.
|
||||
*
|
||||
* A DeckList is a QObject wrapper around an `InnerDecklistNode` tree,
|
||||
* enriched with metadata like deck name, comments, tags, banner card,
|
||||
* and multiple sideboard plans.
|
||||
*
|
||||
* ### Core responsibilities:
|
||||
* - Store and manage the root node tree (zones → groups → cards).
|
||||
* - Provide deck-level metadata (name, comments, tags, banner).
|
||||
* - Support multiple sideboard plans (meta-game strategies).
|
||||
* - Provide import/export in multiple formats:
|
||||
* - Cockatrice native XML format.
|
||||
* - Plain-text list format.
|
||||
* - Provide hashing for deck identity (deck hash).
|
||||
*
|
||||
* ### Ownership:
|
||||
* - Owns the root `InnerDecklistNode` tree.
|
||||
* - Owns `SideboardPlan` instances stored in `sideboardPlans`.
|
||||
*
|
||||
* ### Signals:
|
||||
* - @c deckHashChanged() — emitted when the deck contents change.
|
||||
* - @c deckTagsChanged() — emitted when tags are added/removed.
|
||||
*
|
||||
* ### Example workflow:
|
||||
* ```
|
||||
* DeckList deck;
|
||||
* deck.setName("Mono Red Aggro");
|
||||
* deck.addCard("Lightning Bolt", "main", -1);
|
||||
* deck.addTag("Aggro");
|
||||
* deck.saveToFile_Native(device);
|
||||
* ```
|
||||
*/
|
||||
class DeckList : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QString name; ///< User-defined deck name.
|
||||
QString comments; ///< Free-form comments or notes.
|
||||
CardRef bannerCard; ///< Optional representative card for the deck.
|
||||
QString lastLoadedTimestamp; ///< Timestamp string of last load.
|
||||
QStringList tags; ///< User-defined tags for deck classification.
|
||||
QMap<QString, SideboardPlan *> sideboardPlans; ///< Named sideboard plans.
|
||||
InnerDecklistNode *root; ///< Root of the deck tree (zones + cards).
|
||||
|
||||
/**
|
||||
* @brief Cached deck hash, recalculated lazily.
|
||||
* An empty string indicates the cache is invalid.
|
||||
*/
|
||||
mutable QString cachedDeckHash;
|
||||
|
||||
// Helpers for traversing the tree
|
||||
static void getCardListHelper(InnerDecklistNode *node, QSet<QString> &result);
|
||||
static void getCardRefListHelper(InnerDecklistNode *item, QList<CardRef> &result);
|
||||
InnerDecklistNode *getZoneObjFromName(const QString &zoneName);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Map a card name to its zone.
|
||||
* Override in subclasses for format-specific logic.
|
||||
* @param cardName Card being placed.
|
||||
* @param currentZoneName Zone candidate.
|
||||
* @return Zone name to use.
|
||||
*/
|
||||
virtual QString getCardZoneFromName(const QString /*cardName*/, QString currentZoneName)
|
||||
{
|
||||
return currentZoneName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Produce the complete display name of a card.
|
||||
* Override in subclasses to add set suffixes or annotations.
|
||||
* @param cardName Base name.
|
||||
* @return Full display name.
|
||||
*/
|
||||
virtual QString getCompleteCardName(const QString &cardName) const
|
||||
{
|
||||
return cardName;
|
||||
};
|
||||
|
||||
signals:
|
||||
/// Emitted when the deck hash changes.
|
||||
void deckHashChanged();
|
||||
/// Emitted when the deck tags are modified.
|
||||
void deckTagsChanged();
|
||||
|
||||
public slots:
|
||||
/// @name Metadata setters
|
||||
///@{
|
||||
void setName(const QString &_name = QString())
|
||||
{
|
||||
name = _name;
|
||||
}
|
||||
void setComments(const QString &_comments = QString())
|
||||
{
|
||||
comments = _comments;
|
||||
}
|
||||
void setTags(const QStringList &_tags = QStringList())
|
||||
{
|
||||
tags = _tags;
|
||||
emit deckTagsChanged();
|
||||
}
|
||||
void addTag(const QString &_tag)
|
||||
{
|
||||
tags.append(_tag);
|
||||
emit deckTagsChanged();
|
||||
}
|
||||
void clearTags()
|
||||
{
|
||||
tags.clear();
|
||||
emit deckTagsChanged();
|
||||
}
|
||||
void setBannerCard(const CardRef &_bannerCard = {})
|
||||
{
|
||||
bannerCard = _bannerCard;
|
||||
}
|
||||
void setLastLoadedTimestamp(const QString &_lastLoadedTimestamp = QString())
|
||||
{
|
||||
lastLoadedTimestamp = _lastLoadedTimestamp;
|
||||
}
|
||||
///@}
|
||||
|
||||
public:
|
||||
/// @brief Construct an empty deck.
|
||||
explicit DeckList();
|
||||
/// @brief Deep-copy constructor.
|
||||
DeckList(const DeckList &other);
|
||||
/// @brief Construct from a serialized native-format string.
|
||||
explicit DeckList(const QString &nativeString);
|
||||
~DeckList() override;
|
||||
|
||||
/// @name Metadata getters
|
||||
///@{
|
||||
QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
QString getComments() const
|
||||
{
|
||||
return comments;
|
||||
}
|
||||
QStringList getTags() const
|
||||
{
|
||||
return tags;
|
||||
}
|
||||
CardRef getBannerCard() const
|
||||
{
|
||||
return bannerCard;
|
||||
}
|
||||
QString getLastLoadedTimestamp() const
|
||||
{
|
||||
return lastLoadedTimestamp;
|
||||
}
|
||||
///@}
|
||||
|
||||
bool isBlankDeck() const
|
||||
{
|
||||
return name.isEmpty() && comments.isEmpty() && getCardList().isEmpty();
|
||||
}
|
||||
|
||||
/// @name Sideboard plans
|
||||
///@{
|
||||
QList<MoveCard_ToZone> getCurrentSideboardPlan();
|
||||
void setCurrentSideboardPlan(const QList<MoveCard_ToZone> &plan);
|
||||
const QMap<QString, SideboardPlan *> &getSideboardPlans() const
|
||||
{
|
||||
return sideboardPlans;
|
||||
}
|
||||
///@}
|
||||
|
||||
/// @name Serialization (XML)
|
||||
///@{
|
||||
bool readElement(QXmlStreamReader *xml);
|
||||
void write(QXmlStreamWriter *xml) const;
|
||||
bool loadFromXml(QXmlStreamReader *xml);
|
||||
bool loadFromString_Native(const QString &nativeString);
|
||||
QString writeToString_Native() const;
|
||||
bool loadFromFile_Native(QIODevice *device);
|
||||
bool saveToFile_Native(QIODevice *device);
|
||||
///@}
|
||||
|
||||
/// @name Serialization (Plain text)
|
||||
///@{
|
||||
bool loadFromStream_Plain(QTextStream &stream, bool preserveMetadata);
|
||||
bool loadFromFile_Plain(QIODevice *device);
|
||||
bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards);
|
||||
bool saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false);
|
||||
QString writeToString_Plain(bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false);
|
||||
///@}
|
||||
|
||||
/// @name Deck manipulation
|
||||
///@{
|
||||
void cleanList(bool preserveMetadata = false);
|
||||
bool isEmpty() const
|
||||
{
|
||||
return root->isEmpty() && name.isEmpty() && comments.isEmpty() && sideboardPlans.isEmpty();
|
||||
}
|
||||
QStringList getCardList() const;
|
||||
QList<CardRef> getCardRefList() const;
|
||||
int getSideboardSize() const;
|
||||
InnerDecklistNode *getRoot() const
|
||||
{
|
||||
return root;
|
||||
}
|
||||
DecklistCardNode *addCard(const QString &cardName,
|
||||
const QString &zoneName,
|
||||
int position,
|
||||
const QString &cardSetName = QString(),
|
||||
const QString &cardSetCollectorNumber = QString(),
|
||||
const QString &cardProviderId = QString());
|
||||
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
|
||||
///@}
|
||||
|
||||
/// @name Deck identity
|
||||
///@{
|
||||
QString getDeckHash() const;
|
||||
void refreshDeckHash();
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @brief Apply a function to every card in the deck tree.
|
||||
*
|
||||
* @param func Function taking (zone node, card node).
|
||||
*/
|
||||
void forEachCard(const std::function<void(InnerDecklistNode *, DecklistCardNode *)> &func);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#include "deck_list_card_node.h"
|
||||
|
||||
DecklistCardNode::DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent)
|
||||
: AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()),
|
||||
cardSetShortName(other->getCardSetShortName()), cardSetNumber(other->getCardCollectorNumber()),
|
||||
cardProviderId(other->getCardProviderId())
|
||||
{
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
/**
|
||||
* @file deck_list_card_node.h
|
||||
* @brief Defines the DecklistCardNode class, representing a single card entry
|
||||
* in the deck list tree.
|
||||
*
|
||||
* DecklistCardNode is the concrete data-bearing node that corresponds to
|
||||
* an individual card entry in a deck. It stores the card’s name, quantity,
|
||||
* set information, and provider ID. These nodes live inside an
|
||||
* InnerDecklistNode (e.g., under Mainboard → Group → Card).
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_DECK_LIST_CARD_NODE_H
|
||||
#define COCKATRICE_DECK_LIST_CARD_NODE_H
|
||||
|
||||
#include "abstract_deck_list_card_node.h"
|
||||
#include "card_ref.h"
|
||||
|
||||
/**
|
||||
* @class DecklistCardNode
|
||||
* @ingroup DeckModels
|
||||
* @brief Concrete node type representing an actual card entry in the deck.
|
||||
*
|
||||
* This class extends AbstractDecklistCardNode to hold all information
|
||||
* needed to uniquely identify a card printing within the deck.
|
||||
*
|
||||
* ### Role in the hierarchy:
|
||||
* - Child of an InnerDecklistNode (which groups cards by zone or criteria).
|
||||
* - Leaf node in the deck tree; it does not contain further children.
|
||||
*
|
||||
* ### Data stored:
|
||||
* - @c name: Card’s display name.
|
||||
* - @c number: Quantity of this card in the deck.
|
||||
* - @c cardSetShortName: Abbreviation of the set (e.g., "NEO" for Neon Dynasty).
|
||||
* - @c cardSetNumber: Collector number within the set.
|
||||
* - @c cardProviderId: External provider identifier (e.g., UUID or MTGJSON ID).
|
||||
*
|
||||
* ### Usage:
|
||||
* - Constructed directly when building a deck list from user input or file.
|
||||
* - Used by DeckListModel to present cards in Qt views.
|
||||
* - Convertible to @c CardRef for database lookups or cross-references.
|
||||
*
|
||||
* ### Ownership:
|
||||
* - Owned by its parent InnerDecklistNode.
|
||||
* - Destroyed automatically when its parent is destroyed.
|
||||
*/
|
||||
class DecklistCardNode : public AbstractDecklistCardNode
|
||||
{
|
||||
QString name; ///< Display name of the card.
|
||||
int number; ///< Quantity of this card in the deck.
|
||||
QString cardSetShortName; ///< Short set code (e.g., "NEO").
|
||||
QString cardSetNumber; ///< Collector number within the set.
|
||||
QString cardProviderId; ///< External provider identifier (e.g., UUID).
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new DecklistCardNode.
|
||||
*
|
||||
* @param _name Display name of the card.
|
||||
* @param _number Quantity of this card (default = 1).
|
||||
* @param _parent Parent node in the tree (zone or group). May be nullptr.
|
||||
* @param position Index to insert into parent’s children. -1 = append.
|
||||
* @param _cardSetShortName Short set code (e.g., "NEO").
|
||||
* @param _cardSetNumber Collector number within the set.
|
||||
* @param _cardProviderId External provider ID (e.g., UUID).
|
||||
*
|
||||
* On construction, if a parent is provided, this node is inserted into
|
||||
* the parent’s children list automatically.
|
||||
*/
|
||||
explicit DecklistCardNode(QString _name = QString(),
|
||||
int _number = 1,
|
||||
InnerDecklistNode *_parent = nullptr,
|
||||
int position = -1,
|
||||
QString _cardSetShortName = QString(),
|
||||
QString _cardSetNumber = QString(),
|
||||
QString _cardProviderId = QString())
|
||||
: AbstractDecklistCardNode(_parent, position), name(std::move(_name)), number(_number),
|
||||
cardSetShortName(std::move(_cardSetShortName)), cardSetNumber(std::move(_cardSetNumber)),
|
||||
cardProviderId(std::move(_cardProviderId))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor with new parent assignment.
|
||||
* @param other Existing DecklistCardNode to copy.
|
||||
* @param _parent Parent node for the copy.
|
||||
*
|
||||
* Creates a deep copy of the card node’s properties, but attaches
|
||||
* the new instance to a different parent in the tree.
|
||||
*/
|
||||
explicit DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent);
|
||||
|
||||
/// @return The quantity of this card.
|
||||
int getNumber() const override
|
||||
{
|
||||
return number;
|
||||
}
|
||||
|
||||
/// @param _number Set the quantity of this card.
|
||||
void setNumber(int _number) override
|
||||
{
|
||||
number = _number;
|
||||
}
|
||||
|
||||
/// @return The display name of this card.
|
||||
QString getName() const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/// @param _name Set the display name of this card.
|
||||
void setName(const QString &_name) override
|
||||
{
|
||||
name = _name;
|
||||
}
|
||||
|
||||
/// @return The provider identifier for this card.
|
||||
QString getCardProviderId() const override
|
||||
{
|
||||
return cardProviderId;
|
||||
}
|
||||
|
||||
/// @param _providerId Set the provider identifier for this card.
|
||||
void setCardProviderId(const QString &_providerId) override
|
||||
{
|
||||
cardProviderId = _providerId;
|
||||
}
|
||||
|
||||
/// @return The short set code (e.g., "NEO").
|
||||
QString getCardSetShortName() const override
|
||||
{
|
||||
return cardSetShortName;
|
||||
}
|
||||
|
||||
/// @param _cardSetShortName Set the short set code.
|
||||
void setCardSetShortName(const QString &_cardSetShortName) override
|
||||
{
|
||||
cardSetShortName = _cardSetShortName;
|
||||
}
|
||||
|
||||
/// @return The collector number of this card within its set.
|
||||
QString getCardCollectorNumber() const override
|
||||
{
|
||||
return cardSetNumber;
|
||||
}
|
||||
|
||||
/// @param _cardSetNumber Set the collector number.
|
||||
void setCardCollectorNumber(const QString &_cardSetNumber) override
|
||||
{
|
||||
cardSetNumber = _cardSetNumber;
|
||||
}
|
||||
|
||||
/// @return Always false; card nodes are not deck headers.
|
||||
[[nodiscard]] bool isDeckHeader() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert this node to a CardRef.
|
||||
*
|
||||
* @return A CardRef with the card’s name and provider ID, suitable
|
||||
* for database lookups or comparison with other card sources.
|
||||
*/
|
||||
CardRef toCardRef() const
|
||||
{
|
||||
return {name, cardProviderId};
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_DECK_LIST_CARD_NODE_H
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
#include "expression.h"
|
||||
|
||||
#include "./lib/peglib.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QtMath>
|
||||
#include <functional>
|
||||
|
||||
peg::parser math(R"(
|
||||
EXPRESSION <- P0
|
||||
P0 <- P1 (P1_OPERATOR P1)*
|
||||
P1 <- P2 (P2_OPERATOR P2)*
|
||||
P2 <- P3 (P3_OPERATOR P3)*
|
||||
P3 <- NUMBER / FUNCTION / VARIABLE / '(' P0 ')'
|
||||
|
||||
P1_OPERATOR <- < [-+] >
|
||||
P2_OPERATOR <- < [/*] >
|
||||
P3_OPERATOR <- < '^' >
|
||||
|
||||
NUMBER <- < '-'? [0-9]+ >
|
||||
NAME <- < [a-z][a-z0-9]* >
|
||||
VARIABLE <- < [x] >
|
||||
FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')'
|
||||
|
||||
%whitespace <- [ \t\r]*
|
||||
)");
|
||||
|
||||
QMap<QString, std::function<double(double)>> *default_functions = nullptr;
|
||||
|
||||
Expression::Expression(double initial) : value(initial)
|
||||
{
|
||||
if (default_functions == nullptr) {
|
||||
default_functions = new QMap<QString, std::function<double(double)>>();
|
||||
default_functions->insert("abs", [](double a) { return qFabs(a); });
|
||||
default_functions->insert("ceil", [](double a) { return qCeil(a); });
|
||||
default_functions->insert("cos", [](double a) { return qCos(a); });
|
||||
default_functions->insert("floor", [](double a) { return qFloor(a); });
|
||||
default_functions->insert("log", [](double a) { return qLn(a); });
|
||||
default_functions->insert("log10", [](double a) { return qLn(a); });
|
||||
default_functions->insert("round", [](double a) { return qRound(a); });
|
||||
default_functions->insert("sin", [](double a) { return qSin(a); });
|
||||
default_functions->insert("sqrt", [](double a) { return qSqrt(a); });
|
||||
default_functions->insert("tan", [](double a) { return qTan(a); });
|
||||
default_functions->insert("trunc", [](double a) { return std::trunc(a); });
|
||||
}
|
||||
fns = QMap<QString, std::function<double(double)>>(*default_functions);
|
||||
}
|
||||
|
||||
double Expression::eval(const peg::Ast &ast)
|
||||
{
|
||||
const auto &nodes = ast.nodes;
|
||||
if (ast.name == "NUMBER") {
|
||||
return stod(std::string(ast.token));
|
||||
} else if (ast.name == "FUNCTION") {
|
||||
QString name = QString::fromStdString(std::string(nodes[0]->token));
|
||||
if (!fns.contains(name))
|
||||
return 0;
|
||||
return fns[name](eval(*nodes[1]));
|
||||
} else if (ast.name == "VARIABLE") {
|
||||
return value;
|
||||
} else if (ast.name[0] == 'P') {
|
||||
double result = eval(*nodes[0]);
|
||||
for (unsigned int i = 1; i < nodes.size(); i += 2) {
|
||||
double arg = eval(*nodes[i + 1]);
|
||||
char operation = nodes[i]->token[0];
|
||||
switch (operation) {
|
||||
case '+':
|
||||
result += arg;
|
||||
break;
|
||||
case '-':
|
||||
result -= arg;
|
||||
break;
|
||||
case '*':
|
||||
result *= arg;
|
||||
break;
|
||||
case '/':
|
||||
result /= arg;
|
||||
break;
|
||||
case '^':
|
||||
result = qPow(result, arg);
|
||||
break;
|
||||
default:
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
double Expression::parse(const QString &expr)
|
||||
{
|
||||
QByteArray ba = expr.toUtf8();
|
||||
|
||||
math.enable_ast();
|
||||
|
||||
std::shared_ptr<peg::Ast> ast;
|
||||
if (math.parse(ba.data(), ast)) {
|
||||
ast = peg::AstOptimizer(true).optimize(ast);
|
||||
return eval(*ast);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#ifndef EXPRESSION_H
|
||||
#define EXPRESSION_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
|
||||
namespace peg
|
||||
{
|
||||
template <typename Annotation> struct AstBase;
|
||||
struct EmptyType;
|
||||
typedef AstBase<EmptyType> Ast;
|
||||
} // namespace peg
|
||||
|
||||
class Expression
|
||||
{
|
||||
public:
|
||||
double value;
|
||||
|
||||
explicit Expression(double initial = 0);
|
||||
double parse(const QString &expr);
|
||||
|
||||
private:
|
||||
double eval(const peg::Ast &ast);
|
||||
QMap<QString, std::function<double(double)>> fns;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#include "featureset.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMap>
|
||||
|
||||
FeatureSet::FeatureSet()
|
||||
{
|
||||
}
|
||||
|
||||
QMap<QString, bool> FeatureSet::getDefaultFeatureList()
|
||||
{
|
||||
initalizeFeatureList(featureList);
|
||||
return featureList;
|
||||
}
|
||||
|
||||
void FeatureSet::initalizeFeatureList(QMap<QString, bool> &_featureList)
|
||||
{
|
||||
// default features [name], [is required to connect]
|
||||
_featureList.insert("client_id", false);
|
||||
_featureList.insert("client_ver", false);
|
||||
_featureList.insert("feature_set", false);
|
||||
_featureList.insert("user_ban_history", false);
|
||||
_featureList.insert("room_chat_history", false);
|
||||
_featureList.insert("client_warnings", false);
|
||||
_featureList.insert("mod_log_lookup", false);
|
||||
_featureList.insert("idle_client", false);
|
||||
_featureList.insert("forgot_password", false);
|
||||
_featureList.insert("websocket", false);
|
||||
// featureList.insert("hashed_password_login", false);
|
||||
// These are temp to force users onto a newer client
|
||||
_featureList.insert("2.7.0_min_version", false);
|
||||
_featureList.insert("2.8.0_min_version", false);
|
||||
}
|
||||
|
||||
void FeatureSet::enableRequiredFeature(QMap<QString, bool> &_featureList, const QString &featureName)
|
||||
{
|
||||
if (_featureList.contains(featureName))
|
||||
_featureList.insert(featureName, true);
|
||||
}
|
||||
|
||||
void FeatureSet::disableRequiredFeature(QMap<QString, bool> &_featureList, const QString &featureName)
|
||||
{
|
||||
if (_featureList.contains(featureName))
|
||||
_featureList.insert(featureName, false);
|
||||
}
|
||||
|
||||
QMap<QString, bool>
|
||||
FeatureSet::addFeature(QMap<QString, bool> &_featureList, const QString &featureName, bool isFeatureRequired)
|
||||
{
|
||||
_featureList.insert(featureName, isFeatureRequired);
|
||||
return _featureList;
|
||||
}
|
||||
|
||||
QMap<QString, bool> FeatureSet::identifyMissingFeatures(const QMap<QString, bool> &suppliedFeatures,
|
||||
QMap<QString, bool> requiredFeatures)
|
||||
{
|
||||
QMap<QString, bool> missingList;
|
||||
QMap<QString, bool>::iterator i;
|
||||
for (i = requiredFeatures.begin(); i != requiredFeatures.end(); ++i) {
|
||||
if (!suppliedFeatures.contains(i.key())) {
|
||||
missingList.insert(i.key(), i.value());
|
||||
}
|
||||
}
|
||||
return missingList;
|
||||
}
|
||||
|
||||
bool FeatureSet::isRequiredFeaturesMissing(const QMap<QString, bool> &suppliedFeatures,
|
||||
QMap<QString, bool> requiredFeatures)
|
||||
{
|
||||
QMap<QString, bool>::iterator i;
|
||||
for (i = requiredFeatures.begin(); i != requiredFeatures.end(); ++i) {
|
||||
if (i.value() && suppliedFeatures.contains(i.key())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#ifndef FEATURESET_H
|
||||
#define FEATURESET_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
|
||||
class FeatureSet
|
||||
{
|
||||
public:
|
||||
FeatureSet();
|
||||
QMap<QString, bool> getDefaultFeatureList();
|
||||
void initalizeFeatureList(QMap<QString, bool> &_featureList);
|
||||
void enableRequiredFeature(QMap<QString, bool> &_featureList, const QString &featureName);
|
||||
void disableRequiredFeature(QMap<QString, bool> &_featureList, const QString &featureName);
|
||||
QMap<QString, bool>
|
||||
addFeature(QMap<QString, bool> &_featureList, const QString &featureName, bool isFeatureRequired);
|
||||
QMap<QString, bool> identifyMissingFeatures(const QMap<QString, bool> &featureListToCheck,
|
||||
QMap<QString, bool> featureListToCompareTo);
|
||||
bool isRequiredFeaturesMissing(const QMap<QString, bool> &featureListToCheck,
|
||||
QMap<QString, bool> featureListToCompareTo);
|
||||
|
||||
private:
|
||||
QMap<QString, bool> featureList;
|
||||
};
|
||||
|
||||
#endif // FEEATURESET_H
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#include "get_pb_extension.h"
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
int getPbExtension(const ::google::protobuf::Message &message)
|
||||
{
|
||||
std::vector<const ::google::protobuf::FieldDescriptor *> fieldList;
|
||||
message.GetReflection()->ListFields(message, &fieldList);
|
||||
for (unsigned int j = 0; j < fieldList.size(); ++j)
|
||||
if (fieldList[j]->is_extension())
|
||||
return fieldList[j]->number();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef GET_PB_EXTENSION_H
|
||||
#define GET_PB_EXTENSION_H
|
||||
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
} // namespace google
|
||||
|
||||
int getPbExtension(const ::google::protobuf::Message &message);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
#include "inner_deck_list_node.h"
|
||||
|
||||
#include "deck_list_card_node.h"
|
||||
|
||||
InnerDecklistNode::InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent)
|
||||
: AbstractDecklistNode(_parent), name(other->getName())
|
||||
{
|
||||
for (int i = 0; i < other->size(); ++i) {
|
||||
auto *inner = dynamic_cast<InnerDecklistNode *>(other->at(i));
|
||||
if (inner) {
|
||||
new InnerDecklistNode(inner, this);
|
||||
} else {
|
||||
new DecklistCardNode(dynamic_cast<DecklistCardNode *>(other->at(i)), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InnerDecklistNode::~InnerDecklistNode()
|
||||
{
|
||||
clearTree();
|
||||
}
|
||||
|
||||
QString InnerDecklistNode::visibleNameFromName(const QString &_name)
|
||||
{
|
||||
if (_name == DECK_ZONE_MAIN) {
|
||||
return QObject::tr("Maindeck");
|
||||
} else if (_name == DECK_ZONE_SIDE) {
|
||||
return QObject::tr("Sideboard");
|
||||
} else if (_name == DECK_ZONE_TOKENS) {
|
||||
return QObject::tr("Tokens");
|
||||
} else {
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
void InnerDecklistNode::setSortMethod(DeckSortMethod method)
|
||||
{
|
||||
sortMethod = method;
|
||||
for (int i = 0; i < size(); i++) {
|
||||
at(i)->setSortMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
QString InnerDecklistNode::getVisibleName() const
|
||||
{
|
||||
return visibleNameFromName(name);
|
||||
}
|
||||
|
||||
void InnerDecklistNode::clearTree()
|
||||
{
|
||||
for (int i = 0; i < size(); i++)
|
||||
delete at(i);
|
||||
clear();
|
||||
}
|
||||
|
||||
AbstractDecklistNode *InnerDecklistNode::findChild(const QString &_name)
|
||||
{
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (at(i)->getName() == _name) {
|
||||
return at(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AbstractDecklistNode *InnerDecklistNode::findCardChildByNameProviderIdAndNumber(const QString &_name,
|
||||
const QString &_providerId,
|
||||
const QString &_cardNumber)
|
||||
{
|
||||
for (const auto &i : *this) {
|
||||
if (!i || i->getName() != _name) {
|
||||
continue;
|
||||
}
|
||||
if (_cardNumber != "" && i->getCardCollectorNumber() != _cardNumber) {
|
||||
continue;
|
||||
}
|
||||
if (_providerId != "" && i->getCardProviderId() != _providerId) {
|
||||
continue;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int InnerDecklistNode::height() const
|
||||
{
|
||||
return at(0)->height() + 1;
|
||||
}
|
||||
|
||||
int InnerDecklistNode::recursiveCount(bool countTotalCards) const
|
||||
{
|
||||
int result = 0;
|
||||
for (int i = 0; i < size(); i++) {
|
||||
auto *node = dynamic_cast<InnerDecklistNode *>(at(i));
|
||||
|
||||
if (node) {
|
||||
result += node->recursiveCount(countTotalCards);
|
||||
} else if (countTotalCards) {
|
||||
result += dynamic_cast<AbstractDecklistCardNode *>(at(i))->getNumber();
|
||||
} else {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InnerDecklistNode::compare(AbstractDecklistNode *other) const
|
||||
{
|
||||
switch (sortMethod) {
|
||||
case ByNumber:
|
||||
return compareNumber(other);
|
||||
case ByName:
|
||||
return compareName(other);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InnerDecklistNode::compareNumber(AbstractDecklistNode *other) const
|
||||
{
|
||||
auto *other2 = dynamic_cast<InnerDecklistNode *>(other);
|
||||
if (other2) {
|
||||
int n1 = recursiveCount(true);
|
||||
int n2 = other2->recursiveCount(true);
|
||||
return (n1 != n2) ? (n1 > n2) : compareName(other);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InnerDecklistNode::compareName(AbstractDecklistNode *other) const
|
||||
{
|
||||
auto *other2 = dynamic_cast<InnerDecklistNode *>(other);
|
||||
if (other2) {
|
||||
return (getName() > other2->getName());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InnerDecklistNode::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
const QString childName = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName == "zone") {
|
||||
InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this);
|
||||
newZone->readElement(xml);
|
||||
} else if (childName == "card") {
|
||||
DecklistCardNode *newCard = new DecklistCardNode(
|
||||
xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(),
|
||||
this, -1, xml->attributes().value("setShortName").toString(),
|
||||
xml->attributes().value("collectorNumber").toString(), xml->attributes().value("uuid").toString());
|
||||
newCard->readElement(xml);
|
||||
}
|
||||
} else if (xml->isEndElement() && (childName == "zone"))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InnerDecklistNode::writeElement(QXmlStreamWriter *xml)
|
||||
{
|
||||
xml->writeStartElement("zone");
|
||||
xml->writeAttribute("name", name);
|
||||
for (int i = 0; i < size(); i++)
|
||||
at(i)->writeElement(xml);
|
||||
xml->writeEndElement(); // zone
|
||||
}
|
||||
|
||||
QVector<QPair<int, int>> InnerDecklistNode::sort(Qt::SortOrder order)
|
||||
{
|
||||
QVector<QPair<int, int>> result(size());
|
||||
|
||||
// Initialize temporary list with contents of current list
|
||||
QVector<QPair<int, AbstractDecklistNode *>> tempList(size());
|
||||
for (int i = size() - 1; i >= 0; --i) {
|
||||
tempList[i].first = i;
|
||||
tempList[i].second = at(i);
|
||||
}
|
||||
|
||||
// Sort temporary list
|
||||
auto cmp = [order](const auto &a, const auto &b) {
|
||||
return (order == Qt::AscendingOrder) ? (b.second->compare(a.second)) : (a.second->compare(b.second));
|
||||
};
|
||||
|
||||
std::sort(tempList.begin(), tempList.end(), cmp);
|
||||
|
||||
// Map old indexes to new indexes and
|
||||
// copy temporary list to the current one
|
||||
for (int i = size() - 1; i >= 0; --i) {
|
||||
result[i].first = tempList[i].first;
|
||||
result[i].second = i;
|
||||
replace(i, tempList[i].second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
/**
|
||||
* @file inner_deck_list_node.h
|
||||
* @brief Defines the InnerDecklistNode class, which represents
|
||||
* structural nodes (zones and groups) in the deck tree.
|
||||
*
|
||||
* The deck tree consists of:
|
||||
* - A root node (invisible).
|
||||
* - Zones (Main, Sideboard, Tokens).
|
||||
* - Optional grouping nodes (e.g., by type, color, or mana cost).
|
||||
* - Card nodes as leaves.
|
||||
*
|
||||
* InnerDecklistNode implements the zone/group nodes and provides
|
||||
* storage and management of child nodes.
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_INNER_DECK_LIST_NODE_H
|
||||
#define COCKATRICE_INNER_DECK_LIST_NODE_H
|
||||
|
||||
#include "abstract_deck_list_node.h"
|
||||
|
||||
/// Constant for the "main" deck zone name.
|
||||
#define DECK_ZONE_MAIN "main"
|
||||
/// Constant for the "sideboard" zone name.
|
||||
#define DECK_ZONE_SIDE "side"
|
||||
/// Constant for the "tokens" zone name.
|
||||
#define DECK_ZONE_TOKENS "tokens"
|
||||
|
||||
/**
|
||||
* @class InnerDecklistNode
|
||||
* @brief Represents a container node in the deck list hierarchy
|
||||
* (zones and groupings).
|
||||
*
|
||||
* Unlike DecklistCardNode, which holds leaf card data, this class
|
||||
* manages collections of child nodes, which may themselves be
|
||||
* InnerDecklistNode or DecklistCardNode objects.
|
||||
*
|
||||
* ### Role in the hierarchy:
|
||||
* - Root node (invisible): Holds zones.
|
||||
* - Zone nodes: "main", "side", "tokens".
|
||||
* - Grouping nodes: Created dynamically when grouping by type,
|
||||
* color, or mana cost.
|
||||
* - Card nodes: Always children of an InnerDecklistNode.
|
||||
*
|
||||
* ### Design notes:
|
||||
* - Inherits from AbstractDecklistNode (tree interface) and
|
||||
* QList<AbstractDecklistNode*> (storage of children).
|
||||
* This allows direct QList-style manipulation of children while
|
||||
* still presenting a polymorphic node interface.
|
||||
*
|
||||
* ### Responsibilities:
|
||||
* - Store a display name.
|
||||
* - Own and manage child nodes (insert, clear, find).
|
||||
* - Provide recursive operations such as counting cards or computing height.
|
||||
* - Implement sorting logic for reordering children.
|
||||
* - Implement XML serialization for persistence.
|
||||
*
|
||||
* ### Ownership:
|
||||
* - Owns all child nodes stored in the QList. The destructor
|
||||
* recursively deletes children.
|
||||
*/
|
||||
class InnerDecklistNode : public AbstractDecklistNode, public QList<AbstractDecklistNode *>
|
||||
{
|
||||
QString name; ///< Internal identifier for this node (zone or group name).
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new InnerDecklistNode.
|
||||
*
|
||||
* @param _name Internal name (e.g., "main", "side", "tokens", or group label).
|
||||
* @param _parent Parent node (may be nullptr for the root).
|
||||
* @param position Optional index for insertion into parent. -1 = append.
|
||||
*/
|
||||
explicit InnerDecklistNode(QString _name = QString(), InnerDecklistNode *_parent = nullptr, int position = -1)
|
||||
: AbstractDecklistNode(_parent, position), name(std::move(_name))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor with parent reassignment.
|
||||
* @param other Node to copy from (deep copy of children).
|
||||
* @param _parent Parent node for the copy.
|
||||
*/
|
||||
explicit InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destructor. Recursively deletes all child nodes.
|
||||
*/
|
||||
~InnerDecklistNode() override;
|
||||
|
||||
/**
|
||||
* @brief Set the sorting method for this node and all children.
|
||||
* @param method Sort method to apply recursively.
|
||||
*/
|
||||
void setSortMethod(DeckSortMethod method) override;
|
||||
|
||||
/// @return The internal name of this node.
|
||||
[[nodiscard]] QString getName() const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/// @param _name Set the internal name of this node.
|
||||
void setName(const QString &_name)
|
||||
{
|
||||
name = _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Translate an internal name into a user-visible name.
|
||||
*
|
||||
* For example, the internal string "main" is presented as
|
||||
* "Mainboard" in the UI.
|
||||
*
|
||||
* @param _name Internal identifier.
|
||||
* @return Display-friendly string.
|
||||
*/
|
||||
static QString visibleNameFromName(const QString &_name);
|
||||
|
||||
/**
|
||||
* @brief Get this node’s display-friendly name.
|
||||
* @return Human-readable name (zone/group name).
|
||||
*/
|
||||
[[nodiscard]] virtual QString getVisibleName() const;
|
||||
|
||||
/// @return Always empty for container nodes.
|
||||
[[nodiscard]] QString getCardProviderId() const override
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/// @return Always empty for container nodes.
|
||||
[[nodiscard]] QString getCardSetShortName() const override
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/// @return Always empty for container nodes.
|
||||
[[nodiscard]] QString getCardCollectorNumber() const override
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/// @return Always true; InnerDecklistNode represents deck structure.
|
||||
[[nodiscard]] bool isDeckHeader() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete all children of this node, recursively.
|
||||
*/
|
||||
void clearTree();
|
||||
|
||||
/**
|
||||
* @brief Find a direct child node by name.
|
||||
* @param _name Name to match.
|
||||
* @return Pointer to child node, or nullptr if not found.
|
||||
*/
|
||||
AbstractDecklistNode *findChild(const QString &_name);
|
||||
|
||||
/**
|
||||
* @brief Find a child card node by name, provider ID, and collector number.
|
||||
*
|
||||
* Searches immediate children only.
|
||||
*
|
||||
* @param _name Card name to match.
|
||||
* @param _providerId Optional provider ID to match.
|
||||
* @param _cardNumber Optional collector number to match.
|
||||
* @return Pointer to child node if found, nullptr otherwise.
|
||||
*/
|
||||
AbstractDecklistNode *findCardChildByNameProviderIdAndNumber(const QString &_name,
|
||||
const QString &_providerId = "",
|
||||
const QString &_cardNumber = "");
|
||||
|
||||
/**
|
||||
* @brief Compute the height of this node.
|
||||
* @return Maximum depth of descendants + 1.
|
||||
*/
|
||||
int height() const override;
|
||||
|
||||
/**
|
||||
* @brief Count cards recursively under this node.
|
||||
* @param countTotalCards If true, sums up quantities of cards.
|
||||
* If false, counts unique card nodes.
|
||||
* @return Total count.
|
||||
*/
|
||||
int recursiveCount(bool countTotalCards = false) const;
|
||||
|
||||
/**
|
||||
* @brief Compare this node against another for sorting.
|
||||
*
|
||||
* Uses current @c sortMethod to determine the comparison.
|
||||
*
|
||||
* @param other Node to compare.
|
||||
* @return true if this node should sort before @p other.
|
||||
*/
|
||||
bool compare(AbstractDecklistNode *other) const override;
|
||||
|
||||
/// @copydoc compare(AbstractDecklistNode*) const
|
||||
bool compareNumber(AbstractDecklistNode *other) const;
|
||||
|
||||
/// @copydoc compare(AbstractDecklistNode*) const
|
||||
bool compareName(AbstractDecklistNode *other) const;
|
||||
|
||||
/**
|
||||
* @brief Sort this node’s children recursively.
|
||||
*
|
||||
* @param order Ascending or descending.
|
||||
* @return A QVector of (oldIndex, newIndex) pairs indicating
|
||||
* how children were reordered.
|
||||
*/
|
||||
QVector<QPair<int, int>> sort(Qt::SortOrder order = Qt::AscendingOrder);
|
||||
|
||||
/**
|
||||
* @brief Deserialize this node and its children from XML.
|
||||
* @param xml Reader positioned at this element.
|
||||
* @return true if parsing succeeded.
|
||||
*/
|
||||
bool readElement(QXmlStreamReader *xml) override;
|
||||
|
||||
/**
|
||||
* @brief Serialize this node and its children to XML.
|
||||
* @param xml Writer to append elements to.
|
||||
*/
|
||||
void writeElement(QXmlStreamWriter *xml) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_INNER_DECK_LIST_NODE_H
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#include "passwordhasher.h"
|
||||
|
||||
#include "rng_sfmt.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
|
||||
QString PasswordHasher::computeHash(const QString &password, const QString &salt)
|
||||
{
|
||||
QCryptographicHash::Algorithm algo = QCryptographicHash::Sha512;
|
||||
const int rounds = 1000;
|
||||
|
||||
QByteArray hash = (salt + password).toUtf8();
|
||||
for (int i = 0; i < rounds; ++i) {
|
||||
hash = QCryptographicHash::hash(hash, algo);
|
||||
}
|
||||
QString hashedPass = salt + QString(hash.toBase64());
|
||||
return hashedPass;
|
||||
}
|
||||
|
||||
QString PasswordHasher::generateRandomSalt(const int len)
|
||||
{
|
||||
static const char alphanum[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
QString ret;
|
||||
int size = sizeof(alphanum) - 1;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
ret.append(alphanum[rng->rand(0, size)]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString PasswordHasher::generateActivationToken()
|
||||
{
|
||||
return QCryptographicHash::hash(generateRandomSalt().toUtf8(), QCryptographicHash::Md5).toBase64().left(16);
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef PASSWORDHASHER_H
|
||||
#define PASSWORDHASHER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class PasswordHasher
|
||||
{
|
||||
public:
|
||||
static QString computeHash(const QString &password, const QString &salt);
|
||||
static QString generateRandomSalt(const int len = 16);
|
||||
static QString generateActivationToken();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
# CMakeLists for common directory
|
||||
#
|
||||
# provides the protobuf interfaces
|
||||
|
||||
set(PROTO_FILES
|
||||
admin_commands.proto
|
||||
card_attributes.proto
|
||||
color.proto
|
||||
command_attach_card.proto
|
||||
command_change_zone_properties.proto
|
||||
command_concede.proto
|
||||
command_create_arrow.proto
|
||||
command_create_counter.proto
|
||||
command_create_token.proto
|
||||
command_deck_del.proto
|
||||
command_deck_del_dir.proto
|
||||
command_deck_download.proto
|
||||
command_deck_list.proto
|
||||
command_deck_new_dir.proto
|
||||
command_deck_select.proto
|
||||
command_deck_upload.proto
|
||||
command_del_counter.proto
|
||||
command_delete_arrow.proto
|
||||
command_draw_cards.proto
|
||||
command_dump_zone.proto
|
||||
command_flip_card.proto
|
||||
command_game_say.proto
|
||||
command_inc_card_counter.proto
|
||||
command_inc_counter.proto
|
||||
command_kick_from_game.proto
|
||||
command_leave_game.proto
|
||||
command_move_card.proto
|
||||
command_mulligan.proto
|
||||
command_next_turn.proto
|
||||
command_ready_start.proto
|
||||
command_replay_delete_match.proto
|
||||
command_replay_download.proto
|
||||
command_replay_get_code.proto
|
||||
command_replay_list.proto
|
||||
command_replay_modify_match.proto
|
||||
command_replay_submit_code.proto
|
||||
command_reveal_cards.proto
|
||||
command_reverse_turn.proto
|
||||
command_roll_die.proto
|
||||
command_set_active_phase.proto
|
||||
command_set_card_attr.proto
|
||||
command_set_card_counter.proto
|
||||
command_set_counter.proto
|
||||
command_set_sideboard_lock.proto
|
||||
command_set_sideboard_plan.proto
|
||||
command_shuffle.proto
|
||||
command_undo_draw.proto
|
||||
commands.proto
|
||||
context_concede.proto
|
||||
context_connection_state_changed.proto
|
||||
context_deck_select.proto
|
||||
context_move_card.proto
|
||||
context_mulligan.proto
|
||||
context_ping_changed.proto
|
||||
context_ready_start.proto
|
||||
context_set_sideboard_lock.proto
|
||||
context_undo_draw.proto
|
||||
event_add_to_list.proto
|
||||
event_attach_card.proto
|
||||
event_change_zone_properties.proto
|
||||
event_connection_closed.proto
|
||||
event_create_arrow.proto
|
||||
event_create_counter.proto
|
||||
event_create_token.proto
|
||||
event_del_counter.proto
|
||||
event_delete_arrow.proto
|
||||
event_destroy_card.proto
|
||||
event_draw_cards.proto
|
||||
event_dump_zone.proto
|
||||
event_flip_card.proto
|
||||
event_game_closed.proto
|
||||
event_game_host_changed.proto
|
||||
event_game_joined.proto
|
||||
event_game_say.proto
|
||||
event_game_state_changed.proto
|
||||
event_game_state_changed.proto
|
||||
event_join.proto
|
||||
event_join_room.proto
|
||||
event_kicked.proto
|
||||
event_leave.proto
|
||||
event_leave_room.proto
|
||||
event_list_games.proto
|
||||
event_list_rooms.proto
|
||||
event_move_card.proto
|
||||
event_notify_user.proto
|
||||
event_player_properties_changed.proto
|
||||
event_remove_from_list.proto
|
||||
event_remove_messages.proto
|
||||
event_replay_added.proto
|
||||
event_reveal_cards.proto
|
||||
event_reverse_turn.proto
|
||||
event_roll_die.proto
|
||||
event_room_say.proto
|
||||
event_server_complete_list.proto
|
||||
event_server_identification.proto
|
||||
event_server_message.proto
|
||||
event_server_shutdown.proto
|
||||
event_set_active_phase.proto
|
||||
event_set_active_player.proto
|
||||
event_set_card_attr.proto
|
||||
event_set_card_counter.proto
|
||||
event_set_counter.proto
|
||||
event_shuffle.proto
|
||||
event_user_joined.proto
|
||||
event_user_left.proto
|
||||
event_user_message.proto
|
||||
game_commands.proto
|
||||
game_event.proto
|
||||
game_event_container.proto
|
||||
game_event_context.proto
|
||||
game_replay.proto
|
||||
isl_message.proto
|
||||
moderator_commands.proto
|
||||
move_card_to_zone.proto
|
||||
response.proto
|
||||
response_activate.proto
|
||||
response_adjust_mod.proto
|
||||
response_ban_history.proto
|
||||
response_deck_download.proto
|
||||
response_deck_list.proto
|
||||
response_deck_upload.proto
|
||||
response_dump_zone.proto
|
||||
response_forgotpasswordrequest.proto
|
||||
response_get_admin_notes.proto
|
||||
response_get_games_of_user.proto
|
||||
response_get_user_info.proto
|
||||
response_join_room.proto
|
||||
response_list_users.proto
|
||||
response_login.proto
|
||||
response_password_salt.proto
|
||||
response_register.proto
|
||||
response_replay_download.proto
|
||||
response_replay_get_code.proto
|
||||
response_replay_list.proto
|
||||
response_viewlog_history.proto
|
||||
response_warn_history.proto
|
||||
response_warn_list.proto
|
||||
room_commands.proto
|
||||
room_event.proto
|
||||
server_message.proto
|
||||
serverinfo_arrow.proto
|
||||
serverinfo_ban.proto
|
||||
serverinfo_card.proto
|
||||
serverinfo_cardcounter.proto
|
||||
serverinfo_chat_message.proto
|
||||
serverinfo_counter.proto
|
||||
serverinfo_deckstorage.proto
|
||||
serverinfo_game.proto
|
||||
serverinfo_gametype.proto
|
||||
serverinfo_player.proto
|
||||
serverinfo_playerping.proto
|
||||
serverinfo_playerproperties.proto
|
||||
serverinfo_replay.proto
|
||||
serverinfo_replay_match.proto
|
||||
serverinfo_room.proto
|
||||
serverinfo_user.proto
|
||||
serverinfo_warning.proto
|
||||
serverinfo_zone.proto
|
||||
session_commands.proto
|
||||
session_event.proto
|
||||
)
|
||||
|
||||
if(${Protobuf_VERSION} VERSION_LESS "3.21.0.0")
|
||||
message(STATUS "Using Protobuf Legacy Mode")
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES})
|
||||
|
||||
add_library(cockatrice_protocol ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
set(cockatrice_protocol_LIBS ${PROTOBUF_LIBRARIES})
|
||||
if(UNIX)
|
||||
set(cockatrice_protocol_LIBS ${cockatrice_protocol_LIBS} -lpthread)
|
||||
endif(UNIX)
|
||||
target_link_libraries(cockatrice_protocol ${cockatrice_protocol_LIBS})
|
||||
|
||||
# ubuntu uses an outdated package for protobuf, 3.1.0 is required
|
||||
if(${Protobuf_VERSION} VERSION_LESS "3.1.0")
|
||||
# remove unused parameter and misleading indentation warnings when compiling to avoid errors
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-unused-parameter -Wno-misleading-indentation")
|
||||
message(WARNING "Older protobuf version found (${Protobuf_VERSION} < 3.1.0), "
|
||||
"disabled the warnings 'unused-parameter' and 'misleading-indentation' for protobuf generated code "
|
||||
"to avoid compilation errors."
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
add_library(cockatrice_protocol ${PROTO_FILES})
|
||||
target_link_libraries(cockatrice_protocol PUBLIC protobuf::libprotobuf)
|
||||
set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_include_directories(cockatrice_protocol PUBLIC "${PROTOBUF_INCLUDE_DIRS}")
|
||||
|
||||
protobuf_generate(
|
||||
TARGET cockatrice_protocol IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}" PROTOC_OUT_DIR "${PROTO_BINARY_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
syntax = "proto2";
|
||||
message AdminCommand {
|
||||
enum AdminCommandType {
|
||||
UPDATE_SERVER_MESSAGE = 1000;
|
||||
SHUTDOWN_SERVER = 1001;
|
||||
RELOAD_CONFIG = 1002;
|
||||
ADJUST_MOD = 1003;
|
||||
}
|
||||
extensions 100 to max;
|
||||
}
|
||||
|
||||
message Command_UpdateServerMessage {
|
||||
extend AdminCommand {
|
||||
optional Command_UpdateServerMessage ext = 1000;
|
||||
}
|
||||
}
|
||||
|
||||
message Command_ShutdownServer {
|
||||
extend AdminCommand {
|
||||
optional Command_ShutdownServer ext = 1001;
|
||||
}
|
||||
optional string reason = 1;
|
||||
optional uint32 minutes = 2;
|
||||
}
|
||||
|
||||
message Command_ReloadConfig {
|
||||
extend AdminCommand {
|
||||
optional Command_ReloadConfig ext = 1002;
|
||||
}
|
||||
}
|
||||
|
||||
message Command_AdjustMod {
|
||||
extend AdminCommand {
|
||||
optional Command_AdjustMod ext = 1003;
|
||||
}
|
||||
required string user_name = 1;
|
||||
optional bool should_be_mod = 2;
|
||||
optional bool should_be_judge = 3;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
enum CardAttribute {
|
||||
AttrTapped = 1;
|
||||
AttrAttacking = 2;
|
||||
AttrFaceDown = 3;
|
||||
AttrColor = 4;
|
||||
AttrPT = 5;
|
||||
AttrAnnotation = 6;
|
||||
AttrDoesntUntap = 7;
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
// Container for a 4 component color code
|
||||
message color {
|
||||
// the red component of the color, limited to 256 values
|
||||
optional uint32 r = 1;
|
||||
|
||||
// the green component of the color, limited to 256 values
|
||||
optional uint32 g = 2;
|
||||
|
||||
// the blue component of the color, limited to 256 values
|
||||
optional uint32 b = 3;
|
||||
|
||||
// the opacity component of the color, limited to 256 values
|
||||
optional uint32 a = 4;
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_AttachCard {
|
||||
extend GameCommand {
|
||||
optional Command_AttachCard ext = 1009;
|
||||
}
|
||||
optional string start_zone = 1;
|
||||
optional sint32 card_id = 2 [default = -1];
|
||||
optional sint32 target_player_id = 3 [default = -1];
|
||||
optional string target_zone = 4;
|
||||
optional sint32 target_card_id = 5 [default = -1];
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
|
||||
message Command_ChangeZoneProperties {
|
||||
extend GameCommand {
|
||||
optional Command_ChangeZoneProperties ext = 1031;
|
||||
}
|
||||
optional string zone_name = 1;
|
||||
|
||||
// Reveal top card to all players.
|
||||
optional bool always_reveal_top_card = 10;
|
||||
// reveal top card to the owner.
|
||||
optional bool always_look_at_top_card = 11;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_Concede {
|
||||
extend GameCommand {
|
||||
optional Command_Concede ext = 1017;
|
||||
}
|
||||
}
|
||||
|
||||
message Command_Unconcede {
|
||||
extend GameCommand {
|
||||
optional Command_Unconcede ext = 1032;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
import "color.proto";
|
||||
|
||||
// Command to draw an arrow from cards to either other cards or a player
|
||||
message Command_CreateArrow {
|
||||
extend GameCommand {
|
||||
optional Command_CreateArrow ext = 1011;
|
||||
}
|
||||
// the player that has the card the arrow is drawn from
|
||||
optional sint32 start_player_id = 1 [default = -1];
|
||||
|
||||
// the zone that the card the arrow is drawn from is in
|
||||
optional string start_zone = 2;
|
||||
|
||||
// the id of the card that the arrow is drawn from
|
||||
optional sint32 start_card_id = 3 [default = -1];
|
||||
|
||||
// the player that has the card the arrow is drawn to, or that the arrow is drawn to if not a card
|
||||
optional sint32 target_player_id = 4 [default = -1];
|
||||
|
||||
// the zone that the card the arrow is drawn to is in, the player will be targeted if this is absent
|
||||
optional string target_zone = 5;
|
||||
|
||||
// the id of the card that the arrow is drawn to, the player will be targeted if this is absent
|
||||
optional sint32 target_card_id = 6 [default = -1];
|
||||
|
||||
// the color of the arrow
|
||||
optional color arrow_color = 7;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
import "color.proto";
|
||||
|
||||
message Command_CreateCounter {
|
||||
extend GameCommand {
|
||||
optional Command_CreateCounter ext = 1019;
|
||||
}
|
||||
optional string counter_name = 1;
|
||||
optional color counter_color = 2;
|
||||
optional uint32 radius = 3;
|
||||
optional sint32 value = 4;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
|
||||
message Command_CreateToken {
|
||||
enum TargetMode {
|
||||
// Attach the target to the token
|
||||
ATTACH_TO = 0;
|
||||
// Transform the target into the token
|
||||
TRANSFORM_INTO = 1;
|
||||
}
|
||||
|
||||
extend GameCommand {
|
||||
optional Command_CreateToken ext = 1010;
|
||||
}
|
||||
optional string zone = 1;
|
||||
optional string card_name = 2;
|
||||
optional string color = 3;
|
||||
optional string pt = 4;
|
||||
optional string annotation = 5;
|
||||
optional bool destroy_on_zone_change = 6;
|
||||
optional sint32 x = 7;
|
||||
optional sint32 y = 8;
|
||||
optional string target_zone = 9;
|
||||
optional sint32 target_card_id = 10 [default = -1];
|
||||
|
||||
// What to do with the target card. Ignored if there is no target card.
|
||||
optional TargetMode target_mode = 11;
|
||||
|
||||
optional string card_provider_id = 12;
|
||||
optional bool face_down = 13;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_DeckDel {
|
||||
extend SessionCommand {
|
||||
optional Command_DeckDel ext = 1011;
|
||||
}
|
||||
optional sint32 deck_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_DeckDelDir {
|
||||
extend SessionCommand {
|
||||
optional Command_DeckDelDir ext = 1010;
|
||||
}
|
||||
optional string path = 1;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_DeckDownload {
|
||||
extend SessionCommand {
|
||||
optional Command_DeckDownload ext = 1012;
|
||||
}
|
||||
optional sint32 deck_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_DeckList {
|
||||
extend SessionCommand {
|
||||
optional Command_DeckList ext = 1008;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_DeckNewDir {
|
||||
extend SessionCommand {
|
||||
optional Command_DeckNewDir ext = 1009;
|
||||
}
|
||||
optional string path = 1;
|
||||
optional string dir_name = 2;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_DeckSelect {
|
||||
extend GameCommand {
|
||||
optional Command_DeckSelect ext = 1029;
|
||||
}
|
||||
optional string deck = 1;
|
||||
optional sint32 deck_id = 2 [default = -1];
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_DeckUpload {
|
||||
extend SessionCommand {
|
||||
optional Command_DeckUpload ext = 1013;
|
||||
}
|
||||
optional string path = 1; // to upload a new deck
|
||||
optional uint32 deck_id = 2; // to replace an existing deck
|
||||
optional string deck_list = 3;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_DelCounter {
|
||||
extend GameCommand {
|
||||
optional Command_DelCounter ext = 1021;
|
||||
}
|
||||
optional sint32 counter_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_DeleteArrow {
|
||||
extend GameCommand {
|
||||
optional Command_DeleteArrow ext = 1012;
|
||||
}
|
||||
optional sint32 arrow_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_DrawCards {
|
||||
extend GameCommand {
|
||||
optional Command_DrawCards ext = 1006;
|
||||
}
|
||||
optional uint32 number = 1;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_DumpZone {
|
||||
extend GameCommand {
|
||||
optional Command_DumpZone ext = 1024;
|
||||
}
|
||||
optional sint32 player_id = 1 [default = -1];
|
||||
optional string zone_name = 2;
|
||||
optional sint32 number_cards = 3;
|
||||
optional bool is_reversed = 4 [default = false];
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_FlipCard {
|
||||
extend GameCommand {
|
||||
optional Command_FlipCard ext = 1008;
|
||||
}
|
||||
optional string zone = 1;
|
||||
optional sint32 card_id = 2 [default = -1];
|
||||
optional bool face_down = 3;
|
||||
optional string pt = 4;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_GameSay {
|
||||
extend GameCommand {
|
||||
optional Command_GameSay ext = 1002;
|
||||
}
|
||||
optional string message = 1;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_IncCardCounter {
|
||||
extend GameCommand {
|
||||
optional Command_IncCardCounter ext = 1015;
|
||||
}
|
||||
optional string zone = 1;
|
||||
optional sint32 card_id = 2 [default = -1];
|
||||
optional sint32 counter_id = 3 [default = -1];
|
||||
optional sint32 counter_delta = 4;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_IncCounter {
|
||||
extend GameCommand {
|
||||
optional Command_IncCounter ext = 1018;
|
||||
}
|
||||
optional sint32 counter_id = 1 [default = -1];
|
||||
optional sint32 delta = 2;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_KickFromGame {
|
||||
extend GameCommand {
|
||||
optional Command_KickFromGame ext = 1000;
|
||||
}
|
||||
optional sint32 player_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_LeaveGame {
|
||||
extend GameCommand {
|
||||
optional Command_LeaveGame ext = 1001;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
|
||||
// Container describing a single card to move
|
||||
message CardToMove {
|
||||
// Id of the card in its current zone
|
||||
optional sint32 card_id = 1 [default = -1];
|
||||
|
||||
// Places the card face down, hiding its name
|
||||
optional bool face_down = 2;
|
||||
|
||||
// When moving add this value to the power/toughness field of the card
|
||||
optional string pt = 3;
|
||||
|
||||
// When moving sets the card to be tapped
|
||||
optional bool tapped = 4;
|
||||
}
|
||||
|
||||
// Container of multiple cards to move
|
||||
message ListOfCardsToMove {
|
||||
repeated CardToMove card = 1;
|
||||
}
|
||||
|
||||
// Command to move an amount of cards from one zone to another index/coordinate or another zone
|
||||
message Command_MoveCard {
|
||||
extend GameCommand {
|
||||
optional Command_MoveCard ext = 1027;
|
||||
}
|
||||
|
||||
// The player the zone the cards are in belongs to
|
||||
optional sint32 start_player_id = 1 [default = -1];
|
||||
|
||||
// The zone the cards start in
|
||||
optional string start_zone = 2;
|
||||
|
||||
// List of the cards and their new properties
|
||||
optional ListOfCardsToMove cards_to_move = 3;
|
||||
|
||||
// The player the zone the cards will be moved to belongs to
|
||||
optional sint32 target_player_id = 4 [default = -1];
|
||||
|
||||
// The zone the cards will be moved to
|
||||
optional string target_zone = 5;
|
||||
|
||||
// New x coordinate of the first card in the list
|
||||
optional sint32 x = 6 [default = -1];
|
||||
|
||||
// New y coordinate of the first card in the list
|
||||
optional sint32 y = 7 [default = -1];
|
||||
|
||||
// Inverts the x coordinate to apply from the end of the target zone instead of the start
|
||||
optional bool is_reversed = 8 [default = false];
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_Mulligan {
|
||||
extend GameCommand {
|
||||
optional Command_Mulligan ext = 1004;
|
||||
}
|
||||
optional uint32 number = 7;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_NextTurn {
|
||||
extend GameCommand {
|
||||
optional Command_NextTurn ext = 1022;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_ReadyStart {
|
||||
extend GameCommand {
|
||||
optional Command_ReadyStart ext = 1016;
|
||||
}
|
||||
optional bool ready = 1;
|
||||
optional bool force_start = 2;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplayDeleteMatch {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplayDeleteMatch ext = 1103;
|
||||
}
|
||||
optional sint32 game_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplayDownload {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplayDownload ext = 1101;
|
||||
}
|
||||
optional sint32 replay_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplayGetCode {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplayGetCode ext = 1104;
|
||||
}
|
||||
optional sint32 game_id = 1 [default = -1];
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplayList {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplayList ext = 1100;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplayModifyMatch {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplayModifyMatch ext = 1102;
|
||||
}
|
||||
optional sint32 game_id = 1 [default = -1];
|
||||
optional bool do_not_hide = 2;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplaySubmitCode {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplaySubmitCode ext = 1105;
|
||||
}
|
||||
optional string replay_code = 1;
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_RevealCards {
|
||||
extend GameCommand {
|
||||
optional Command_RevealCards ext = 1026;
|
||||
}
|
||||
optional string zone_name = 1;
|
||||
repeated sint32 card_id = 2 [packed = false];
|
||||
optional sint32 player_id = 3 [default = -1];
|
||||
optional bool grant_write_access = 4;
|
||||
optional sint32 top_cards = 5 [default = -1];
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_ReverseTurn {
|
||||
extend GameCommand {
|
||||
optional Command_ReverseTurn ext = 1034;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_RollDie {
|
||||
extend GameCommand {
|
||||
optional Command_RollDie ext = 1005;
|
||||
}
|
||||
optional uint32 sides = 1;
|
||||
optional uint32 count = 2;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_SetActivePhase {
|
||||
extend GameCommand {
|
||||
optional Command_SetActivePhase ext = 1023;
|
||||
}
|
||||
optional uint32 phase = 1;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
import "card_attributes.proto";
|
||||
|
||||
message Command_SetCardAttr {
|
||||
extend GameCommand {
|
||||
optional Command_SetCardAttr ext = 1013;
|
||||
}
|
||||
optional string zone = 1;
|
||||
optional sint32 card_id = 2 [default = -1];
|
||||
optional CardAttribute attribute = 3;
|
||||
optional string attr_value = 4;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_SetCardCounter {
|
||||
extend GameCommand {
|
||||
optional Command_SetCardCounter ext = 1014;
|
||||
}
|
||||
optional string zone = 1;
|
||||
optional sint32 card_id = 2 [default = -1];
|
||||
optional sint32 counter_id = 3 [default = -1];
|
||||
optional sint32 counter_value = 4;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_SetCounter {
|
||||
extend GameCommand {
|
||||
optional Command_SetCounter ext = 1020;
|
||||
}
|
||||
optional sint32 counter_id = 1 [default = -1];
|
||||
optional sint32 value = 2;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_SetSideboardLock {
|
||||
extend GameCommand {
|
||||
optional Command_SetSideboardLock ext = 1030;
|
||||
}
|
||||
optional bool locked = 1;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
import "move_card_to_zone.proto";
|
||||
|
||||
message Command_SetSideboardPlan {
|
||||
extend GameCommand {
|
||||
optional Command_SetSideboardPlan ext = 1028;
|
||||
}
|
||||
repeated MoveCard_ToZone move_list = 1;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_Shuffle {
|
||||
extend GameCommand {
|
||||
optional Command_Shuffle ext = 1003;
|
||||
}
|
||||
optional string zone_name = 1;
|
||||
optional sint32 start = 2 [default = 0];
|
||||
optional sint32 end = 3 [default = -1];
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
message Command_UndoDraw {
|
||||
extend GameCommand {
|
||||
optional Command_UndoDraw ext = 1007;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
import "game_commands.proto";
|
||||
import "room_commands.proto";
|
||||
import "moderator_commands.proto";
|
||||
import "admin_commands.proto";
|
||||
|
||||
message CommandContainer {
|
||||
optional uint64 cmd_id = 1;
|
||||
|
||||
optional uint32 game_id = 10;
|
||||
optional uint32 room_id = 20;
|
||||
|
||||
repeated SessionCommand session_command = 100;
|
||||
repeated GameCommand game_command = 101;
|
||||
repeated RoomCommand room_command = 102;
|
||||
repeated ModeratorCommand moderator_command = 103;
|
||||
repeated AdminCommand admin_command = 104;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_Concede {
|
||||
extend GameEventContext {
|
||||
optional Context_Concede ext = 1001;
|
||||
}
|
||||
}
|
||||
|
||||
message Context_Unconcede {
|
||||
extend GameEventContext {
|
||||
optional Context_Unconcede ext = 1009;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_ConnectionStateChanged {
|
||||
extend GameEventContext {
|
||||
optional Context_ConnectionStateChanged ext = 1007;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_DeckSelect {
|
||||
extend GameEventContext {
|
||||
optional Context_DeckSelect ext = 1002;
|
||||
}
|
||||
optional string deck_hash = 1;
|
||||
optional int32 sideboard_size = 2 [default = -1];
|
||||
optional string deck_list = 3;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_MoveCard {
|
||||
extend GameEventContext {
|
||||
optional Context_MoveCard ext = 1004;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_Mulligan {
|
||||
extend GameEventContext {
|
||||
optional Context_Mulligan ext = 1005;
|
||||
}
|
||||
optional uint32 number = 1;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_PingChanged {
|
||||
extend GameEventContext {
|
||||
optional Context_PingChanged ext = 1006;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_ReadyStart {
|
||||
extend GameEventContext {
|
||||
optional Context_ReadyStart ext = 1000;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_SetSideboardLock {
|
||||
extend GameEventContext {
|
||||
optional Context_SetSideboardLock ext = 1008;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event_context.proto";
|
||||
|
||||
message Context_UndoDraw {
|
||||
extend GameEventContext {
|
||||
optional Context_UndoDraw ext = 1003;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_event.proto";
|
||||
import "serverinfo_user.proto";
|
||||
|
||||
message Event_AddToList {
|
||||
extend SessionEvent {
|
||||
optional Event_AddToList ext = 1005;
|
||||
}
|
||||
optional string list_name = 1;
|
||||
optional ServerInfo_User user_info = 2;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_AttachCard {
|
||||
extend GameEvent {
|
||||
optional Event_AttachCard ext = 2012;
|
||||
}
|
||||
optional string start_zone = 1;
|
||||
optional sint32 card_id = 2;
|
||||
optional sint32 target_player_id = 3;
|
||||
optional string target_zone = 4;
|
||||
optional sint32 target_card_id = 5;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_ChangeZoneProperties {
|
||||
extend GameEvent {
|
||||
optional Event_ChangeZoneProperties ext = 2020;
|
||||
}
|
||||
optional string zone_name = 1;
|
||||
|
||||
// Reveal top card to all players.
|
||||
optional bool always_reveal_top_card = 10;
|
||||
// reveal top card to the owner.
|
||||
optional bool always_look_at_top_card = 11;
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_event.proto";
|
||||
|
||||
message Event_ConnectionClosed {
|
||||
extend SessionEvent {
|
||||
optional Event_ConnectionClosed ext = 1002;
|
||||
}
|
||||
enum CloseReason {
|
||||
OTHER = 1;
|
||||
SERVER_SHUTDOWN = 2;
|
||||
TOO_MANY_CONNECTIONS = 3;
|
||||
BANNED = 4;
|
||||
USERNAMEINVALID = 5;
|
||||
USER_LIMIT_REACHED = 6;
|
||||
DEMOTED = 7;
|
||||
LOGGEDINELSEWERE = 8;
|
||||
}
|
||||
optional CloseReason reason = 1;
|
||||
optional string reason_str = 2;
|
||||
optional uint32 end_time = 3;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
import "serverinfo_arrow.proto";
|
||||
|
||||
message Event_CreateArrow {
|
||||
extend GameEvent {
|
||||
optional Event_CreateArrow ext = 2000;
|
||||
}
|
||||
optional ServerInfo_Arrow arrow_info = 1;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
import "serverinfo_counter.proto";
|
||||
|
||||
message Event_CreateCounter {
|
||||
extend GameEvent {
|
||||
optional Event_CreateCounter ext = 2002;
|
||||
}
|
||||
optional ServerInfo_Counter counter_info = 1;
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_CreateToken {
|
||||
extend GameEvent {
|
||||
optional Event_CreateToken ext = 2013;
|
||||
}
|
||||
optional string zone_name = 1;
|
||||
optional sint32 card_id = 2;
|
||||
optional string card_name = 3;
|
||||
optional string color = 4;
|
||||
optional string pt = 5;
|
||||
optional string annotation = 6;
|
||||
optional bool destroy_on_zone_change = 7;
|
||||
optional sint32 x = 8;
|
||||
optional sint32 y = 9;
|
||||
optional string card_provider_id = 10;
|
||||
optional bool face_down = 11;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_DelCounter {
|
||||
extend GameEvent {
|
||||
optional Event_DelCounter ext = 2004;
|
||||
}
|
||||
optional sint32 counter_id = 1;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_DeleteArrow {
|
||||
extend GameEvent {
|
||||
optional Event_DeleteArrow ext = 2001;
|
||||
}
|
||||
optional sint32 arrow_id = 1;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_DestroyCard {
|
||||
extend GameEvent {
|
||||
optional Event_DestroyCard ext = 2011;
|
||||
}
|
||||
optional string zone_name = 1;
|
||||
optional uint32 card_id = 2;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
import "serverinfo_card.proto";
|
||||
|
||||
message Event_DrawCards {
|
||||
extend GameEvent {
|
||||
optional Event_DrawCards ext = 2005;
|
||||
}
|
||||
optional sint32 number = 1;
|
||||
repeated ServerInfo_Card cards = 2;
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_DumpZone {
|
||||
extend GameEvent {
|
||||
optional Event_DumpZone ext = 2018;
|
||||
}
|
||||
optional sint32 zone_owner_id = 1;
|
||||
optional string zone_name = 2;
|
||||
optional sint32 number_cards = 3;
|
||||
optional bool is_reversed = 4 [default = false];
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_FlipCard {
|
||||
extend GameEvent {
|
||||
optional Event_FlipCard ext = 2010;
|
||||
}
|
||||
optional string zone_name = 1;
|
||||
optional sint32 card_id = 2;
|
||||
optional string card_name = 3;
|
||||
optional bool face_down = 4;
|
||||
optional string card_provider_id = 5;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_GameClosed {
|
||||
extend GameEvent {
|
||||
optional Event_GameClosed ext = 1002;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_GameHostChanged {
|
||||
extend GameEvent {
|
||||
optional Event_GameHostChanged ext = 1003;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "session_event.proto";
|
||||
import "serverinfo_game.proto";
|
||||
import "serverinfo_gametype.proto";
|
||||
|
||||
message Event_GameJoined {
|
||||
extend SessionEvent {
|
||||
optional Event_GameJoined ext = 1009;
|
||||
}
|
||||
optional ServerInfo_Game game_info = 1;
|
||||
repeated ServerInfo_GameType game_types = 2;
|
||||
optional sint32 host_id = 3;
|
||||
optional sint32 player_id = 4;
|
||||
optional bool spectator = 5;
|
||||
optional bool resuming = 6;
|
||||
optional bool judge = 7;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message Event_GameSay {
|
||||
extend GameEvent {
|
||||
optional Event_GameSay ext = 1009;
|
||||
}
|
||||
optional string message = 1;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
import "serverinfo_player.proto";
|
||||
|
||||
// Signals that the game state has changed.
|
||||
// If a field is present in this message, it will overwrite the client's game state.
|
||||
// Also used to provide the entire game state when joining a game.
|
||||
message Event_GameStateChanged {
|
||||
extend GameEvent {
|
||||
optional Event_GameStateChanged ext = 1005;
|
||||
}
|
||||
|
||||
// the list of players. Players contain their zones which contain all cards in the game
|
||||
repeated ServerInfo_Player player_list = 1;
|
||||
|
||||
// if the game has started
|
||||
optional bool game_started = 2;
|
||||
|
||||
// the player who is currently holding the turn
|
||||
optional sint32 active_player_id = 3;
|
||||
|
||||
// the current phase
|
||||
optional sint32 active_phase = 4;
|
||||
|
||||
// the amount of seconds since the game started
|
||||
optional uint32 seconds_elapsed = 5;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
import "serverinfo_playerproperties.proto";
|
||||
|
||||
message Event_Join {
|
||||
extend GameEvent {
|
||||
optional Event_Join ext = 1000;
|
||||
}
|
||||
optional ServerInfo_PlayerProperties player_properties = 1;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue