Compare commits

...

12 Commits

Author SHA1 Message Date
David Markowitz
7c8ca1c1cb chore: some cleanup work on WorldServer and PerformanceManager 2025-05-02 16:33:28 -07:00
David Markowitz
b31f9670d1 feat: shutdown command (#1780) 2025-04-24 15:41:26 -05:00
David Markowitz
1cc1782b35 fix: lock crash to operator (#1779) 2025-04-24 11:23:46 -07:00
David Markowitz
55d409eb82 Add invite initial response msg (#1775)
re-do team leave logic to send more accurate messages

Players are still able to leave the team with the same results as before, however now the correct messages are sent to team chats (no fixes for local teams).
2025-04-23 01:56:38 -07:00
David Markowitz
65f3c33ca5 chore: use client enum packet type instead (#1776)
Same values, different namespace and not duplicated
2025-04-23 01:55:52 -07:00
David Markowitz
93fa4e268f fix: buff station dying and rotating too soon (#1768)
Tested that the buff station now waits for a player to build it and is alive for 25 seconds before moving positions fixes #1767
2025-04-23 01:55:36 -07:00
David Markowitz
1fb1da101c fix: multiple progression for shark mission (#1769)
tested that mission progresses once and only once per death
2025-04-19 07:37:08 -05:00
David Markowitz
6f94043b33 feat: broadcast achievements in chat as in live (#1771)
* feat: broadcast achievements in chat as in live

Tested that everyone on the receiving players' friends list receives the announcement as it went out in live.  Only works for achievements that have an entry in the MissionEmail table.  This may have been sent out to everyone in your zone as well however we don't really have a way to verify this aside from questioning why the client checks for the receiver being in the ignore list.  This is the only hint to me that this may have been broadcast to more than friends but again, no proof.

* Add initial response msg and sending

* Revert "Add initial response msg and sending"

This reverts commit fb942e4692.
2025-04-19 07:36:53 -05:00
Wincent Holm
5785764a95 Cache build directory when using docker with BuildKit (#1772) 2025-04-18 17:38:24 -07:00
Wincent Holm
fa53fa7935 Migrations only flag (#1773) 2025-04-18 17:38:08 -07:00
David Markowitz
6b0f3a66e9 fix: session flags not being loaded every other world load (#1763)
tested that news screen no longer shows up on every other world load
2025-04-11 09:10:38 -05:00
David Markowitz
f5c212fb86 fix: sys addr for private zones (#1760)
* fix: sys addr for private zones

* Initialize variables in Instance
2025-04-11 09:05:31 -05:00
26 changed files with 440 additions and 288 deletions

View File

@@ -11,7 +11,12 @@ COPY --chmod=0500 ./build.sh /app/
RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt
RUN ./build.sh RUN --mount=type=cache,target=/app/build,id=build-cache \
mkdir -p /app/build /tmp/persisted-build && \
cd /app/build && \
cmake .. && \
make -j$(nproc --ignore 1) && \
cp -r /app/build/* /tmp/persisted-build/
FROM debian:12 as runtime FROM debian:12 as runtime
@@ -23,23 +28,23 @@ RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# Grab libraries and load them # Grab libraries and load them
COPY --from=build /app/build/mariadbcpp/libmariadbcpp.so /usr/local/lib/ COPY --from=build /tmp/persisted-build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
RUN ldconfig RUN ldconfig
# Server bins # Server bins
COPY --from=build /app/build/*Server /app/ COPY --from=build /tmp/persisted-build/*Server /app/
# Necessary suplimentary files # Necessary suplimentary files
COPY --from=build /app/build/*.ini /app/configs/ COPY --from=build /tmp/persisted-build/*.ini /app/configs/
COPY --from=build /app/build/vanity/*.* /app/vanity/ COPY --from=build /tmp/persisted-build/vanity/*.* /app/vanity/
COPY --from=build /app/build/navmeshes /app/navmeshes COPY --from=build /tmp/persisted-build/navmeshes /app/navmeshes
COPY --from=build /app/build/migrations /app/migrations COPY --from=build /tmp/persisted-build/migrations /app/migrations
COPY --from=build /app/build/*.dcf /app/ COPY --from=build /tmp/persisted-build/*.dcf /app/
# backup of config and vanity files to copy to the host incase # backup of config and vanity files to copy to the host incase
# of a mount clobbering the copy from above # of a mount clobbering the copy from above
COPY --from=build /app/build/*.ini /app/default-configs/ COPY --from=build /tmp/persisted-build/*.ini /app/default-configs/
COPY --from=build /app/build/vanity/*.* /app/default-vanity/ COPY --from=build /tmp/persisted-build/vanity/*.* /app/default-vanity/
# needed as the container runs with the root user # needed as the container runs with the root user
# and therefore sudo doesn't exist # and therefore sudo doesn't exist

View File

@@ -12,8 +12,8 @@
// not allowing teams, rejecting DMs, friends requets etc. // not allowing teams, rejecting DMs, friends requets etc.
// The only thing not auto-handled is instance activities force joining the team on the server. // The only thing not auto-handled is instance activities force joining the team on the server.
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) { void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const MessageType::Client type) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receivingPlayer); bitStream.Write(receivingPlayer);
//portion that will get routed: //portion that will get routed:
@@ -48,9 +48,9 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) {
} }
CBITSTREAM; CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::GET_IGNORE); WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::GET_IGNORE_LIST_RESPONSE);
bitStream.Write<uint8_t>(false); // Probably is Is Free Trial, but we don't care about that bitStream.Write<uint8_t>(false); // Is Free Trial, but we don't care about that
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
bitStream.Write<uint16_t>(receiver.ignoredPlayers.size()); bitStream.Write<uint16_t>(receiver.ignoredPlayers.size());
@@ -86,7 +86,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
std::string toIgnoreStr = toIgnoreName.GetAsString(); std::string toIgnoreStr = toIgnoreName.GetAsString();
CBITSTREAM; CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::ADD_IGNORE); WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::ADD_IGNORE_RESPONSE);
// Check if the player exists // Check if the player exists
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY; LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
@@ -161,7 +161,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) {
receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end()); receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end());
CBITSTREAM; CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::REMOVE_IGNORE); WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::REMOVE_IGNORE_RESPONSE);
bitStream.Write<int8_t>(0); bitStream.Write<int8_t>(0);
LUWString playerNameSend(removedIgnoreStr, 33); LUWString playerNameSend(removedIgnoreStr, 33);

View File

@@ -5,17 +5,16 @@ struct Packet;
#include <cstdint> #include <cstdint>
/**
* @brief The ignore list allows players to ignore someone silently. Requests will generally be blocked by the client, but they should also be checked
* on the server as well so the sender can get a generic error code in response.
*
*/
namespace ChatIgnoreList { namespace ChatIgnoreList {
void GetIgnoreList(Packet* packet); void GetIgnoreList(Packet* packet);
void AddIgnore(Packet* packet); void AddIgnore(Packet* packet);
void RemoveIgnore(Packet* packet); void RemoveIgnore(Packet* packet);
enum class Response : uint8_t {
ADD_IGNORE = 32,
REMOVE_IGNORE = 33,
GET_IGNORE = 34,
};
enum class AddResponse : uint8_t { enum class AddResponse : uint8_t {
SUCCESS, SUCCESS,
ALREADY_IGNORED, ALREADY_IGNORED,

View File

@@ -73,7 +73,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
data.Serialize(bitStream); data.Serialize(bitStream);
} }
SystemAddress sysAddr = player.sysAddr; SystemAddress sysAddr = player.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -122,7 +122,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
requesteeFriendData.isOnline = false; requesteeFriendData.isOnline = false;
requesteeFriendData.zoneID = requestor.zoneID; requesteeFriendData.zoneID = requestor.zoneID;
requestee.friends.push_back(requesteeFriendData); requestee.friends.push_back(requesteeFriendData);
requestee.sysAddr = UNASSIGNED_SYSTEM_ADDRESS; requestee.worldServerSysAddr = UNASSIGNED_SYSTEM_ADDRESS;
break; break;
} }
} }
@@ -189,8 +189,8 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus); Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus);
// Sent the best friend update here if the value is 3 // Sent the best friend update here if the value is 3
if (bestFriendStatus == 3U) { if (bestFriendStatus == 3U) {
if (requestee.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true); if (requestee.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true);
if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true); if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true);
for (auto& friendData : requestor.friends) { for (auto& friendData : requestor.friends) {
if (friendData.friendID == requestee.playerID) { if (friendData.friendID == requestee.playerID) {
@@ -211,7 +211,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
} }
} }
} else { } else {
if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true); if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true);
} }
} else { } else {
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends(); auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
@@ -384,7 +384,7 @@ void ChatPacketHandler::HandleWho(Packet* packet) {
bitStream.Write(player.zoneID.GetCloneID()); bitStream.Write(player.zoneID.GetCloneID());
bitStream.Write(request.playerName); bitStream.Write(request.playerName);
SystemAddress sysAddr = sender.sysAddr; SystemAddress sysAddr = sender.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -418,7 +418,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
} }
} }
} }
SystemAddress sysAddr = sender.sysAddr; SystemAddress sysAddr = sender.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -519,6 +519,28 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::GENERAL, eChatMessageResponseCode::NOTFRIENDS); SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::GENERAL, eChatMessageResponseCode::NOTFRIENDS);
} }
void ChatPacketHandler::OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr) {
ChatPackets::AchievementNotify notify{};
notify.Deserialize(bitstream);
const auto& playerData = Game::playerContainer.GetPlayerData(notify.earnerName.GetAsString());
if (!playerData) return;
for (const auto& myFriend : playerData.friends) {
auto& friendData = Game::playerContainer.GetPlayerData(myFriend.friendID);
if (friendData) {
notify.targetPlayerName.string = GeneralUtils::ASCIIToUTF16(friendData.playerName);
LOG_DEBUG("Sending achievement notify to %s", notify.targetPlayerName.GetAsString().c_str());
RakNet::BitStream worldStream;
BitStreamUtils::WriteHeader(worldStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
worldStream.Write(friendData.playerID);
notify.WriteHeader(worldStream);
notify.Serialize(worldStream);
Game::server->Send(worldStream, friendData.worldServerSysAddr, false);
}
}
}
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) { void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
@@ -537,7 +559,7 @@ void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const P
bitStream.Write(responseCode); bitStream.Write(responseCode);
bitStream.Write(message); bitStream.Write(message);
SystemAddress sysAddr = routeTo.sysAddr; SystemAddress sysAddr = routeTo.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -580,6 +602,19 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
SendTeamInvite(other, player); SendTeamInvite(other, player);
LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.GetAsString().c_str()); LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.GetAsString().c_str());
bool failed = false;
for (const auto& ignore : other.ignoredPlayers) {
if (ignore.playerId == player.playerID) {
failed = true;
break;
}
}
ChatPackets::TeamInviteInitialResponse response{};
response.inviteFailedToSend = failed;
response.playerName = invitedPlayer.string;
ChatPackets::SendRoutedMsg(response, playerID, player.worldServerSysAddr);
} }
void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
@@ -593,7 +628,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
LWOOBJID leaderID = LWOOBJID_EMPTY; LWOOBJID leaderID = LWOOBJID_EMPTY;
inStream.Read(leaderID); inStream.Read(leaderID);
LOG("Accepted invite: %llu -> %llu (%d)", playerID, leaderID, declined); LOG("Invite reponse received: %llu -> %llu (%d)", playerID, leaderID, declined);
if (declined) { if (declined) {
return; return;
@@ -722,14 +757,15 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
const auto& data = Game::playerContainer.GetPlayerData(playerID); const auto& data = Game::playerContainer.GetPlayerData(playerID);
if (team != nullptr && data) { if (team != nullptr && data) {
LOG_DEBUG("Player %llu is requesting team status", playerID);
if (team->local && data.zoneID.GetMapID() != team->zoneId.GetMapID() && data.zoneID.GetCloneID() != team->zoneId.GetCloneID()) { if (team->local && data.zoneID.GetMapID() != team->zoneId.GetMapID() && data.zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
Game::playerContainer.RemoveMember(team, playerID, false, false, true, true); Game::playerContainer.RemoveMember(team, playerID, false, false, false, true);
return; return;
} }
if (team->memberIDs.size() <= 1 && !team->local) { if (team->memberIDs.size() <= 1 && !team->local) {
Game::playerContainer.DisbandTeam(team); Game::playerContainer.DisbandTeam(team, LWOOBJID_EMPTY, u"");
return; return;
} }
@@ -772,7 +808,7 @@ void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerD
bitStream.Write(LUWString(sender.playerName.c_str())); bitStream.Write(LUWString(sender.playerName.c_str()));
bitStream.Write(sender.playerID); bitStream.Write(sender.playerID);
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -799,7 +835,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool b
bitStream.Write(character); bitStream.Write(character);
} }
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -824,7 +860,7 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
bitStream.Write(character); bitStream.Write(character);
} }
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -841,7 +877,7 @@ void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i
bitStream.Write(i64PlayerID); bitStream.Write(i64PlayerID);
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -870,7 +906,7 @@ void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFr
} }
bitStream.Write(zoneID); bitStream.Write(zoneID);
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -896,7 +932,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
bitStream.Write(character); bitStream.Write(character);
} }
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -917,7 +953,7 @@ void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOO
} }
bitStream.Write(zoneID); bitStream.Write(zoneID);
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -959,7 +995,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
bitStream.Write<uint8_t>(isBestFriend); //isBFF bitStream.Write<uint8_t>(isBestFriend); //isBFF
bitStream.Write<uint8_t>(0); //isFTP bitStream.Write<uint8_t>(0); //isFTP
SystemAddress sysAddr = friendData.sysAddr; SystemAddress sysAddr = friendData.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -981,7 +1017,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
bitStream.Write(LUWString(sender.playerName)); bitStream.Write(LUWString(sender.playerName));
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side. bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -994,7 +1030,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE);
bitStream.Write(responseCode); bitStream.Write(responseCode);
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver. // For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS); bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS);
// Then write the player name // Then write the player name
bitStream.Write(LUWString(sender.playerName)); bitStream.Write(LUWString(sender.playerName));
// Then if this is an acceptance code, write the following extra info. // Then if this is an acceptance code, write the following extra info.
@@ -1004,7 +1040,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
bitStream.Write(isBestFriendRequest); //isBFF bitStream.Write(isBestFriendRequest); //isBFF
bitStream.Write<uint8_t>(0); //isFTP bitStream.Write<uint8_t>(0); //isFTP
} }
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }
@@ -1018,6 +1054,6 @@ void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string
bitStream.Write<uint8_t>(isSuccessful); //isOnline bitStream.Write<uint8_t>(isSuccessful); //isOnline
bitStream.Write(LUWString(personToRemove)); bitStream.Write(LUWString(personToRemove));
SystemAddress sysAddr = receiver.sysAddr; SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET; SEND_PACKET;
} }

View File

@@ -64,12 +64,15 @@ namespace ChatPacketHandler {
void HandleTeamPromote(Packet* packet); void HandleTeamPromote(Packet* packet);
void HandleTeamLootOption(Packet* packet); void HandleTeamLootOption(Packet* packet);
void HandleTeamStatusRequest(Packet* packet); void HandleTeamStatusRequest(Packet* packet);
void OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr);
void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender); void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender);
void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName); void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID); void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID);
void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID); void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
/* Sends a message to the provided `receiver` with information about the updated team. If `i64LeaderID` is not LWOOBJID_EMPTY, the client will update the leader to that new playerID. */
void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName); void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);

View File

@@ -224,6 +224,10 @@ void HandlePacket(Packet* packet) {
if (connection != eConnectionType::CHAT) return; if (connection != eConnectionType::CHAT) return;
inStream.Read(chatMessageID); inStream.Read(chatMessageID);
// Our packing byte wasnt there? Probably a false packet
if (inStream.GetNumberOfUnreadBits() < 8) return;
inStream.IgnoreBytes(1);
switch (chatMessageID) { switch (chatMessageID) {
case MessageType::Chat::GM_MUTE: case MessageType::Chat::GM_MUTE:
Game::playerContainer.MuteUpdate(packet); Game::playerContainer.MuteUpdate(packet);
@@ -322,6 +326,9 @@ void HandlePacket(Packet* packet) {
case MessageType::Chat::SHOW_ALL: case MessageType::Chat::SHOW_ALL:
ChatPacketHandler::HandleShowAll(packet); ChatPacketHandler::HandleShowAll(packet);
break; break;
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
ChatPacketHandler::OnAchievementNotify(inStream, packet->systemAddress);
break;
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE: case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
case MessageType::Chat::WORLD_DISCONNECT_REQUEST: case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE: case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
@@ -357,7 +364,6 @@ void HandlePacket(Packet* packet) {
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT: case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case MessageType::Chat::UGCC_REQUEST: case MessageType::Chat::UGCC_REQUEST:
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE: case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW: case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case MessageType::Chat::PLAYER_READY: case MessageType::Chat::PLAYER_READY:
case MessageType::Chat::GET_DONATION_TOTAL: case MessageType::Chat::GET_DONATION_TOTAL:

View File

@@ -52,7 +52,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
if (!inStream.Read(data.zoneID)) return; if (!inStream.Read(data.zoneID)) return;
if (!inStream.Read(data.muteExpire)) return; if (!inStream.Read(data.muteExpire)) return;
if (!inStream.Read(data.gmLevel)) return; if (!inStream.Read(data.gmLevel)) return;
data.sysAddr = packet->systemAddress; data.worldServerSysAddr = packet->systemAddress;
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
m_PlayerCount++; m_PlayerCount++;
@@ -241,7 +241,7 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
LOG("Tried to add player to team that already had 4 players"); LOG("Tried to add player to team that already had 4 players");
const auto& player = GetPlayerData(playerID); const auto& player = GetPlayerData(playerID);
if (!player) return; if (!player) return;
ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!"); ChatPackets::SendSystemMessage(player.worldServerSysAddr, u"The teams is full! You have not been added to a team!");
return; return;
} }
@@ -284,41 +284,39 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
} }
} }
void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent) { void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID causingPlayerID, bool disband, bool kicked, bool leaving, bool silent) {
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID); LOG_DEBUG("Player %llu is leaving team %i", causingPlayerID, team->teamID);
const auto index = std::ranges::find(team->memberIDs, causingPlayerID);
if (index == team->memberIDs.end()) return; if (index == team->memberIDs.end()) return;
const auto& member = GetPlayerData(playerID);
if (member && !silent) {
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
}
const auto memberName = GetName(playerID);
for (const auto memberId : team->memberIDs) {
if (silent && memberId == playerID) {
continue;
}
const auto& otherMember = GetPlayerData(memberId);
if (!otherMember) continue;
ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName);
}
team->memberIDs.erase(index); team->memberIDs.erase(index);
UpdateTeamsOnWorld(team, false); const auto& member = GetPlayerData(causingPlayerID);
const auto causingMemberName = GetName(causingPlayerID);
if (member && !silent) {
ChatPacketHandler::SendTeamRemovePlayer(member, disband, kicked, leaving, team->local, LWOOBJID_EMPTY, causingPlayerID, causingMemberName);
}
if (team->memberIDs.size() <= 1) { if (team->memberIDs.size() <= 1) {
DisbandTeam(team); DisbandTeam(team, causingPlayerID, causingMemberName);
} else { } else /* team has enough members to be a team still */ {
if (playerID == team->leaderID) { team->leaderID = (causingPlayerID == team->leaderID) ? team->memberIDs[0] : team->leaderID;
PromoteMember(team, team->memberIDs[0]); for (const auto memberId : team->memberIDs) {
if (silent && memberId == causingPlayerID) {
continue;
}
const auto& otherMember = GetPlayerData(memberId);
if (!otherMember) continue;
ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, team->local, team->leaderID, causingPlayerID, causingMemberName);
} }
UpdateTeamsOnWorld(team, false);
} }
} }
@@ -334,20 +332,19 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
} }
} }
void PlayerContainer::DisbandTeam(TeamData* team) { void PlayerContainer::DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName) {
const auto index = std::find(GetTeams().begin(), GetTeams().end(), team); const auto index = std::ranges::find(GetTeams(), team);
if (index == GetTeams().end()) return; if (index == GetTeams().end()) return;
LOG_DEBUG("Disbanding team %i", (*index)->teamID);
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
if (!otherMember) continue; if (!otherMember) continue;
const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember.playerName);
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember.playerID, memberName); ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, causingPlayerID, causingPlayerName);
} }
UpdateTeamsOnWorld(team, true); UpdateTeamsOnWorld(team, true);

View File

@@ -42,7 +42,7 @@ struct PlayerData {
return muteExpire == 1 || muteExpire > time(NULL); return muteExpire == 1 || muteExpire > time(NULL);
} }
SystemAddress sysAddr{}; SystemAddress worldServerSysAddr{};
LWOZONEID zoneID{}; LWOZONEID zoneID{};
LWOOBJID playerID = LWOOBJID_EMPTY; LWOOBJID playerID = LWOOBJID_EMPTY;
time_t muteExpire = 0; time_t muteExpire = 0;
@@ -91,7 +91,7 @@ public:
void AddMember(TeamData* team, LWOOBJID playerID); void AddMember(TeamData* team, LWOOBJID playerID);
void RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent = false); void RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent = false);
void PromoteMember(TeamData* team, LWOOBJID newLeader); void PromoteMember(TeamData* team, LWOOBJID newLeader);
void DisbandTeam(TeamData* team); void DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName);
void TeamStatusUpdate(TeamData* team); void TeamStatusUpdate(TeamData* team);
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam); void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
std::u16string GetName(LWOOBJID playerID); std::u16string GetName(LWOOBJID playerID);

View File

@@ -98,6 +98,7 @@ public:
constexpr LWOZONEID() noexcept = default; constexpr LWOZONEID() noexcept = default;
constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; } constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; }
constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; } constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; }
constexpr bool operator==(const LWOZONEID&) const = default;
private: private:
LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc... LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc...

View File

@@ -204,6 +204,7 @@ void Character::DoQuickXMLDataParse() {
while (currentChild) { while (currentChild) {
const auto* temp = currentChild->Attribute("v"); const auto* temp = currentChild->Attribute("v");
const auto* id = currentChild->Attribute("id"); const auto* id = currentChild->Attribute("id");
const auto* si = currentChild->Attribute("si");
if (temp && id) { if (temp && id) {
uint32_t index = 0; uint32_t index = 0;
uint64_t value = 0; uint64_t value = 0;
@@ -212,6 +213,9 @@ void Character::DoQuickXMLDataParse() {
value = std::stoull(temp); value = std::stoull(temp);
m_PlayerFlags.insert(std::make_pair(index, value)); m_PlayerFlags.insert(std::make_pair(index, value));
} else if (si) {
auto value = GeneralUtils::TryParse<uint32_t>(si);
if (value) m_SessionFlags.insert(value.value());
} }
currentChild = currentChild->NextSiblingElement(); currentChild = currentChild->NextSiblingElement();
} }

View File

@@ -27,6 +27,8 @@
#include "Character.h" #include "Character.h"
#include "CDMissionEmailTable.h" #include "CDMissionEmailTable.h"
#include "ChatPackets.h"
#include "PlayerManager.h"
Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) { Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
m_MissionComponent = missionComponent; m_MissionComponent = missionComponent;
@@ -355,12 +357,25 @@ void Mission::Complete(const bool yieldRewards) {
for (const auto& email : missionEmails) { for (const auto& email : missionEmails) {
const auto missionEmailBase = "MissionEmail_" + std::to_string(email.ID) + "_"; const auto missionEmailBase = "MissionEmail_" + std::to_string(email.ID) + "_";
if (email.messageType == 1) { if (email.messageType == 1 /* Send an email to the player */) {
const auto subject = "%[" + missionEmailBase + "subjectText]"; const auto subject = "%[" + missionEmailBase + "subjectText]";
const auto body = "%[" + missionEmailBase + "bodyText]"; const auto body = "%[" + missionEmailBase + "bodyText]";
const auto sender = "%[" + missionEmailBase + "senderName]"; const auto sender = "%[" + missionEmailBase + "senderName]";
Mail::SendMail(LWOOBJID_EMPTY, sender, GetAssociate(), subject, body, email.attachmentLOT, 1); Mail::SendMail(LWOOBJID_EMPTY, sender, GetAssociate(), subject, body, email.attachmentLOT, 1);
} else if (email.messageType == 2 /* Send an announcement in chat */) {
auto* character = entity->GetCharacter();
ChatPackets::AchievementNotify notify{};
notify.missionEmailID = email.ID;
notify.earningPlayerID = entity->GetObjectID();
notify.earnerName.string = character ? GeneralUtils::ASCIIToUTF16(character->GetName()) : u"";
// Manual write since it's sent to chat server and not a game client
RakNet::BitStream bitstream;
notify.WriteHeader(bitstream);
notify.Serialize(bitstream);
Game::chatServer->Send(&bitstream, HIGH_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }
} }
} }

View File

@@ -777,7 +777,7 @@ void SlashCommandHandler::Startup() {
.info = "Crashes the server", .info = "Crashes the server",
.aliases = { "crash", "pumpkin" }, .aliases = { "crash", "pumpkin" },
.handle = DEVGMCommands::Crash, .handle = DEVGMCommands::Crash,
.requiredLevel = eGameMasterLevel::DEVELOPER .requiredLevel = eGameMasterLevel::OPERATOR
}; };
RegisterCommand(CrashCommand); RegisterCommand(CrashCommand);
@@ -1444,4 +1444,13 @@ void SlashCommandHandler::Startup() {
.requiredLevel = eGameMasterLevel::CIVILIAN .requiredLevel = eGameMasterLevel::CIVILIAN
}; };
RegisterCommand(removeIgnoreCommand); RegisterCommand(removeIgnoreCommand);
Command shutdownCommand{
.help = "Shuts this world down",
.info = "Shuts this world down",
.aliases = {"shutdown"},
.handle = DEVGMCommands::Shutdown,
.requiredLevel = eGameMasterLevel::DEVELOPER
};
RegisterCommand(shutdownCommand);
} }

View File

@@ -1622,4 +1622,10 @@ namespace DEVGMCommands {
} }
} }
} }
void Shutdown(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
auto* character = entity->GetCharacter();
if (character) LOG("Mythran (%s) has shutdown the world", character->GetName().c_str());
Game::OnSignal(-1);
}
}; };

View File

@@ -73,6 +73,7 @@ namespace DEVGMCommands {
void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args); void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args); void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args); void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Shutdown(Entity* entity, const SystemAddress& sysAddr, const std::string args);
} }
#endif //!DEVGMCOMMANDS_H #endif //!DEVGMCOMMANDS_H

View File

@@ -273,6 +273,16 @@ Instance* InstanceManager::FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID
return nullptr; return nullptr;
} }
Instance* InstanceManager::FindInstanceWithPrivate(LWOMAPID mapID, LWOINSTANCEID instanceID) {
for (Instance* i : m_Instances) {
if (i && i->GetMapID() == mapID && i->GetInstanceID() == instanceID && !i->GetShutdownComplete() && !i->GetIsShuttingDown()) {
return i;
}
}
return nullptr;
}
Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password) { Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password) {
auto* instance = FindPrivateInstance(password); auto* instance = FindPrivateInstance(password);

View File

@@ -76,25 +76,25 @@ public:
void Shutdown(); void Shutdown();
private: private:
std::string m_IP; std::string m_IP{};
uint32_t m_Port; uint32_t m_Port{};
LWOZONEID m_ZoneID; LWOZONEID m_ZoneID{};
int m_MaxClientsSoftCap; int m_MaxClientsSoftCap{};
int m_MaxClientsHardCap; int m_MaxClientsHardCap{};
int m_CurrentClientCount; int m_CurrentClientCount{};
std::vector<Player> m_Players; std::vector<Player> m_Players{};
SystemAddress m_SysAddr; SystemAddress m_SysAddr{};
bool m_Ready; bool m_Ready{};
bool m_IsShuttingDown; bool m_IsShuttingDown{};
std::vector<PendingInstanceRequest> m_PendingRequests; std::vector<PendingInstanceRequest> m_PendingRequests{};
std::vector<PendingInstanceRequest> m_PendingAffirmations; std::vector<PendingInstanceRequest> m_PendingAffirmations{};
uint32_t m_AffirmationTimeout; uint32_t m_AffirmationTimeout{};
bool m_IsPrivate; bool m_IsPrivate{};
std::string m_Password; std::string m_Password{};
bool m_Shutdown; bool m_Shutdown{};
//Private functions: //Private functions:
}; };
@@ -125,6 +125,7 @@ public:
Instance* FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId = 0); Instance* FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId = 0);
Instance* FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID); Instance* FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID);
Instance* FindInstanceWithPrivate(LWOMAPID mapID, LWOINSTANCEID instanceID);
Instance* CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password); Instance* CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password);
Instance* FindPrivateInstance(const std::string& password); Instance* FindPrivateInstance(const std::string& password);

View File

@@ -42,6 +42,7 @@
#include "Server.h" #include "Server.h"
#include "CDZoneTableTable.h" #include "CDZoneTableTable.h"
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "StringifiedEnum.h"
#ifdef DARKFLAME_PLATFORM_UNIX #ifdef DARKFLAME_PLATFORM_UNIX
@@ -212,6 +213,13 @@ int main(int argc, char** argv) {
// Run migrations should any need to be run. // Run migrations should any need to be run.
MigrationRunner::RunSQLiteMigrations(); MigrationRunner::RunSQLiteMigrations();
// Check for the --migrations-only flag
if ((argc > 1 &&
(strcmp(argv[1], "--migrations-only") == 0 || strcmp(argv[1], "-m") == 0))) {
LOG("Migrations only flag detected. Exiting.");
return EXIT_SUCCESS;
}
//If the first command line argument is -a or --account then make the user //If the first command line argument is -a or --account then make the user
//input a username and password, with the password being hidden. //input a username and password, with the password being hidden.
bool createAccount = Database::Get()->GetAccountCount() == 0 && Game::config->GetValue("skip_account_creation") != "1"; bool createAccount = Database::Get()->GetAccountCount() == 0 && Game::config->GetValue("skip_account_creation") != "1";
@@ -556,7 +564,7 @@ void HandlePacket(Packet* packet) {
Instance* in = Game::im->GetInstance(zoneID, false, zoneClone); Instance* in = Game::im->GetInstance(zoneID, false, zoneClone);
for (auto* instance : Game::im->GetInstances()) { for (auto* instance : Game::im->GetInstances()) {
LOG("Instance: %i/%i/%i -> %i", instance->GetMapID(), instance->GetCloneID(), instance->GetInstanceID(), instance == in); LOG("Instance: %i/%i/%i -> %i %s", instance->GetMapID(), instance->GetCloneID(), instance->GetInstanceID(), instance == in, instance->GetSysAddr().ToString());
} }
if (in && !in->GetIsReady()) //Instance not ready, make a pending request if (in && !in->GetIsReady()) //Instance not ready, make a pending request
@@ -597,15 +605,10 @@ void HandlePacket(Packet* packet) {
if (!Game::im->IsPortInUse(theirPort)) { if (!Game::im->IsPortInUse(theirPort)) {
Instance* in = new Instance(theirIP.string, theirPort, theirZoneID, theirInstanceID, 0, 12, 12); Instance* in = new Instance(theirIP.string, theirPort, theirZoneID, theirInstanceID, 0, 12, 12);
SystemAddress copy; in->SetSysAddr(packet->systemAddress);
copy.binaryAddress = packet->systemAddress.binaryAddress;
copy.port = packet->systemAddress.port;
in->SetSysAddr(copy);
Game::im->AddInstance(in); Game::im->AddInstance(in);
} else { } else {
auto instance = Game::im->FindInstance( auto* instance = Game::im->FindInstanceWithPrivate(theirZoneID, static_cast<LWOINSTANCEID>(theirInstanceID));
theirZoneID, static_cast<uint16_t>(theirInstanceID));
if (instance) { if (instance) {
instance->SetSysAddr(packet->systemAddress); instance->SetSysAddr(packet->systemAddress);
} }
@@ -613,22 +616,14 @@ void HandlePacket(Packet* packet) {
} }
if (theirServerType == ServerType::Chat) { if (theirServerType == ServerType::Chat) {
SystemAddress copy; chatServerMasterPeerSysAddr = packet->systemAddress;
copy.binaryAddress = packet->systemAddress.binaryAddress;
copy.port = packet->systemAddress.port;
chatServerMasterPeerSysAddr = copy;
} }
if (theirServerType == ServerType::Auth) { if (theirServerType == ServerType::Auth) {
SystemAddress copy; authServerMasterPeerSysAddr = packet->systemAddress;
copy.binaryAddress = packet->systemAddress.binaryAddress;
copy.port = packet->systemAddress.port;
authServerMasterPeerSysAddr = copy;
} }
LOG("Received server info, instance: %i port: %i", theirInstanceID, theirPort); LOG("Received %s server info, instance: %i port: %i", StringifiedEnum::ToString(theirServerType).data(), theirInstanceID, theirPort);
break; break;
} }
@@ -692,7 +687,7 @@ void HandlePacket(Packet* packet) {
if (instance) { if (instance) {
instance->AddPlayer(Player()); instance->AddPlayer(Player());
} else { } else {
printf("Instance missing? What?"); LOG("Instance missing? What?");
} }
break; break;
} }
@@ -733,8 +728,8 @@ void HandlePacket(Packet* packet) {
inStream.Read<char>(character); inStream.Read<char>(character);
password += character; password += character;
} }
auto* newInst = Game::im->CreatePrivateInstance(mapId, cloneId, password.c_str());
Game::im->CreatePrivateInstance(mapId, cloneId, password.c_str()); LOG("Creating private zone %i/%i/%i with password %s", newInst->GetMapID(), newInst->GetCloneID(), newInst->GetInstanceID(), password.c_str());
break; break;
} }
@@ -835,11 +830,10 @@ void HandlePacket(Packet* packet) {
} }
case MessageType::Master::SHUTDOWN_RESPONSE: { case MessageType::Master::SHUTDOWN_RESPONSE: {
RakNet::BitStream inStream(packet->data, packet->length, false); CINSTREAM_SKIP_HEADER;
uint64_t header = inStream.Read(header);
auto* instance = Game::im->GetInstanceBySysAddr(packet->systemAddress); auto* instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
LOG("Got shutdown response from %s", packet->systemAddress.ToString());
if (instance == nullptr) { if (instance == nullptr) {
return; return;
} }

View File

@@ -107,3 +107,44 @@ void ChatPackets::Announcement::Send() {
bitStream.Write(message); bitStream.Write(message);
SEND_PACKET_BROADCAST; SEND_PACKET_BROADCAST;
} }
void ChatPackets::AchievementNotify::Serialize(RakNet::BitStream& bitstream) const {
bitstream.Write<uint64_t>(0); // Packing
bitstream.Write<uint32_t>(0); // Packing
bitstream.Write<uint8_t>(0); // Packing
bitstream.Write(targetPlayerName);
bitstream.Write<uint64_t>(0); // Packing / No way to know meaning because of not enough data.
bitstream.Write<uint32_t>(0); // Packing / No way to know meaning because of not enough data.
bitstream.Write<uint16_t>(0); // Packing / No way to know meaning because of not enough data.
bitstream.Write<uint8_t>(0); // Packing / No way to know meaning because of not enough data.
bitstream.Write(missionEmailID);
bitstream.Write(earningPlayerID);
bitstream.Write(earnerName);
}
bool ChatPackets::AchievementNotify::Deserialize(RakNet::BitStream& bitstream) {
bitstream.IgnoreBytes(13);
VALIDATE_READ(bitstream.Read(targetPlayerName));
bitstream.IgnoreBytes(15);
VALIDATE_READ(bitstream.Read(missionEmailID));
VALIDATE_READ(bitstream.Read(earningPlayerID));
VALIDATE_READ(bitstream.Read(earnerName));
return true;
}
void ChatPackets::TeamInviteInitialResponse::Serialize(RakNet::BitStream& bitstream) const {
bitstream.Write<uint8_t>(inviteFailedToSend);
bitstream.Write(playerName);
}
void ChatPackets::SendRoutedMsg(const LUBitStream& msg, const LWOOBJID targetID, const SystemAddress& sysAddr) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(targetID);
// Now write the actual packet
msg.WriteHeader(bitStream);
msg.Serialize(bitStream);
Game::server->Send(bitStream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
}

View File

@@ -10,6 +10,8 @@ struct SystemAddress;
#include <string> #include <string>
#include "dCommonVars.h" #include "dCommonVars.h"
#include "MessageType/Chat.h"
#include "BitStreamUtils.h"
struct ShowAllRequest{ struct ShowAllRequest{
LWOOBJID requestor = LWOOBJID_EMPTY; LWOOBJID requestor = LWOOBJID_EMPTY;
@@ -34,9 +36,29 @@ namespace ChatPackets {
void Send(); void Send();
}; };
struct AchievementNotify : public LUBitStream {
LUWString targetPlayerName{};
uint32_t missionEmailID{};
LWOOBJID earningPlayerID{};
LUWString earnerName{};
AchievementNotify() : LUBitStream(eConnectionType::CHAT, MessageType::Chat::ACHIEVEMENT_NOTIFY) {}
void Serialize(RakNet::BitStream& bitstream) const override;
bool Deserialize(RakNet::BitStream& bitstream) override;
};
struct TeamInviteInitialResponse : public LUBitStream {
bool inviteFailedToSend{};
LUWString playerName{};
TeamInviteInitialResponse() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::TEAM_INVITE_INITIAL_RESPONSE) {}
void Serialize(RakNet::BitStream& bitstream) const override;
// No Deserialize needed on our end
};
void SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message); void SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message);
void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false); void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false);
void SendMessageFail(const SystemAddress& sysAddr); void SendMessageFail(const SystemAddress& sysAddr);
void SendRoutedMsg(const LUBitStream& msg, const LWOOBJID targetID, const SystemAddress& sysAddr);
}; };
#endif // CHATPACKETS_H #endif // CHATPACKETS_H

View File

@@ -503,7 +503,7 @@ void BaseSurvivalServer::ActivateSpawnerNetwork(SpawnerNetworkCollection& spawne
if (!possibleSpawners.empty()) { if (!possibleSpawners.empty()) {
auto* spawnerObject = possibleSpawners.at(0); auto* spawnerObject = possibleSpawners.at(0);
spawnerObject->Activate(); spawnerObject->Activate();
spawnerObject->Reset(); spawnerObject->SoftReset();
} }
} }
} }

View File

@@ -9,9 +9,10 @@ void ActSharkPlayerDeathTrigger::OnFireEventServerSide(Entity* self, Entity* sen
auto missionComponent = sender->GetComponent<MissionComponent>(); auto missionComponent = sender->GetComponent<MissionComponent>();
if (!missionComponent) return; if (!missionComponent) return;
missionComponent->Progress(eMissionTaskType::SCRIPT, 8419); // This check is only needed because dlu doesnt have proper collision checks on rotated phantom physics
if (sender->GetIsDead() || !sender->GetPlayerReadyForUpdates()) return; //Don't kill already dead players or players not ready if (sender->GetIsDead() || !sender->GetPlayerReadyForUpdates()) return; //Don't kill already dead players or players not ready
missionComponent->Progress(eMissionTaskType::SCRIPT, 8419);
if (sender->GetCharacter()) { if (sender->GetCharacter()) {
sender->Smash(self->GetObjectID(), eKillType::VIOLENT, u"big-shark-death"); sender->Smash(self->GetObjectID(), eKillType::VIOLENT, u"big-shark-death");

View File

@@ -3,69 +3,68 @@
#include "CDClientManager.h" #include "CDClientManager.h"
#include "UserManager.h" #include "UserManager.h"
#define SOCIAL { lowFrameDelta } #define SOCIAL lowFrameDelta
#define SOCIAL_HUB { mediumFrameDelta } //Added to compensate for the large playercounts in NS and NT #define SOCIAL_HUB mediumFrameDelta //Added to compensate for the large playercounts in NS and NT
#define BATTLE { highFrameDelta } #define BATTLE highFrameDelta
#define BATTLE_INSTANCE { mediumFrameDelta } #define BATTLE_INSTANCE mediumFrameDelta
#define RACE { highFrameDelta } #define RACE highFrameDelta
#define PROPERTY { lowFrameDelta } #define PROPERTY lowFrameDelta
PerformanceProfile PerformanceManager::m_CurrentProfile = SOCIAL; namespace {
PerformanceProfile m_CurrentProfile = SOCIAL;
PerformanceProfile m_DefaultProfile = SOCIAL;
PerformanceProfile m_InactiveProfile = lowFrameDelta;
std::map<LWOMAPID, PerformanceProfile> m_Profiles = {
// VE
{ 1000, SOCIAL },
PerformanceProfile PerformanceManager::m_DefaultProfile = SOCIAL; // AG
{ 1100, BATTLE },
{ 1101, BATTLE_INSTANCE },
{ 1102, BATTLE_INSTANCE },
{ 1150, PROPERTY },
{ 1151, PROPERTY },
PerformanceProfile PerformanceManager::m_InactiveProfile = { lowFrameDelta }; // NS
{ 1200, SOCIAL_HUB },
{ 1201, SOCIAL },
{ 1203, RACE },
{ 1204, BATTLE_INSTANCE },
{ 1250, PROPERTY },
{ 1251, PROPERTY },
std::map<LWOMAPID, PerformanceProfile> PerformanceManager::m_Profiles = { // GF
// VE { 1300, BATTLE },
{ 1000, SOCIAL }, { 1302, BATTLE_INSTANCE },
{ 1303, BATTLE_INSTANCE },
{ 1350, PROPERTY },
// AG // FV
{ 1100, BATTLE }, { 1400, BATTLE },
{ 1101, BATTLE_INSTANCE }, { 1402, BATTLE_INSTANCE },
{ 1102, BATTLE_INSTANCE }, { 1403, RACE },
{ 1150, PROPERTY }, { 1450, PROPERTY },
{ 1151, PROPERTY },
// NS // LUP
{ 1200, SOCIAL_HUB }, { 1600, SOCIAL },
{ 1201, SOCIAL }, { 1601, SOCIAL },
{ 1203, RACE }, { 1602, SOCIAL },
{ 1204, BATTLE_INSTANCE }, { 1603, SOCIAL },
{ 1250, PROPERTY }, { 1604, SOCIAL },
{ 1251, PROPERTY },
// GF // LEGO Club
{ 1300, BATTLE }, { 1700, SOCIAL },
{ 1302, BATTLE_INSTANCE },
{ 1303, BATTLE_INSTANCE },
{ 1350, PROPERTY },
// FV // AM
{ 1400, BATTLE }, { 1800, BATTLE },
{ 1402, BATTLE_INSTANCE },
{ 1403, RACE },
{ 1450, PROPERTY },
// LUP // NT
{ 1600, SOCIAL }, { 1900, SOCIAL_HUB },
{ 1601, SOCIAL },
{ 1602, SOCIAL },
{ 1603, SOCIAL },
{ 1604, SOCIAL },
// LEGO Club // NJ
{ 1700, SOCIAL }, { 2000, BATTLE },
{ 2001, BATTLE_INSTANCE },
// AM };
{ 1800, BATTLE },
// NT
{ 1900, SOCIAL_HUB },
// NJ
{ 2000, BATTLE },
{ 2001, BATTLE_INSTANCE },
}; };
void PerformanceManager::SelectProfile(LWOMAPID mapID) { void PerformanceManager::SelectProfile(LWOMAPID mapID) {
@@ -74,16 +73,16 @@ void PerformanceManager::SelectProfile(LWOMAPID mapID) {
if (zoneTable) { if (zoneTable) {
const CDZoneTable* zone = zoneTable->Query(mapID); const CDZoneTable* zone = zoneTable->Query(mapID);
if (zone) { if (zone) {
if (zone->serverPhysicsFramerate == "high"){ if (zone->serverPhysicsFramerate == "high") {
m_CurrentProfile = { highFrameDelta }; m_CurrentProfile = highFrameDelta;
return; return;
} }
if (zone->serverPhysicsFramerate == "medium"){ if (zone->serverPhysicsFramerate == "medium") {
m_CurrentProfile = { mediumFrameDelta }; m_CurrentProfile = mediumFrameDelta;
return; return;
} }
if (zone->serverPhysicsFramerate == "low"){ if (zone->serverPhysicsFramerate == "low") {
m_CurrentProfile = { lowFrameDelta }; m_CurrentProfile = lowFrameDelta;
return; return;
} }
} }
@@ -91,18 +90,9 @@ void PerformanceManager::SelectProfile(LWOMAPID mapID) {
// Fall back to hardcoded list and defaults // Fall back to hardcoded list and defaults
const auto pair = m_Profiles.find(mapID); const auto pair = m_Profiles.find(mapID);
if (pair == m_Profiles.end()) { m_CurrentProfile = pair == m_Profiles.end() ? m_DefaultProfile : pair->second;
m_CurrentProfile = m_DefaultProfile;
return;
}
m_CurrentProfile = pair->second;
} }
uint32_t PerformanceManager::GetServerFrameDelta() { uint32_t PerformanceManager::GetServerFrameDelta() {
if (UserManager::Instance()->GetUserCount() == 0) { return UserManager::Instance()->GetUserCount() == 0 ? m_CurrentProfile : m_InactiveProfile;
return m_InactiveProfile.serverFrameDelta;
}
return m_CurrentProfile.serverFrameDelta;
} }

View File

@@ -1,22 +1,16 @@
#pragma once #pragma once
#include <cstdint>
#include <map> #include <map>
#include "dCommonVars.h" #include "dCommonVars.h"
struct PerformanceProfile { using PerformanceProfile = uint32_t;
uint32_t serverFrameDelta;
}; namespace PerformanceManager {
/* Sets a performance profile for a given world. */
class PerformanceManager { void SelectProfile(LWOMAPID mapID);
public:
static void SelectProfile(LWOMAPID mapID); /* Gets the frame millisecond delta. Will return a higher value if the zone is empty. */
uint32_t GetServerFrameDelta();
static uint32_t GetServerFrameDelta();
private:
static PerformanceProfile m_CurrentProfile;
static PerformanceProfile m_DefaultProfile;
static PerformanceProfile m_InactiveProfile;
static std::map<LWOMAPID, PerformanceProfile> m_Profiles;
}; };

View File

@@ -98,9 +98,22 @@ namespace Game {
std::string projectVersion = PROJECT_VERSION; std::string projectVersion = PROJECT_VERSION;
} // namespace Game } // namespace Game
bool chatDisabled = false; namespace {
bool chatConnected = false; struct TempSessionInfo {
bool worldShutdownSequenceComplete = false; SystemAddress sysAddr;
std::string hash;
};
std::map<std::string, TempSessionInfo> g_PendingUsers;
uint32_t g_InstanceID = 0;
uint32_t g_CloneID = 0;
std::string g_DatabaseChecksum = "";
bool g_ChatDisabled = false;
bool g_ChatConnected = false;
bool g_WorldShutdownSequenceComplete = false;
}; // namespace anonymous
void WorldShutdownSequence(); void WorldShutdownSequence();
void WorldShutdownProcess(uint32_t zoneId); void WorldShutdownProcess(uint32_t zoneId);
void FinalizeShutdown(); void FinalizeShutdown();
@@ -110,16 +123,6 @@ void HandlePacketChat(Packet* packet);
void HandleMasterPacket(Packet* packet); void HandleMasterPacket(Packet* packet);
void HandlePacket(Packet* packet); void HandlePacket(Packet* packet);
struct tempSessionInfo {
SystemAddress sysAddr;
std::string hash;
};
std::map<std::string, tempSessionInfo> m_PendingUsers;
uint32_t instanceID = 0;
uint32_t g_CloneID = 0;
std::string databaseChecksum = "";
int main(int argc, char** argv) { int main(int argc, char** argv) {
Diagnostics::SetProcessName("World"); Diagnostics::SetProcessName("World");
Diagnostics::SetProcessFileName(argv[0]); Diagnostics::SetProcessFileName(argv[0]);
@@ -137,27 +140,31 @@ int main(int argc, char** argv) {
uint32_t ourPort = 2007; uint32_t ourPort = 2007;
//Check our arguments: //Check our arguments:
for (int32_t i = 0; i < argc; ++i) { for (int32_t i = 0; (i + 1) < argc; i++) {
std::string argument(argv[i]); std::string argument(argv[i]);
const auto valOptional = GeneralUtils::TryParse<uint32_t>(argv[i + 1]);
if (!valOptional) {
continue;
}
if (argument == "-zone") zoneID = atoi(argv[i + 1]); if (argument == "-zone") zoneID = valOptional.value_or(1000);
if (argument == "-instance") instanceID = atoi(argv[i + 1]); else if (argument == "-instance") g_InstanceID = valOptional.value_or(0);
if (argument == "-clone") cloneID = atoi(argv[i + 1]); else if (argument == "-clone") cloneID = valOptional.value_or(0);
if (argument == "-maxclients") maxClients = atoi(argv[i + 1]); else if (argument == "-maxclients") maxClients = valOptional.value_or(8);
if (argument == "-port") ourPort = atoi(argv[i + 1]); else if (argument == "-port") ourPort = valOptional.value_or(2007);
} }
Game::config = new dConfig("worldconfig.ini"); Game::config = new dConfig("worldconfig.ini");
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Server::SetupLogger("WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(instanceID)); Server::SetupLogger("WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(g_InstanceID));
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
LOG("Starting World server..."); LOG("Starting World server...");
LOG("Version: %s", Game::projectVersion.c_str()); LOG("Version: %s", Game::projectVersion.c_str());
LOG("Compiled on: %s", __TIMESTAMP__); LOG("Compiled on: %s", __TIMESTAMP__);
if (Game::config->GetValue("disable_chat") == "1") chatDisabled = true; g_ChatDisabled = Game::config->GetValue("disable_chat") == "1";
try { try {
std::string clientPathStr = Game::config->GetValue("client_location"); std::string clientPathStr = Game::config->GetValue("client_location");
@@ -167,7 +174,7 @@ int main(int argc, char** argv) {
clientPath = BinaryPathFinder::GetBinaryDir() / clientPath; clientPath = BinaryPathFinder::GetBinaryDir() / clientPath;
} }
Game::assetManager = new AssetManager(clientPath); Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) { } catch (const std::exception& ex) {
LOG("Got an error while setting up assets: %s", ex.what()); LOG("Got an error while setting up assets: %s", ex.what());
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -176,11 +183,14 @@ int main(int argc, char** argv) {
// Connect to CDClient // Connect to CDClient
try { try {
CDClientDatabase::Connect((BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").string()); CDClientDatabase::Connect((BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").string());
} catch (CppSQLite3Exception& e) { } catch (const CppSQLite3Exception& e) {
LOG("Unable to connect to CDServer SQLite Database"); LOG("Unable to connect to CDServer SQLite Database");
LOG("Error: %s", e.errorMessage()); LOG("Error: %s", e.errorMessage());
LOG("Error Code: %i", e.errorCode()); LOG("Error Code: %i", e.errorCode());
return EXIT_FAILURE; return EXIT_FAILURE;
} catch (const std::exception& e) {
LOG("Caught generic exception %s when connecting to CDClient", e.what());
return EXIT_FAILURE;
} }
CDClientManager::LoadValuesFromDatabase(); CDClientManager::LoadValuesFromDatabase();
@@ -194,7 +204,7 @@ int main(int argc, char** argv) {
//Connect to the MySQL Database: //Connect to the MySQL Database:
try { try {
Database::Connect(); Database::Connect();
} catch (std::exception& ex) { } catch (const std::exception& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -203,7 +213,7 @@ int main(int argc, char** argv) {
std::string masterIP = "localhost"; std::string masterIP = "localhost";
uint32_t masterPort = 1000; uint32_t masterPort = 1000;
std::string masterPassword; std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo(); const auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) { if (masterInfo) {
masterIP = masterInfo->ip; masterIP = masterInfo->ip;
@@ -216,13 +226,25 @@ int main(int argc, char** argv) {
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false); const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, masterPassword, zoneID); Game::server = new dServer(masterIP,
ourPort,
g_InstanceID,
maxClients,
false /* Is internal */,
true /* Use encryption */,
Game::logger,
masterIP,
masterPort,
ServerType::World,
Game::config,
&Game::lastSignal,
masterPassword,
zoneID);
//Connect to the chat server: //Connect to the chat server:
uint32_t chatPort = 1501; uint32_t chatPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("chat_server_port")).value_or(1501);
if (Game::config->GetValue("chat_server_port") != "") chatPort = std::atoi(Game::config->GetValue("chat_server_port").c_str());
auto chatSock = SocketDescriptor(static_cast<uint16_t>(ourPort + 2), 0); auto chatSock = SocketDescriptor(static_cast<uint16_t>(ourPort + 2), NULL);
Game::chatServer = RakNetworkFactory::GetRakPeerInterface(); Game::chatServer = RakNetworkFactory::GetRakPeerInterface();
Game::chatServer->Startup(1, 30, &chatSock, 1); Game::chatServer->Startup(1, 30, &chatSock, 1);
Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL))); Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
@@ -260,9 +282,8 @@ int main(int argc, char** argv) {
//Load our level: //Load our level:
if (zoneID != 0) { if (zoneID != 0) {
dpWorld::Initialize(zoneID); dpWorld::Initialize(zoneID);
Game::zoneManager->Initialize(LWOZONEID(zoneID, instanceID, cloneID)); Game::zoneManager->Initialize(LWOZONEID(zoneID, g_InstanceID, cloneID));
g_CloneID = cloneID; g_CloneID = cloneID;
} else { } else {
Game::entityManager->Initialize(); Game::entityManager->Initialize();
} }
@@ -286,11 +307,11 @@ int main(int argc, char** argv) {
const char* nullTerminateBuffer = "\0"; const char* nullTerminateBuffer = "\0";
md5.update(nullTerminateBuffer, 1); // null terminate the data md5.update(nullTerminateBuffer, 1); // null terminate the data
md5.finalize(); md5.finalize();
databaseChecksum = md5.hexdigest(); g_DatabaseChecksum = md5.hexdigest();
LOG("FDB Checksum calculated as: %s", databaseChecksum.c_str()); LOG("FDB Checksum calculated as: %s", g_DatabaseChecksum.c_str());
} }
if (databaseChecksum.empty()) { if (g_DatabaseChecksum.empty()) {
LOG("check_fdb is on but no fdb file found."); LOG("check_fdb is on but no fdb file found.");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -334,7 +355,7 @@ int main(int argc, char** argv) {
float_t ratioBeforeToAfter = static_cast<float>(currentFrameDelta) / static_cast<float>(newFrameDelta); float_t ratioBeforeToAfter = static_cast<float>(currentFrameDelta) / static_cast<float>(newFrameDelta);
currentFrameDelta = newFrameDelta; currentFrameDelta = newFrameDelta;
currentFramerate = MS_TO_FRAMES(newFrameDelta); currentFramerate = MS_TO_FRAMES(newFrameDelta);
LOG_DEBUG("Framerate for zone/instance/clone %i/%i/%i is now %i", zoneID, instanceID, cloneID, currentFramerate); LOG_DEBUG("Framerate for zone/instance/clone %i/%i/%i is now %i", zoneID, g_InstanceID, cloneID, currentFramerate);
logFlushTime = 15 * currentFramerate; // 15 seconds in frames logFlushTime = 15 * currentFramerate; // 15 seconds in frames
framesSinceLastFlush *= ratioBeforeToAfter; framesSinceLastFlush *= ratioBeforeToAfter;
shutdownTimeout = 10 * 60 * currentFramerate; // 10 minutes in frames shutdownTimeout = 10 * 60 * currentFramerate; // 10 minutes in frames
@@ -367,7 +388,7 @@ int main(int argc, char** argv) {
} else framesSinceMasterDisconnect = 0; } else framesSinceMasterDisconnect = 0;
// Check if we're still connected to chat: // Check if we're still connected to chat:
if (!chatConnected) { if (!g_ChatConnected) {
framesSinceChatDisconnect++; framesSinceChatDisconnect++;
if (framesSinceChatDisconnect >= chatReconnectionTime) { if (framesSinceChatDisconnect >= chatReconnectionTime) {
@@ -404,16 +425,18 @@ int main(int argc, char** argv) {
//Check for packets here: //Check for packets here:
packet = Game::server->ReceiveFromMaster(); packet = Game::server->ReceiveFromMaster();
if (packet) { //We can get messages not handle-able by the dServer class, so handle them if we returned anything. while (packet) { //We can get messages not handle-able by the dServer class, so handle them if we returned anything.
HandleMasterPacket(packet); HandleMasterPacket(packet);
Game::server->DeallocateMasterPacket(packet); Game::server->DeallocateMasterPacket(packet);
packet = Game::server->ReceiveFromMaster();
} }
//Handle our chat packets: //Handle our chat packets:
packet = Game::chatServer->Receive(); packet = Game::chatServer->Receive();
if (packet) { while (packet) {
HandlePacketChat(packet); HandlePacketChat(packet);
Game::chatServer->DeallocatePacket(packet); Game::chatServer->DeallocatePacket(packet);
packet = Game::chatServer->Receive();
} }
//Handle world-specific packets: //Handle world-specific packets:
@@ -421,7 +444,6 @@ int main(int argc, char** argv) {
UserManager::Instance()->DeletePendingRemovals(); UserManager::Instance()->DeletePendingRemovals();
auto t1 = std::chrono::high_resolution_clock::now();
for (uint32_t curPacket = 0; curPacket < maxPacketsToProcess && timeSpent < maxPacketProcessingTime; curPacket++) { for (uint32_t curPacket = 0; curPacket < maxPacketsToProcess && timeSpent < maxPacketProcessingTime; curPacket++) {
packet = Game::server->Receive(); packet = Game::server->Receive();
if (packet) { if (packet) {
@@ -497,20 +519,14 @@ int main(int argc, char** argv) {
Metrics::EndMeasurement(MetricVariable::Sleep); Metrics::EndMeasurement(MetricVariable::Sleep);
if (!ready && Game::server->GetIsConnectedToMaster()) { if (!ready && Game::server->GetIsConnectedToMaster()) {
// Some delay is required here or else we crash the client? LOG("Finished loading world with zone (%i), ready up!", Game::server->GetZoneID());
framesSinceMasterStatus++; MasterPackets::SendWorldReady(Game::server, Game::server->GetZoneID(), Game::server->GetInstanceID());
if (framesSinceMasterStatus >= 200) { ready = true;
LOG("Finished loading world with zone (%i), ready up!", Game::server->GetZoneID());
MasterPackets::SendWorldReady(Game::server, Game::server->GetZoneID(), Game::server->GetInstanceID());
ready = true;
}
} }
if (Game::ShouldShutdown() && !worldShutdownSequenceComplete) { if (Game::ShouldShutdown() && !g_WorldShutdownSequenceComplete) {
WorldShutdownProcess(zoneID); WorldShutdownProcess(zoneID);
break; break;
} }
@@ -527,14 +543,14 @@ void HandlePacketChat(Packet* packet) {
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
LOG("Lost our connection to chat, zone(%i), instance(%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); LOG("Lost our connection to chat, zone(%i), instance(%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
chatConnected = false; g_ChatConnected = false;
} }
if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) { if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) {
LOG("Established connection to chat, zone(%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); LOG("Established connection to chat, zone(%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
Game::chatSysAddr = packet->systemAddress; Game::chatSysAddr = packet->systemAddress;
chatConnected = true; g_ChatConnected = true;
} }
if (packet->data[0] == ID_USER_PACKET_ENUM && packet->length >= 4) { if (packet->data[0] == ID_USER_PACKET_ENUM && packet->length >= 4) {
@@ -634,13 +650,13 @@ void HandlePacketChat(Packet* packet) {
inStream.Read(lootOption); inStream.Read(lootOption);
inStream.Read(memberCount); inStream.Read(memberCount);
LOG("Updating team (%llu), (%i), (%i)", teamID, lootOption, memberCount); LOG("Updating team ID:(%llu), Loot:(%i), #Members:(%i)", teamID, lootOption, memberCount);
for (char i = 0; i < memberCount; i++) { for (char i = 0; i < memberCount; i++) {
LWOOBJID member = LWOOBJID_EMPTY; LWOOBJID member = LWOOBJID_EMPTY;
inStream.Read(member); inStream.Read(member);
members.push_back(member); members.push_back(member);
LOG("Updating team member (%llu)", member); LOG("Added member (%llu) to the team", member);
} }
TeamManager::Instance()->UpdateTeam(teamID, lootOption, members); TeamManager::Instance()->UpdateTeam(teamID, lootOption, members);
@@ -648,7 +664,7 @@ void HandlePacketChat(Packet* packet) {
break; break;
} }
default: default:
LOG("Received an unknown chat: %i", int(packet->data[3])); LOG("Received an unknown chat: %s", StringifiedEnum::ToString(static_cast<MessageType::Chat>(packet->data[3])).data());
} }
} }
} }
@@ -677,8 +693,8 @@ void HandleMasterPacket(Packet* packet) {
inStream.Read(username); inStream.Read(username);
//Find them: //Find them:
auto it = m_PendingUsers.find(username.GetAsString()); auto it = g_PendingUsers.find(username.GetAsString());
if (it == m_PendingUsers.end()) return; if (it == g_PendingUsers.end()) return;
//Convert our key: //Convert our key:
std::string userHash = std::to_string(sessionKey); std::string userHash = std::to_string(sessionKey);
@@ -718,14 +734,14 @@ void HandleMasterPacket(Packet* packet) {
UserManager::Instance()->RequestCharacterList(it->second.sysAddr); UserManager::Instance()->RequestCharacterList(it->second.sysAddr);
} }
m_PendingUsers.erase(username.GetAsString()); g_PendingUsers.erase(username.GetAsString());
//Notify master: //Notify master:
{ {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, MessageType::Master::PLAYER_ADDED); BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, MessageType::Master::PLAYER_ADDED);
bitStream.Write<LWOMAPID>(Game::server->GetZoneID()); bitStream.Write<LWOMAPID>(Game::server->GetZoneID());
bitStream.Write<LWOINSTANCEID>(instanceID); bitStream.Write<LWOINSTANCEID>(g_InstanceID);
Game::server->SendToMaster(bitStream); Game::server->SendToMaster(bitStream);
} }
} }
@@ -828,7 +844,7 @@ void HandlePacket(Packet* packet) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, MessageType::Master::PLAYER_REMOVED); BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, MessageType::Master::PLAYER_REMOVED);
bitStream.Write<LWOMAPID>(Game::server->GetZoneID()); bitStream.Write<LWOMAPID>(Game::server->GetZoneID());
bitStream.Write<LWOINSTANCEID>(instanceID); bitStream.Write<LWOINSTANCEID>(g_InstanceID);
Game::server->SendToMaster(bitStream); Game::server->SendToMaster(bitStream);
} }
@@ -859,7 +875,7 @@ void HandlePacket(Packet* packet) {
inStream.Read(clientDatabaseChecksum); inStream.Read(clientDatabaseChecksum);
// If the check is turned on, validate the client's database checksum. // If the check is turned on, validate the client's database checksum.
if (Game::config->GetValue("check_fdb") == "1" && !databaseChecksum.empty()) { if (Game::config->GetValue("check_fdb") == "1" && !g_DatabaseChecksum.empty()) {
auto accountInfo = Database::Get()->GetAccountInfo(username.GetAsString()); auto accountInfo = Database::Get()->GetAccountInfo(username.GetAsString());
if (!accountInfo) { if (!accountInfo) {
LOG("Client's account does not exist in the database, aborting connection."); LOG("Client's account does not exist in the database, aborting connection.");
@@ -868,7 +884,7 @@ void HandlePacket(Packet* packet) {
} }
// Developers may skip this check // Developers may skip this check
if (clientDatabaseChecksum.string != databaseChecksum) { if (clientDatabaseChecksum.string != g_DatabaseChecksum) {
if (accountInfo->maxGmLevel < eGameMasterLevel::DEVELOPER) { if (accountInfo->maxGmLevel < eGameMasterLevel::DEVELOPER) {
LOG("Client's database checksum does not match the server's, aborting connection."); LOG("Client's database checksum does not match the server's, aborting connection.");
@@ -900,10 +916,10 @@ void HandlePacket(Packet* packet) {
Game::server->SendToMaster(bitStream); Game::server->SendToMaster(bitStream);
//Insert info into our pending list //Insert info into our pending list
tempSessionInfo info; TempSessionInfo info;
info.sysAddr = SystemAddress(packet->systemAddress); info.sysAddr = packet->systemAddress;
info.hash = sessionKey.GetAsString(); info.hash = sessionKey.GetAsString();
m_PendingUsers.insert(std::make_pair(username.GetAsString(), info)); g_PendingUsers[username.GetAsString()] = info;
break; break;
} }
@@ -918,8 +934,8 @@ void HandlePacket(Packet* packet) {
//This loops prevents users who aren't authenticated to double-request the char list, which //This loops prevents users who aren't authenticated to double-request the char list, which
//would make the login screen freeze sometimes. //would make the login screen freeze sometimes.
if (m_PendingUsers.size() > 0) { if (g_PendingUsers.size() > 0) {
for (auto it : m_PendingUsers) { for (const auto& it : g_PendingUsers) {
if (it.second.sysAddr == packet->systemAddress) { if (it.second.sysAddr == packet->systemAddress) {
return; return;
} }
@@ -1303,7 +1319,7 @@ void HandlePacket(Packet* packet) {
} }
case MessageType::World::GENERAL_CHAT_MESSAGE: { case MessageType::World::GENERAL_CHAT_MESSAGE: {
if (chatDisabled) { if (g_ChatDisabled) {
ChatPackets::SendMessageFail(packet->systemAddress); ChatPackets::SendMessageFail(packet->systemAddress);
} else { } else {
auto chatMessage = ClientPackets::HandleChatMessage(packet); auto chatMessage = ClientPackets::HandleChatMessage(packet);
@@ -1396,7 +1412,7 @@ void HandlePacket(Packet* packet) {
} }
void WorldShutdownProcess(uint32_t zoneId) { void WorldShutdownProcess(uint32_t zoneId) {
LOG("Saving map %i instance %i", zoneId, instanceID); LOG("Saving map %i instance %i", zoneId, g_InstanceID);
for (auto i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) { for (auto i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) {
const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i); const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i);
@@ -1421,7 +1437,7 @@ void WorldShutdownProcess(uint32_t zoneId) {
LOG("ALL property data saved for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId()); LOG("ALL property data saved for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId());
} }
LOG("ALL DATA HAS BEEN SAVED FOR ZONE %i INSTANCE %i!", zoneId, instanceID); LOG("ALL DATA HAS BEEN SAVED FOR ZONE %i INSTANCE %i!", zoneId, g_InstanceID);
while (Game::server->GetReplicaManager()->GetParticipantCount() > 0) { while (Game::server->GetReplicaManager()->GetParticipantCount() > 0) {
const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(0); const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(0);
@@ -1432,7 +1448,7 @@ void WorldShutdownProcess(uint32_t zoneId) {
} }
void WorldShutdownSequence() { void WorldShutdownSequence() {
bool shouldShutdown = Game::ShouldShutdown() || worldShutdownSequenceComplete; bool shouldShutdown = Game::ShouldShutdown() || g_WorldShutdownSequenceComplete;
Game::lastSignal = -1; Game::lastSignal = -1;
#ifndef DARKFLAME_PLATFORM_WIN32 #ifndef DARKFLAME_PLATFORM_WIN32
if (shouldShutdown) if (shouldShutdown)
@@ -1443,13 +1459,13 @@ void WorldShutdownSequence() {
if (!Game::logger) return; if (!Game::logger) return;
LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID); LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), g_InstanceID);
WorldShutdownProcess(Game::server->GetZoneID()); WorldShutdownProcess(Game::server->GetZoneID());
FinalizeShutdown(); FinalizeShutdown();
} }
void FinalizeShutdown() { void FinalizeShutdown() {
LOG("Shutdown complete, zone (%i), instance (%i)", Game::server->GetZoneID(), instanceID); LOG("Shutdown complete, zone (%i), instance (%i)", Game::server->GetZoneID(), g_InstanceID);
//Delete our objects here: //Delete our objects here:
Metrics::Clear(); Metrics::Clear();
@@ -1468,7 +1484,7 @@ void FinalizeShutdown() {
if (Game::logger) delete Game::logger; if (Game::logger) delete Game::logger;
Game::logger = nullptr; Game::logger = nullptr;
worldShutdownSequenceComplete = true; g_WorldShutdownSequenceComplete = true;
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

View File

@@ -12,4 +12,5 @@ namespace Game {
SystemAddress chatSysAddr; SystemAddress chatSysAddr;
EntityManager* entityManager = nullptr; EntityManager* entityManager = nullptr;
std::string projectVersion; std::string projectVersion;
Game::signal_t lastSignal = 0;
} }

View File

@@ -52,9 +52,9 @@ public:
virtual ~CppSQLite3Exception(); virtual ~CppSQLite3Exception();
const int errorCode() { return mnErrCode; } const int errorCode() const { return mnErrCode; }
const char* errorMessage() { return mpszErrMess; } const char* errorMessage() const { return mpszErrMess; }
const char* what() const noexcept override { return mpszErrMess; } const char* what() const noexcept override { return mpszErrMess; }