Merge branch 'Cockatrice:master' into toggle-normal-untapping-once

This commit is contained in:
Skagra42 2026-08-13 13:09:08 -06:00 committed by GitHub
commit fdd7d4eac0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
543 changed files with 82935 additions and 48240 deletions

View file

@ -6,11 +6,7 @@ set(HEADERS abstract_client.h)
set(SOURCES abstract_client.cpp)
if(Qt6_FOUND)
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
elseif(Qt5_FOUND)
qt5_wrap_cpp(MOC_SOURCES ${HEADERS})
endif()
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
add_library(libcockatrice_network_client_abstract STATIC ${MOC_SOURCES} ${SOURCES})

View file

@ -122,6 +122,20 @@ public:
return userName;
}
/**
* @brief Returns the server address configured for the current connection.
*
* May be empty for clients that have no server counterpart (e.g. local test clients).
*/
virtual QString serverName() const
{
return {};
}
virtual quint16 serverPort() const
{
return 0;
}
static PendingCommand *prepareSessionCommand(const ::google::protobuf::Message &cmd);
static PendingCommand *prepareRoomCommand(const ::google::protobuf::Message &cmd, int roomId);
static PendingCommand *prepareModeratorCommand(const ::google::protobuf::Message &cmd);

View file

@ -6,11 +6,7 @@ set(HEADERS local_client.h)
set(SOURCES local_client.cpp)
if(Qt6_FOUND)
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
elseif(Qt5_FOUND)
qt5_wrap_cpp(MOC_SOURCES ${HEADERS})
endif()
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
add_library(libcockatrice_network_client_local STATIC ${MOC_SOURCES} ${SOURCES})

View file

@ -6,11 +6,7 @@ set(HEADERS remote_client.h)
set(SOURCES remote_client.cpp)
if(Qt6_FOUND)
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
elseif(Qt5_FOUND)
qt5_wrap_cpp(MOC_SOURCES ${HEADERS})
endif()
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
add_library(libcockatrice_network_client_remote STATIC ${MOC_SOURCES} ${SOURCES})

View file

@ -131,6 +131,22 @@ public:
return socket->peerName();
}
}
quint16 peerPort() const
{
if (usingWebSocket) {
return websocket->peerPort();
} else {
return socket->peerPort();
}
}
QString serverName() const override
{
return lastHostname;
}
quint16 serverPort() const override
{
return static_cast<quint16>(lastPort);
}
void
connectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password);
void registerToServer(const QString &hostname,

View file

@ -6,11 +6,7 @@ set(HEADERS local_server.h local_server_interface.h)
set(SOURCES local_server.cpp local_server_interface.cpp)
if(Qt6_FOUND)
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
elseif(Qt5_FOUND)
qt5_wrap_cpp(MOC_SOURCES ${HEADERS})
endif()
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
add_library(libcockatrice_network_server_local STATIC ${MOC_SOURCES} ${SOURCES})

View file

@ -23,11 +23,7 @@ set(HEADERS
serverinfo_user_container.h
)
if(Qt6_FOUND)
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
elseif(Qt5_FOUND)
qt5_wrap_cpp(MOC_SOURCES ${HEADERS})
endif()
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
add_library(
libcockatrice_network_server_remote STATIC

View file

@ -48,7 +48,7 @@
#include <libcockatrice/protocol/pb/response.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/string_limits.h>
Server_AbstractParticipant::Server_AbstractParticipant(Server_Game *_game,
int _playerId,

View file

@ -47,7 +47,8 @@
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/rng/rng_abstract.h>
#include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/dice_limits.h>
#include <libcockatrice/utility/string_limits.h>
#include <libcockatrice/utility/zone_names.h>
#include <limits>
#include <ranges>
@ -1636,10 +1637,11 @@ void Server_AbstractPlayer::getInfo(ServerInfo_Player *info,
bool withUserInfo)
{
getProperties(*info->mutable_properties(), withUserInfo);
if (recipient == this) {
if (deck) {
info->set_deck_list(deck->writeToString_Native().toStdString());
}
// Deck lists are only shared with other players when the game is in Open Decklists mode,
// so a player joining an open lobby can see every deck that was loaded before they joined.
if (deck && (recipient == this || game->getShareDecklistsOnLoad())) {
info->set_deck_list(deck->writeToString_Native().toStdString());
}
for (Server_Arrow *arrow : arrows) {

View file

@ -26,7 +26,8 @@
#include <libcockatrice/protocol/pb/event_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/clamped_arithmetic.h>
#include <libcockatrice/utility/counter_limits.h>
#include <limits>
Server_Card::Server_Card(const CardRef &cardRef, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone)
@ -124,8 +125,8 @@ QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue
bool Server_Card::setCounter(int _id, int value, Event_SetCardCounter *event)
{
// Clamp to valid card counter range [0, MAX_COUNTERS_ON_CARD]
value = qBound(0, value, MAX_COUNTERS_ON_CARD);
// Clamp to valid card counter range [0, MAX_COUNTER_VALUE]
value = qBound(0, value, MAX_COUNTER_VALUE);
const int oldValue = counters.value(_id, 0);
if (value == oldValue) {
@ -149,10 +150,8 @@ bool Server_Card::setCounter(int _id, int value, Event_SetCardCounter *event)
bool Server_Card::incrementCounter(int counterId, int delta, Event_SetCardCounter *event)
{
const int oldValue = counters.value(counterId, 0);
const auto result = static_cast<int64_t>(oldValue) + static_cast<int64_t>(delta);
// Clamp to [0, MAX_COUNTERS_ON_CARD] for card counters
const int newValue =
static_cast<int>(qBound(static_cast<int64_t>(0), result, static_cast<int64_t>(MAX_COUNTERS_ON_CARD)));
// Clamp to [0, MAX_COUNTER_VALUE] for card counters
const int newValue = addClamped(oldValue, delta, 0, MAX_COUNTER_VALUE);
if (newValue == oldValue) {
return false;

View file

@ -161,7 +161,7 @@ public:
/**
* @brief Sets a card counter to an exact value with clamping.
* @param _id The counter ID.
* @param value The desired value (clamped to [0, MAX_COUNTERS_ON_CARD]; 0 removes the counter).
* @param value The desired value (clamped to [0, MAX_COUNTER_VALUE]; 0 removes the counter).
* @param event Optional event to populate with counter state.
* @return true if the value changed, false otherwise.
*/
@ -173,7 +173,7 @@ public:
* @param event Optional event to populate with counter state.
* @return true if the value changed, false otherwise.
* @note If counter does not exist, starts from 0. Counter is removed if result is 0.
* @note Clamps result to [0, MAX_COUNTERS_ON_CARD].
* @note Clamps result to [0, MAX_COUNTER_VALUE].
*/
[[nodiscard]] bool incrementCounter(int counterId, int delta, Event_SetCardCounter *event = nullptr);
void setTapped(bool _tapped)

View file

@ -1,24 +1,12 @@
#include "server_counter.h"
#include <libcockatrice/protocol/pb/serverinfo_counter.pb.h>
#include <limits>
Server_Counter::Server_Counter(int _id, const QString &_name, const color &_counterColor, int _radius, int _count)
: id(_id), name(_name), counterColor(_counterColor), radius(_radius), count(_count)
{
}
//! \todo Extract overflow-safe arithmetic into shared helper.
//! Duplicated in Server_Card::incrementCounter() - keep in sync if modified.
bool Server_Counter::incrementCount(int delta)
{
const int oldCount = count;
const auto result = static_cast<int64_t>(count) + static_cast<int64_t>(delta);
count = static_cast<int>(qBound(static_cast<int64_t>(std::numeric_limits<int>::min()), result,
static_cast<int64_t>(std::numeric_limits<int>::max())));
return count != oldCount;
}
void Server_Counter::getInfo(ServerInfo_Counter *info)
{
info->set_id(id);

View file

@ -22,6 +22,8 @@
#include <QString>
#include <libcockatrice/protocol/pb/color.pb.h>
#include <libcockatrice/utility/clamped_arithmetic.h>
#include <limits>
class ServerInfo_Counter;
@ -92,7 +94,12 @@ public:
* @return true if the value changed, false otherwise.
* @note Clamps result to [INT_MIN, INT_MAX] to prevent overflow.
*/
[[nodiscard]] bool incrementCount(int delta);
[[nodiscard]] bool incrementCount(int delta)
{
const int oldCount = count;
count = addClamped(count, delta, std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
return count != oldCount;
}
/**
* @brief Populates info with this counter's current state for network serialization.

View file

@ -526,10 +526,7 @@ void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface,
if (broadcastUpdate) {
ServerInfo_Game gameInfo;
gameInfo.set_room_id(room->getId());
gameInfo.set_game_id(gameId);
gameInfo.set_player_count(getPlayerCount());
gameInfo.set_spectators_count(getSpectatorCount());
getInfo(gameInfo);
emit gameInfoChanged(gameInfo);
}
@ -588,10 +585,7 @@ void Server_Game::removeParticipant(Server_AbstractParticipant *participant, Eve
}
ServerInfo_Game gameInfo;
gameInfo.set_room_id(room->getId());
gameInfo.set_game_id(gameId);
gameInfo.set_player_count(getPlayerCount());
gameInfo.set_spectators_count(getSpectatorCount());
getInfo(gameInfo);
emit gameInfoChanged(gameInfo);
}
@ -847,6 +841,12 @@ void Server_Game::getInfo(ServerInfo_Game &result) const
result.set_player_count(getPlayerCount());
result.set_started(gameStarted);
result.mutable_creator_info()->CopyFrom(*getCreatorInfo());
const Server_AbstractParticipant *host = participants.value(hostId, nullptr);
if (host != nullptr) {
result.mutable_host_info()->CopyFrom(*host->getUserInfo());
} else {
result.mutable_host_info()->CopyFrom(*getCreatorInfo());
}
result.set_only_buddies(onlyBuddies);
result.set_only_registered(onlyRegistered);
result.set_spectators_allowed(getSpectatorsAllowed());

View file

@ -47,7 +47,7 @@
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/rng/rng_abstract.h>
#include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/string_limits.h>
#include <libcockatrice/utility/zone_names.h>
Server_Player::Server_Player(Server_Game *_game,

View file

@ -190,6 +190,25 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session,
return authState;
}
void Server::broadcastUserInfoUpdate(Server_ProtocolHandler *source)
{
Event_UserJoined event;
event.mutable_user_info()->CopyFrom(source->copyUserInfo(false));
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
clientsLock.lockForRead();
for (auto &client : clients) {
if (client->getAcceptsUserListChanges()) {
client->sendProtocolItem(*se);
}
}
clientsLock.unlock();
sendIsl_SessionEvent(*se);
delete se;
}
void Server::addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId)
{
QWriteLocker locker(&persistentPlayersLock);

View file

@ -64,6 +64,7 @@ public:
QString &clientid,
QString &clientVersion,
QString &connectionType);
void broadcastUserInfoUpdate(Server_ProtocolHandler *source);
const QMap<int, Server_Room *> &getRooms()
{
@ -90,6 +91,10 @@ public:
{
return QString();
}
virtual SessionEvent *getLoginSessionEvent() const
{
return nullptr;
}
virtual QString getRequiredFeatures() const
{
return QString();

View file

@ -26,7 +26,7 @@
#include <libcockatrice/protocol/pb/response_list_users.pb.h>
#include <libcockatrice/protocol/pb/response_login.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/string_limits.h>
Server_ProtocolHandler::Server_ProtocolHandler(Server *_server,
Server_DatabaseInterface *_databaseInterface,
@ -562,6 +562,11 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
event.set_message(server->getLoginMessage().toStdString());
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));
SessionEvent *loginEvent = server->getLoginSessionEvent();
if (loginEvent) {
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, loginEvent);
}
auto *re = new Response_Login;
re->mutable_user_info()->CopyFrom(copyUserInfo(true));
@ -685,6 +690,15 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetU
ServerInfo_User_Container *infoSource = server->findUser(userName);
if (!infoSource) {
re->mutable_user_info()->CopyFrom(databaseInterface->getUserData(userName, true));
// The user is not currently online. Mirror the redaction that
// copyUserInfo() applies to online users: the id and email address
// are only ever visible to the account owner, and the client id
// only to moderators.
re->mutable_user_info()->clear_id();
re->mutable_user_info()->clear_email();
if (!(userInfo->user_level() & ServerInfo_User::IsModerator)) {
re->mutable_user_info()->clear_clientid();
}
} else {
re->mutable_user_info()->CopyFrom(
infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator));

View file

@ -15,7 +15,7 @@
#include <libcockatrice/protocol/pb/room_commands.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_chat_message.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_room.pb.h>
#include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/string_limits.h>
Server_Room::Server_Room(int _id,
int _chatHistorySize,