mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-14 19:18:55 -07:00
Merge branch 'master' into 2474-server-status
This commit is contained in:
commit
894828962b
146 changed files with 18357 additions and 12225 deletions
|
|
@ -26,6 +26,7 @@ SET(common_SOURCES
|
|||
server_room.cpp
|
||||
serverinfo_user_container.cpp
|
||||
sfmt/SFMT.c
|
||||
expression.cpp
|
||||
)
|
||||
|
||||
set(ORACLE_LIBS)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,17 @@
|
|||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
|
||||
#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)
|
||||
{
|
||||
|
|
@ -477,161 +486,131 @@ bool DeckList::saveToFile_Native(QIODevice *device)
|
|||
|
||||
bool DeckList::loadFromStream_Plain(QTextStream &in)
|
||||
{
|
||||
const QRegularExpression reCardLine("^\\s*[\\w\\[\\(\\{].*$", QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression reEmpty("^\\s*$");
|
||||
const QRegularExpression reComment("[\\w\\[\\(\\{].*$", QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression reSBMark("^\\s*sb:\\s*(.+)", QRegularExpression::CaseInsensitiveOption);
|
||||
const QRegularExpression reSBComment("sideboard", QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
// simplified matches
|
||||
const QRegularExpression reMultiplier("^[xX\\(\\[]*(\\d+)[xX\\*\\)\\]]* ?(.+)");
|
||||
const QRegularExpression reBrace(" ?[\\[\\{][^\\]\\}]*[\\]\\}] ?"); // not nested
|
||||
const QRegularExpression reRoundBrace("^\\([^\\)]*\\) ?"); // () are only matched at start of string
|
||||
const QRegularExpression reDigitBrace(" ?\\(\\d*\\) ?"); // () are matched if containing digits
|
||||
const QHash<QRegularExpression, QString> differences{{QRegularExpression("’"), QString("'")},
|
||||
{QRegularExpression("Æ"), QString("Ae")},
|
||||
{QRegularExpression("æ"), QString("ae")},
|
||||
{QRegularExpression(" ?[|/]+ ?"), QString(" // ")},
|
||||
{QRegularExpression("(?<![A-Z]) ?& ?"), QString(" // ")}};
|
||||
|
||||
cleanList();
|
||||
QVector<QString> inputs; // QTextStream -> QVector
|
||||
|
||||
bool priorEntryIsBlank = true, isAtBeginning = true;
|
||||
int blankLines = 0;
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().simplified().toLower();
|
||||
QStringList inputs = in.readAll().trimmed().split('\n');
|
||||
int max_line = inputs.size();
|
||||
|
||||
/*
|
||||
* Removes all blank lines at start of inputs
|
||||
* Ex: ("", "", "", "Card1", "Card2") => ("Card1", "Card2")
|
||||
*
|
||||
* This will also concise multiple blank lines in a row to just one blank
|
||||
* Ex: ("Card1", "Card2", "", "", "", "Card3") => ("Card1", "Card2", "", "Card3")
|
||||
*/
|
||||
if (line.isEmpty()) {
|
||||
if (priorEntryIsBlank || isAtBeginning) {
|
||||
continue;
|
||||
}
|
||||
|
||||
priorEntryIsBlank = true;
|
||||
blankLines++;
|
||||
} else {
|
||||
isAtBeginning = false;
|
||||
priorEntryIsBlank = false;
|
||||
// start at the first empty line before the first cardline
|
||||
int deckStart = inputs.indexOf(reCardLine);
|
||||
if (deckStart == -1) { // there are no cards?
|
||||
if (inputs.indexOf(reComment) == -1)
|
||||
return false; // input is empty
|
||||
deckStart = max_line;
|
||||
} else {
|
||||
deckStart = inputs.lastIndexOf(reEmpty, deckStart);
|
||||
if (deckStart == -1) {
|
||||
deckStart = 0;
|
||||
}
|
||||
|
||||
inputs.push_back(line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes blank line at end of inputs (if applicable)
|
||||
* Ex: ("Card1", "Card2", "") => ("Card1", "Card2")
|
||||
* NOTE: Any duplicates were taken care of above, so there can be
|
||||
* at most one blank line at the very end
|
||||
*/
|
||||
if (!inputs.empty() && inputs.last().isEmpty()) {
|
||||
blankLines--;
|
||||
inputs.erase(inputs.end() - 1);
|
||||
}
|
||||
|
||||
// If "Sideboard" line appears in inputs, then blank lines mean nothing
|
||||
if (inputs.contains("sideboard")) {
|
||||
blankLines = 2;
|
||||
}
|
||||
|
||||
bool inSideboard = false, titleFound = false, isSideboard;
|
||||
int okRows = 0;
|
||||
|
||||
foreach (QString line, inputs) {
|
||||
// This is a comment line, ignore it
|
||||
if (line.startsWith("//")) {
|
||||
if (!titleFound) // Set the title to the first comment
|
||||
{
|
||||
name = line.mid(2).trimmed();
|
||||
titleFound = true;
|
||||
} else if (okRows == 0) // We haven't processed any cards yet
|
||||
{
|
||||
comments += line.mid(2).trimmed() + "\n";
|
||||
// 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;
|
||||
}
|
||||
int nextCard = inputs.indexOf(reCardLine, sBStart + 1);
|
||||
if (inputs.indexOf(reEmpty, nextCard + 1) != -1) {
|
||||
sBStart = max_line; // if there is another empty line all cards are mainboard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
QRegularExpressionMatch match;
|
||||
|
||||
// parse name and comments
|
||||
while (index < deckStart) {
|
||||
const QString current = inputs.at(index++);
|
||||
if (!current.contains(reEmpty)) {
|
||||
match = reComment.match(current);
|
||||
name = match.captured();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (index < deckStart) {
|
||||
const QString current = inputs.at(index++);
|
||||
if (!current.contains(reEmpty)) {
|
||||
match = reComment.match(current);
|
||||
comments += match.captured() + '\n';
|
||||
}
|
||||
}
|
||||
comments.chop(1); // remove last newline
|
||||
|
||||
// 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();
|
||||
|
||||
// If we have a blank line and it's the _ONLY_ blank line in the paste
|
||||
// and it follows at least one valid card
|
||||
// Then we assume it means to start the sideboard section of the paste.
|
||||
// If we have the word "Sideboard" appear on any line, then that will
|
||||
// also indicate the start of the sideboard.
|
||||
if ((line.isEmpty() && blankLines == 1 && okRows > 0) || line.startsWith("sideboard")) {
|
||||
inSideboard = true;
|
||||
continue; // The line isn't actually a card
|
||||
}
|
||||
|
||||
isSideboard = inSideboard;
|
||||
|
||||
if (line.startsWith("sb:")) {
|
||||
line = line.mid(3).trimmed();
|
||||
isSideboard = true;
|
||||
}
|
||||
|
||||
if (line.trimmed().isEmpty()) {
|
||||
continue; // The line was " " instead of "\n"
|
||||
}
|
||||
|
||||
// Filter out MWS edition symbols and basic land extras
|
||||
QRegExp rx("\\[.*\\]\\s?");
|
||||
line.remove(rx);
|
||||
rx.setPattern("\\s?\\(.*\\)");
|
||||
line.remove(rx);
|
||||
|
||||
// Filter out post card name editions
|
||||
rx.setPattern("\\|.*$");
|
||||
line.remove(rx);
|
||||
|
||||
// If the user inputs "Quicksilver Elemental" then it will cut it off
|
||||
// 1x Squishy Treaker
|
||||
int i = line.indexOf(' ');
|
||||
int cardNameStart = i + 1;
|
||||
|
||||
if (i > 0) {
|
||||
// If the count ends with an 'x', ignore it. For example,
|
||||
// "4x Storm Crow" will count 4 correctly.
|
||||
if (line.at(i - 1) == 'x') {
|
||||
i--;
|
||||
} else if (!line.at(i - 1).isDigit()) {
|
||||
// If the user inputs "Quicksilver Elemental" then it will work as 1x of that card
|
||||
cardNameStart = 0;
|
||||
// check if card should be sideboard
|
||||
bool sideboard = false;
|
||||
if (sBStart < 0) {
|
||||
match = reSBMark.match(cardName);
|
||||
if (match.hasMatch()) {
|
||||
sideboard = true;
|
||||
cardName = match.captured(1);
|
||||
}
|
||||
} else {
|
||||
if (index == sBStart) // skip sideboard line itself
|
||||
continue;
|
||||
sideboard = index > sBStart;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int number = line.left(i).toInt(&ok);
|
||||
|
||||
if (!ok) {
|
||||
number = 1; // If input is "cardName" assume it's "1x cardName"
|
||||
// check if a specific amount is mentioned
|
||||
int amount = 1;
|
||||
match = reMultiplier.match(cardName);
|
||||
if (match.hasMatch()) {
|
||||
amount = match.capturedRef(1).toInt();
|
||||
cardName = match.captured(2);
|
||||
}
|
||||
|
||||
QString cardName = line.mid(cardNameStart);
|
||||
// remove stuff inbetween braces
|
||||
cardName.remove(reBrace);
|
||||
cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards
|
||||
cardName.remove(reDigitBrace); // all cards from un-sets that have a word in between round braces at the end
|
||||
|
||||
// Common differences between Cockatrice's card names
|
||||
// and what's commonly used in decklists
|
||||
rx.setPattern("’");
|
||||
cardName.replace(rx, "'");
|
||||
rx.setPattern("Æ");
|
||||
cardName.replace(rx, "Ae");
|
||||
rx.setPattern("\\s*[|/]{1,2}\\s*");
|
||||
cardName.replace(rx, " // ");
|
||||
|
||||
// Replace only if the ampersand is preceded by a non-capital letter,
|
||||
// as would happen with acronyms. So 'Fire & Ice' is replaced but not
|
||||
// 'R&D' or 'R & D'.
|
||||
// Qt regexes don't support lookbehind so we capture and replace instead.
|
||||
rx.setPattern("([^A-Z])\\s*&\\s*");
|
||||
if (rx.indexIn(cardName) != -1) {
|
||||
cardName.replace(rx, QString("%1 // ").arg(rx.cap(1)));
|
||||
// replace common differences in cardnames
|
||||
for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) {
|
||||
cardName.replace(diff.key(), diff.value());
|
||||
}
|
||||
|
||||
// We need to get the name of the card from the database,
|
||||
// but we can't do that until we get the "real" name
|
||||
// (name stored in database for the card)
|
||||
// and establish a card info that is of the card, then it's
|
||||
// a simple getting the _real_ name of the card
|
||||
// (i.e. "STOrm, CrOW" => "Storm Crow")
|
||||
// get cardname, this function does nothing if the name is not found
|
||||
cardName = getCompleteCardName(cardName);
|
||||
|
||||
// Look for the correct card zone of where to place the new card
|
||||
QString zoneName = getCardZoneFromName(cardName, isSideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN);
|
||||
// get zone name based on if it's in sideboard
|
||||
QString zoneName = getCardZoneFromName(cardName, sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN);
|
||||
|
||||
okRows++;
|
||||
new DecklistCardNode(cardName, number, getZoneObjFromName(zoneName));
|
||||
// make new entry in decklist
|
||||
new DecklistCardNode(cardName, amount, getZoneObjFromName(zoneName));
|
||||
}
|
||||
|
||||
updateDeckHash();
|
||||
return (okRows > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
InnerDecklistNode *DeckList::getZoneObjFromName(const QString zoneName)
|
||||
|
|
|
|||
108
common/expression.cpp
Normal file
108
common/expression.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#include "expression.h"
|
||||
#include "./lib/peglib.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <cmath>
|
||||
#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("sin", [](double a) { return sin(a); });
|
||||
default_functions->insert("cos", [](double a) { return cos(a); });
|
||||
default_functions->insert("tan", [](double a) { return tan(a); });
|
||||
default_functions->insert("sqrt", [](double a) { return sqrt(a); });
|
||||
default_functions->insert("log", [](double a) { return log(a); });
|
||||
default_functions->insert("log10", [](double a) { return log(a); });
|
||||
default_functions->insert("trunc", [](double a) { return trunc(a); });
|
||||
default_functions->insert("abs", [](double a) { return abs(a); });
|
||||
|
||||
default_functions->insert("floor", [](double a) { return floor(a); });
|
||||
default_functions->insert("ceil", [](double a) { return ceil(a); });
|
||||
default_functions->insert("round", [](double a) { return round(a); });
|
||||
default_functions->insert("trunc", [](double a) { return 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(ast.token);
|
||||
} else if (ast.name == "FUNCTION") {
|
||||
QString name = QString::fromStdString(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 (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 = pow(result, arg);
|
||||
break;
|
||||
default:
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
double Expression::parse(const QString &expr)
|
||||
{
|
||||
QByteArray ba = expr.toLocal8Bit();
|
||||
|
||||
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;
|
||||
}
|
||||
28
common/expression.h
Normal file
28
common/expression.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#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
|
||||
3293
common/lib/peglib.h
Normal file
3293
common/lib/peglib.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -169,3 +169,12 @@ 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 "Outdated protobuf version found (${Protobuf_VERSION} < 3.1.0), "
|
||||
"disabled warnings to avoid compilation errors.")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -5,3 +5,10 @@ message Command_Concede {
|
|||
optional Command_Concede ext = 1017;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
message Command_Unconcede {
|
||||
extend GameCommand {
|
||||
optional Command_Unconcede ext = 1032;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,3 +6,9 @@ message Context_Concede {
|
|||
optional Context_Concede ext = 1001;
|
||||
}
|
||||
}
|
||||
|
||||
message Context_Unconcede {
|
||||
extend GameEventContext {
|
||||
optional Context_Unconcede ext = 1009;
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,8 @@ message GameCommand {
|
|||
DECK_SELECT = 1029;
|
||||
SET_SIDEBOARD_LOCK = 1030;
|
||||
CHANGE_ZONE_PROPERTIES = 1031;
|
||||
UNCONCEDE = 1032;
|
||||
|
||||
}
|
||||
extensions 100 to max;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ message GameEventContext {
|
|||
PING_CHANGED = 1006;
|
||||
CONNECTION_STATE_CHANGED = 1007;
|
||||
SET_SIDEBOARD_LOCK = 1008;
|
||||
UNCONCEDE = 1009;
|
||||
}
|
||||
extensions 100 to max;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ private:
|
|||
Server_Player *playerWhosAsking,
|
||||
bool omniscient,
|
||||
bool withUserInfo);
|
||||
void sendGameStateToPlayers();
|
||||
void storeGameInformation();
|
||||
signals:
|
||||
void sigStartGameIfReady();
|
||||
|
|
@ -192,6 +191,7 @@ public:
|
|||
prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context = 0);
|
||||
GameEventContext prepareGameEventContext(const ::google::protobuf::Message &gameEventContext);
|
||||
|
||||
void sendGameStateToPlayers();
|
||||
void sendGameEventContainer(GameEventContainer *cont,
|
||||
GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate |
|
||||
GameEventStorageItem::SendToOthers,
|
||||
|
|
|
|||
|
|
@ -776,6 +776,30 @@ Server_Player::cmdConcede(const Command_Concede & /*cmd*/, ResponseContainer & /
|
|||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_Player::cmdUnconcede(const Command_Unconcede & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||
{
|
||||
if (spectator)
|
||||
return Response::RespFunctionNotAllowed;
|
||||
if (!game->getGameStarted())
|
||||
return Response::RespGameNotStarted;
|
||||
if (!conceded)
|
||||
return Response::RespContextError;
|
||||
|
||||
setConceded(false);
|
||||
|
||||
Event_PlayerPropertiesChanged event;
|
||||
event.mutable_player_properties()->set_conceded(false);
|
||||
ges.enqueueGameEvent(event, playerId);
|
||||
ges.setGameEventContext(Context_Unconcede());
|
||||
|
||||
setupZones();
|
||||
|
||||
game->sendGameStateToPlayers();
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_Player::cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||
{
|
||||
|
|
@ -1826,6 +1850,10 @@ Server_Player::processGameCommand(const GameCommand &command, ResponseContainer
|
|||
case GameCommand::CHANGE_ZONE_PROPERTIES:
|
||||
return cmdChangeZoneProperties(command.GetExtension(Command_ChangeZoneProperties::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::UNCONCEDE:
|
||||
return cmdUnconcede(command.GetExtension(Command_Unconcede::ext), rc, ges);
|
||||
break;
|
||||
|
||||
default:
|
||||
return Response::RespInvalidCommand;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class Command_SetCardCounter;
|
|||
class Command_IncCardCounter;
|
||||
class Command_ReadyStart;
|
||||
class Command_Concede;
|
||||
class Command_Unconcede;
|
||||
class Command_IncCounter;
|
||||
class Command_CreateCounter;
|
||||
class Command_SetCounter;
|
||||
|
|
@ -190,6 +191,7 @@ public:
|
|||
Response::ResponseCode
|
||||
cmdKickFromGame(const Command_KickFromGame &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
Response::ResponseCode cmdConcede(const Command_Concede &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
Response::ResponseCode cmdUnconcede(const Command_Unconcede &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
Response::ResponseCode cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
Response::ResponseCode cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
Response::ResponseCode
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue