Skip to content
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3f785ff
feat: populate personal info basic stats from mount data
TubaApollo Mar 9, 2026
b31366e
refactor: rename personal info fields from positional to semantic names
TubaApollo Mar 9, 2026
894aa5d
feat: track race and jump statistics persistently in mount data
TubaApollo Mar 10, 2026
00c91f6
feat: track distance, speed and glide stats during races
TubaApollo Mar 12, 2026
d240b66
fix: add mount guards, improve glide tracking and use named magic con…
TubaApollo Mar 12, 2026
eaf8d25
feat: track boost combo and win streaks in race statistics
TubaApollo Mar 12, 2026
de9dd59
fix: use consistent {} initialization and add team win streak TODO
TubaApollo Mar 12, 2026
e2653e0
style: apply clang-format to new code
TubaApollo Mar 12, 2026
434577f
Update include/libserver/network/command/proto/LobbyMessageDefinition…
TubaApollo Mar 13, 2026
e69fbd1
Update include/libserver/network/command/proto/LobbyMessageDefinition…
TubaApollo Mar 13, 2026
2707c4f
Update include/libserver/network/command/proto/LobbyMessageDefinition…
TubaApollo Mar 13, 2026
3fc368d
Update src/server/race/RaceDirector.cpp
TubaApollo Mar 13, 2026
f4a684b
Update src/server/race/RaceDirector.cpp
TubaApollo Mar 13, 2026
857b152
Update src/server/race/RaceDirector.cpp
TubaApollo Mar 13, 2026
72044f8
Update src/server/race/RaceDirector.cpp
TubaApollo Mar 13, 2026
d9ea435
Rename ceremonyId to ceremonySlot, legacyGradeString to gradeTitle
TubaApollo Mar 13, 2026
7b6f637
Fix magic item tracking, add Summon for fire spirit
TubaApollo Mar 13, 2026
258fc94
Fix team win stats to not reset on loss
TubaApollo Mar 13, 2026
203bb98
Flatten mount stats with early returns and named variables
TubaApollo Mar 13, 2026
8565d6f
Remove duplicate comment on padding field
TubaApollo Mar 13, 2026
b4eff96
Rename ceremonySlot to ceremonyAchievementSlot1-3
TubaApollo Mar 13, 2026
18e05bb
Replace em dashes with hyphens in comments
TubaApollo Mar 13, 2026
60d2fca
Add per-field doxygen comments to mount info counters
TubaApollo Mar 13, 2026
5789317
Fix locked ranch bypass, restore missing early return
TubaApollo Mar 13, 2026
87c0ad4
Implement emblem display and selection (SetKeyEmblem 0x254-0x257)
TubaApollo Mar 14, 2026
9c901e7
Rename gradeTitle to unk_gradeString, document client ignores it
TubaApollo Mar 14, 2026
412c83b
Fix unk_gradeString comment: EmblemTable -> AchievementGrade
TubaApollo Mar 14, 2026
1497429
Update include/libserver/network/command/proto/LobbyMessageDefinition…
TubaApollo Mar 14, 2026
73918dd
Update include/libserver/network/command/proto/LobbyMessageDefinition…
TubaApollo Mar 14, 2026
8e15962
Fix ternary and lambda indentation in RaceDirector
TubaApollo Mar 14, 2026
1fe1171
Update include/libserver/network/command/proto/RanchMessageDefinition…
TubaApollo Mar 14, 2026
f6b1736
Update include/libserver/network/command/proto/RanchMessageDefinition…
TubaApollo Mar 14, 2026
49ad848
Update src/libserver/network/command/proto/RanchMessageDefinitions.cpp
TubaApollo Mar 14, 2026
acf5cc9
Update include/libserver/network/command/proto/RanchMessageDefinition…
TubaApollo Mar 14, 2026
d03b621
Update include/libserver/network/command/proto/RanchMessageDefinition…
TubaApollo Mar 14, 2026
43ba547
Fix GetHorse call alignment in RaceDirector
TubaApollo Mar 14, 2026
eef5b98
Update src/libserver/network/command/proto/RanchMessageDefinitions.cpp
TubaApollo Mar 14, 2026
90b4c14
Add BroadcastEmblemNotify, use it in HandleSetKeyEmblem
TubaApollo Mar 14, 2026
92f8624
Fix overhead emblem not refreshing on emblem change
TubaApollo Mar 19, 2026
9b36c46
Fix completion rate counting DNF racers as finished
TubaApollo Mar 19, 2026
912e137
Aggregate personal info stats across all horses, fix emblem broadcast
TubaApollo Mar 19, 2026
5e12037
Move personal info stats from per-horse to character lifetime
TubaApollo Mar 20, 2026
afc12c1
Remove stale TODO - per-horse stats view already implemented
TubaApollo Mar 20, 2026
f2d6ecc
Resolve merge conflict in RaceDirector.cpp
TubaApollo Mar 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions include/libserver/data/DataDefinitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,32 @@ struct Horse
dao::Field<uint32_t> participated{};
dao::Field<uint32_t> cumulativePrize{};
dao::Field<uint32_t> biggestPrize{};

//! Count of races participated.
dao::Field<uint32_t> totalRaces{};
//! Count of finished races.
dao::Field<uint32_t> totalFinished{};
//! Cumulative rank across all races.
dao::Field<uint32_t> cumulativeRank{};

//! Total count of jumps.
dao::Field<uint32_t> totalJumps{};
//! Count of successful jumps.
dao::Field<uint32_t> successfulJumps{};
//! Count of perfect jumps.
dao::Field<uint32_t> perfectJumps{};
//! Highest consecutive jump count.
dao::Field<uint32_t> bestJumpCombo{};
//! Highest consecutive magic defense count.
dao::Field<uint32_t> bestMagicDefenseCombo{};

// TODO: Hit counters need relay passthrough parsing to detect hits.
//! Count of magic ball (fire ball and dark fire) uses.
dao::Field<uint32_t> magicBallUses{};
//! Count of ice wall uses.
dao::Field<uint32_t> iceWallUses{};
//! Count of fire spirit uses.
dao::Field<uint32_t> fireSpiritUses{};
Comment thread
TubaApollo marked this conversation as resolved.
} mountInfo{};
};

Expand Down
36 changes: 26 additions & 10 deletions include/libserver/network/command/proto/LobbyMessageDefinitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1792,25 +1792,41 @@ struct AcCmdLCPersonalInfo
uint16_t magicTeamWinCombo{};
float averageRank{};
float completionRate{};
float member12{};
//! Average chasing count.
//! Displayed as a float with a "times" suffix.
float averageChasingCount{};
uint32_t highestCarnivalPrize{};
uint16_t member14{};
uint16_t member15{};
uint16_t member16{};
//! First achievement showcase slot.
uint16_t ceremonyAchievementSlot1{};
//! Second achievement showcase slot.
uint16_t ceremonyAchievementSlot2{};
//! Third achievement showcase slot.
uint16_t ceremonyAchievementSlot3{};
std::string introduction{};
uint32_t level{60};
//! Level progress as dictated by LevelInfo table in libconfig
uint32_t levelProgress{};
std::string member20{};
//! Parsed by the client but never read back for display.
//! The txt_grade label in the profile UI is populated from
//! a client-side AchievementGrade table lookup instead.
std::string unk_gradeString{};
Comment thread
TubaApollo marked this conversation as resolved.
uint16_t perfectBoostCombo{};
uint16_t perfectJumpCombo{};
uint16_t magicDefenseCombo{};
float member24{};
float member25{};
float member26{};
//! Magic ball (Dark fire and Fireball) attack success rate.
//! Displayed as a percentage represented by a floating point in an interval <0, 1>.
float magicBallAttackSuccessRate{};
//! Fire spirit transfer success rate.
//! Displayed as a percentage represented by a floating point in an interval <0, 1>.
float fireSpiritTransferSuccessRate{};
//! Ice wall attack success rate.
//! Displayed as a percentage represented by a floating point in an interval <0, 1>.
float iceWallAttackSuccessRate{};
std::string guildName{};
uint8_t member28{};
uint8_t member29{};
//! Not used by client.
uint8_t padding28{};
//! Not used by client.
uint8_t padding29{};
Comment thread
TubaApollo marked this conversation as resolved.

static void Write(const Basic& command, SinkStream& stream);
static void Read(Basic& command, SourceStream& stream);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1493,17 +1493,17 @@ struct AcCmdUserRaceUpdatePos
//! Character oid
uint16_t oid{};
//! Position
std::array<float, 3> member2{};
std::array<float, 3> position{};
//! Rotation
std::array<float, 3> member3{};
//! Speed
float member4{};
std::array<float, 3> rotation{};
//! Speed in km/h.
float speed{};
//! 1 = In the air
uint16_t member5{};
uint16_t airborne{};
//! Race track progress
float member6{};
float trackProgress{};
//! Ticks since connected to race director?
uint32_t member7{};
uint32_t ticksSinceConnect{};

static Command GetCommand()
{
Expand Down
100 changes: 100 additions & 0 deletions include/libserver/network/command/proto/RanchMessageDefinitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5084,6 +5084,106 @@ struct AcCmdCROpenRandomBoxCancel
SourceStream& stream);
};

//! Clientbound command to set a character's emblem.
struct AcCmdCRSetKeyEmblemNotify
{
//! UID of the character whose emblem is being set.
uint32_t characterUid{};
//! Emblem ID from the EmblemInfo table (IDs 1-35 in libconfig_c.dat).
uint16_t emblemId{};

static Command GetCommand()
{
return Command::AcCmdCRSetKeyEmblemNotify;
}

//! Writes the command to a provided sink stream.
//! @param command Command.
//! @param stream Sink stream.
static void Write(
const AcCmdCRSetKeyEmblemNotify& command,
SinkStream& stream);

//! Reader a command from a provided source stream.
//! @param command Command.
//! @param stream Source stream.
static void Read(
AcCmdCRSetKeyEmblemNotify& command,
SourceStream& stream);
};

//! Serverbound command to request an emblem change.
struct AcCmdCRSetKeyEmblem
{
//! Emblem ID from the EmblemInfo table (IDs 1-35 in libconfig_c.dat).
uint16_t emblemId{};

static Command GetCommand()
{
return Command::AcCmdCRSetKeyEmblem;
}

//! Writes the command to a provided sink stream.
//! @param command Command.
//! @param stream Sink stream.
static void Write(
const AcCmdCRSetKeyEmblem& command,
SinkStream& stream);

//! Reader a command from a provided source stream.
//! @param command Command.
//! @param stream Source stream.
static void Read(
AcCmdCRSetKeyEmblem& command,
SourceStream& stream);
};

//! Clientbound command to confirm an emblem change request.
struct AcCmdCRSetKeyEmblemOK
{
static Command GetCommand()
{
return Command::AcCmdCRSetKeyEmblemOK;
}

//! Writes the command to a provided sink stream.
//! @param command Command.
//! @param stream Sink stream.
static void Write(
const AcCmdCRSetKeyEmblemOK& command,
SinkStream& stream);

//! Reader a command from a provided source stream.
//! @param command Command.
//! @param stream Source stream.
static void Read(
AcCmdCRSetKeyEmblemOK& command,
SourceStream& stream);
};

//! Clientbound command to cancel an emblem change request.
struct AcCmdCRSetKeyEmblemCancel
{
static Command GetCommand()
{
return Command::AcCmdCRSetKeyEmblemCancel;
}

//! Writes the command to a provided sink stream.
//! @param command Command.
//! @param stream Sink stream.
static void Write(
const AcCmdCRSetKeyEmblemCancel& command,
SinkStream& stream);

//! Reader a command from a provided source stream.
//! @param command Command.
//! @param stream Source stream.
static void Read(
AcCmdCRSetKeyEmblemCancel& command,
SourceStream& stream);
};

} // namespace server::protocol

#endif // RANCH_MESSAGE_DEFINES_HPP
20 changes: 20 additions & 0 deletions include/server/ranch/RanchDirector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ class RanchDirector final
data::Uid& rancherUid,
data::Uid& horseUid);

//! Sends an emblem notify to a specific client's ranch connection.
//! @param recipientCharacterUid UID of the character receiving the notify.
//! @param targetCharacterUid UID of the character whose emblem is shown.
//! @param emblemId Emblem ID from the EmblemInfo table (1-35).
void SendEmblemNotify(
data::Uid recipientCharacterUid,
data::Uid targetCharacterUid,
uint16_t emblemId);

//! Broadcasts an emblem change to all clients on the character's ranch.
//! @param characterUid UID of the character whose emblem changed.
//! @param emblemId Emblem ID from the EmblemInfo table (1-35).
void BroadcastEmblemNotify(
data::Uid characterUid,
uint16_t emblemId);

ServerInstance& GetServerInstance();
Config::Ranch& GetConfig();

Expand Down Expand Up @@ -390,6 +406,10 @@ class RanchDirector final
ClientId clientId,
const protocol::AcCmdCREmblemList& command);

void HandleSetKeyEmblem(
ClientId clientId,
const protocol::AcCmdCRSetKeyEmblem& command);

void HandleChangeNickname(
ClientId clientId,
const protocol::AcCmdCRChangeNickname& command);
Expand Down
23 changes: 23 additions & 0 deletions include/server/tracker/RaceTracker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,29 @@ class RaceTracker
bool hotRodded{};
bool critChance{};
bool gaugeBuff{};

//! Session boost combo (consecutive boosts without combo break).
uint32_t boostComboValue{};
//! Session magic defense combo (consecutive blocks).
uint32_t magicDefenseComboValue{};
//! Session magic item usage counts by item ID.
uint32_t magicBallUses{};
uint32_t iceWallUses{};
uint32_t fireSpiritUses{};

//! Session distance/speed/glide tracking.
std::optional<std::array<float, 3>> previousPosition{};
//! Accumulated distance in metres during this race.
float sessionDistance{};
//! Maximum speed observed during this race (km/h from protocol).
//! Persisted as km/h * 10 (e.g. 1028 = 102.8 km/h).
float sessionMaxSpeed{};
//! Current glide distance accumulator (reset on landing).
float currentGlideDistance{};
//! Longest glide distance observed during this race.
float sessionLongestGlide{};
//! Whether the racer was airborne in the previous update.
bool previousAirborne{};
};

//! An item
Expand Down
24 changes: 23 additions & 1 deletion src/libserver/data/file/FileDataSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,18 @@ void server::FileDataSource::RetrieveHorse(data::Uid uid, data::Horse& horse)
.longestGlideDistance = mountInfo["longestGlideDistance"].get<uint32_t>(),
.participated = mountInfo["participated"].get<uint32_t>(),
.cumulativePrize = mountInfo["cumulativePrize"].get<uint32_t>(),
.biggestPrize = mountInfo["biggestPrize"].get<uint32_t>()};
.biggestPrize = mountInfo["biggestPrize"].get<uint32_t>(),
.totalRaces = mountInfo.value("totalRaces", 0u),
.totalFinished = mountInfo.value("totalFinished", 0u),
.cumulativeRank = mountInfo.value("cumulativeRank", 0u),
.totalJumps = mountInfo.value("totalJumps", 0u),
.successfulJumps = mountInfo.value("successfulJumps", 0u),
.perfectJumps = mountInfo.value("perfectJumps", 0u),
.bestJumpCombo = mountInfo.value("bestJumpCombo", 0u),
.bestMagicDefenseCombo = mountInfo.value("bestMagicDefenseCombo", 0u),
.magicBallUses = mountInfo.value("magicBallUses", 0u),
.iceWallUses = mountInfo.value("iceWallUses", 0u),
.fireSpiritUses = mountInfo.value("fireSpiritUses", 0u)};
}

void server::FileDataSource::StoreHorse(data::Uid uid, const data::Horse& horse)
Expand Down Expand Up @@ -715,6 +726,17 @@ void server::FileDataSource::StoreHorse(data::Uid uid, const data::Horse& horse)
mountInfo["participated"] = horse.mountInfo.participated();
mountInfo["cumulativePrize"] = horse.mountInfo.cumulativePrize();
mountInfo["biggestPrize"] = horse.mountInfo.biggestPrize();
mountInfo["totalRaces"] = horse.mountInfo.totalRaces();
mountInfo["totalFinished"] = horse.mountInfo.totalFinished();
mountInfo["cumulativeRank"] = horse.mountInfo.cumulativeRank();
mountInfo["totalJumps"] = horse.mountInfo.totalJumps();
mountInfo["successfulJumps"] = horse.mountInfo.successfulJumps();
mountInfo["perfectJumps"] = horse.mountInfo.perfectJumps();
mountInfo["bestJumpCombo"] = horse.mountInfo.bestJumpCombo();
mountInfo["bestMagicDefenseCombo"] = horse.mountInfo.bestMagicDefenseCombo();
mountInfo["magicBallUses"] = horse.mountInfo.magicBallUses();
mountInfo["iceWallUses"] = horse.mountInfo.iceWallUses();
mountInfo["fireSpiritUses"] = horse.mountInfo.fireSpiritUses();
json["mountInfo"] = mountInfo;
dataFile << json.dump(2);
}
Expand Down
20 changes: 10 additions & 10 deletions src/libserver/network/command/proto/LobbyMessageDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1153,24 +1153,24 @@ void AcCmdLCPersonalInfo::Basic::Write(
.Write(command.magicTeamWinCombo)
.Write(command.averageRank)
.Write(command.completionRate)
.Write(command.member12)
.Write(command.averageChasingCount)
.Write(command.highestCarnivalPrize)
.Write(command.member14)
.Write(command.member15)
.Write(command.member16)
.Write(command.ceremonyAchievementSlot1)
.Write(command.ceremonyAchievementSlot2)
.Write(command.ceremonyAchievementSlot3)
.Write(command.introduction)
.Write(command.level)
.Write(command.levelProgress)
.Write(command.member20)
.Write(command.unk_gradeString)
.Write(command.perfectBoostCombo)
.Write(command.perfectJumpCombo)
.Write(command.magicDefenseCombo)
.Write(command.member24)
.Write(command.member25)
.Write(command.member26)
.Write(command.magicBallAttackSuccessRate)
.Write(command.fireSpiritTransferSuccessRate)
.Write(command.iceWallAttackSuccessRate)
.Write(command.guildName)
.Write(command.member28)
.Write(command.member29);
.Write(command.padding28)
.Write(command.padding29);
}

void AcCmdLCPersonalInfo::Basic::Read(
Expand Down
12 changes: 6 additions & 6 deletions src/libserver/network/command/proto/RaceMessageDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1077,20 +1077,20 @@ void AcCmdUserRaceUpdatePos::Read(
{
stream.Read(command.oid);

for (auto& element : command.member2)
for (auto& element : command.position)
{
stream.Read(element);
}

for (auto& element : command.member3)
for (auto& element : command.rotation)
{
stream.Read(element);
}

stream.Read(command.member4)
.Read(command.member5)
.Read(command.member6)
.Read(command.member7);
stream.Read(command.speed)
.Read(command.airborne)
.Read(command.trackProgress)
.Read(command.ticksSinceConnect);
}

void AcCmdRCRoomCountdown::Write(
Expand Down
Loading