mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-05-16 11:31:19 +00:00
Merge branch 'main' into leaderboards-again
This commit is contained in:
commit
82524b4165
12
.github/workflows/build-and-test.yml
vendored
12
.github/workflows/build-and-test.yml
vendored
@ -16,12 +16,12 @@ jobs:
|
|||||||
os: [ windows-2022, ubuntu-22.04, macos-13 ]
|
os: [ windows-2022, ubuntu-22.04, macos-13 ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Add msbuild to PATH (Windows only)
|
- name: Add msbuild to PATH (Windows only)
|
||||||
if: ${{ matrix.os == 'windows-2022' }}
|
if: ${{ matrix.os == 'windows-2022' }}
|
||||||
uses: microsoft/setup-msbuild@v2
|
uses: microsoft/setup-msbuild@767f00a3f09872d96a0cb9fcd5e6a4ff33311330
|
||||||
with:
|
with:
|
||||||
vs-version: '[17,18)'
|
vs-version: '[17,18)'
|
||||||
msbuild-architecture: x64
|
msbuild-architecture: x64
|
||||||
@ -30,12 +30,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
brew install openssl@3
|
brew install openssl@3
|
||||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||||
|
- name: Get CMake 3.x
|
||||||
|
uses: lukka/get-cmake@28983e0d3955dba2bb0a6810caae0c6cf268ec0c
|
||||||
|
with:
|
||||||
|
cmakeVersion: "~3.25.0" # <--= optional, use most recent 3.25.x version
|
||||||
- name: cmake
|
- name: cmake
|
||||||
uses: lukka/run-cmake@v10
|
uses: lukka/run-cmake@67c73a83a46f86c4e0b96b741ac37ff495478c38
|
||||||
with:
|
with:
|
||||||
workflowPreset: "ci-${{matrix.os}}"
|
workflowPreset: "ci-${{matrix.os}}"
|
||||||
- name: artifacts
|
- name: artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||||
with:
|
with:
|
||||||
name: build-${{matrix.os}}
|
name: build-${{matrix.os}}
|
||||||
path: |
|
path: |
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
|||||||
[submodule "thirdparty/cpp-httplib"]
|
|
||||||
path = thirdparty/cpp-httplib
|
|
||||||
url = https://github.com/yhirose/cpp-httplib
|
|
||||||
[submodule "thirdparty/tinyxml2"]
|
[submodule "thirdparty/tinyxml2"]
|
||||||
path = thirdparty/tinyxml2
|
path = thirdparty/tinyxml2
|
||||||
url = https://github.com/leethomason/tinyxml2
|
url = https://github.com/leethomason/tinyxml2
|
||||||
|
@ -247,8 +247,9 @@ include_directories(
|
|||||||
"thirdparty/recastnavigation"
|
"thirdparty/recastnavigation"
|
||||||
"thirdparty/SQLite"
|
"thirdparty/SQLite"
|
||||||
"thirdparty/cpplinq"
|
"thirdparty/cpplinq"
|
||||||
"thirdparty/cpp-httplib"
|
|
||||||
"thirdparty/MD5"
|
"thirdparty/MD5"
|
||||||
|
"thirdparty/nlohmann"
|
||||||
|
"thirdparty/mongoose"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||||
|
25
Dockerfile
25
Dockerfile
@ -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
|
||||||
|
@ -78,7 +78,7 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
|
|||||||
|
|
||||||
### Windows packages
|
### Windows packages
|
||||||
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
|
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
|
||||||
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
|
You'll also need to download and install [CMake](https://cmake.org/download/) (<font size="4">**version 3.25**</font> up to <font size="4">**version 3.31**</font>!).
|
||||||
|
|
||||||
### MacOS packages
|
### MacOS packages
|
||||||
Ensure you have [brew](https://brew.sh) installed.
|
Ensure you have [brew](https://brew.sh) installed.
|
||||||
@ -100,7 +100,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Required CMake version
|
#### Required CMake version
|
||||||
This project uses <font size="4">**CMake version 3.25**</font> or higher and as such you will need to ensure you have this version installed.
|
This project uses <font size="4">**CMake version 3.25**</font> up to <font size="4">**version 3.31**</font> and as such you will need to ensure you have this version installed.
|
||||||
You can check your CMake version by using the following command in a terminal.
|
You can check your CMake version by using the following command in a terminal.
|
||||||
```bash
|
```bash
|
||||||
cmake --version
|
cmake --version
|
||||||
@ -354,6 +354,10 @@ Now follow the [build](#build-the-server) section for your system and your serve
|
|||||||
## In-game commands
|
## In-game commands
|
||||||
* A list of all in-game commands can be found [here](./docs/Commands.md).
|
* A list of all in-game commands can be found [here](./docs/Commands.md).
|
||||||
|
|
||||||
|
## Chat Web API
|
||||||
|
* The Chat server has an API that can be enabled via `chatconfig.ini`.
|
||||||
|
* You can view the OpenAPI doc for the API here [here](./docs/ChatWebAPI.yaml).
|
||||||
|
|
||||||
## Verifying your client files
|
## Verifying your client files
|
||||||
|
|
||||||
### LEGO® Universe 1.10.64
|
### LEGO® Universe 1.10.64
|
||||||
|
@ -70,12 +70,15 @@ int main(int argc, char** argv) {
|
|||||||
//Find out the master's IP:
|
//Find out the master's IP:
|
||||||
std::string masterIP;
|
std::string masterIP;
|
||||||
uint32_t masterPort = 1500;
|
uint32_t masterPort = 1500;
|
||||||
|
std::string masterPassword;
|
||||||
|
|
||||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||||
if (masterInfo) {
|
if (masterInfo) {
|
||||||
masterIP = masterInfo->ip;
|
masterIP = masterInfo->ip;
|
||||||
masterPort = masterInfo->port;
|
masterPort = masterInfo->port;
|
||||||
|
masterPassword = masterInfo->password;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
|
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
|
||||||
|
|
||||||
Game::randomEngine = std::mt19937(time(0));
|
Game::randomEngine = std::mt19937(time(0));
|
||||||
@ -89,7 +92,7 @@ int main(int argc, char** argv) {
|
|||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
|
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal, masterPassword);
|
||||||
|
|
||||||
//Run it until server gets a kill message from Master:
|
//Run it until server gets a kill message from Master:
|
||||||
auto t = std::chrono::high_resolution_clock::now();
|
auto t = std::chrono::high_resolution_clock::now();
|
||||||
|
@ -2,6 +2,8 @@ set(DCHATSERVER_SOURCES
|
|||||||
"ChatIgnoreList.cpp"
|
"ChatIgnoreList.cpp"
|
||||||
"ChatPacketHandler.cpp"
|
"ChatPacketHandler.cpp"
|
||||||
"PlayerContainer.cpp"
|
"PlayerContainer.cpp"
|
||||||
|
"ChatWebAPI.cpp"
|
||||||
|
"JSONUtils.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(ChatServer "ChatServer.cpp")
|
add_executable(ChatServer "ChatServer.cpp")
|
||||||
@ -12,5 +14,5 @@ add_library(dChatServer ${DCHATSERVER_SOURCES})
|
|||||||
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
|
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
|
||||||
|
|
||||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
||||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer)
|
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose)
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -29,35 +29,33 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
|||||||
auto& player = Game::playerContainer.GetPlayerDataMutable(playerID);
|
auto& player = Game::playerContainer.GetPlayerDataMutable(playerID);
|
||||||
if (!player) return;
|
if (!player) return;
|
||||||
|
|
||||||
if (player.friends.empty()) {
|
auto friendsList = Database::Get()->GetFriendsList(playerID);
|
||||||
auto friendsList = Database::Get()->GetFriendsList(playerID);
|
for (const auto& friendData : friendsList) {
|
||||||
for (const auto& friendData : friendsList) {
|
FriendData fd;
|
||||||
FriendData fd;
|
fd.isFTP = false; // not a thing in DLU
|
||||||
fd.isFTP = false; // not a thing in DLU
|
fd.friendID = friendData.friendID;
|
||||||
fd.friendID = friendData.friendID;
|
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
|
||||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
|
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
|
||||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
|
|
||||||
|
|
||||||
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||||
if (fd.isBestFriend) player.countOfBestFriends += 1;
|
if (fd.isBestFriend) player.countOfBestFriends += 1;
|
||||||
fd.friendName = friendData.friendName;
|
fd.friendName = friendData.friendName;
|
||||||
|
|
||||||
//Now check if they're online:
|
//Now check if they're online:
|
||||||
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
|
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
|
||||||
|
|
||||||
if (fr) {
|
if (fr) {
|
||||||
fd.isOnline = true;
|
fd.isOnline = true;
|
||||||
fd.zoneID = fr.zoneID;
|
fd.zoneID = fr.zoneID;
|
||||||
|
|
||||||
//Since this friend is online, we need to update them on the fact that we've just logged in:
|
//Since this friend is online, we need to update them on the fact that we've just logged in:
|
||||||
SendFriendUpdate(fr, player, 1, fd.isBestFriend);
|
if (player.isLogin) SendFriendUpdate(fr, player, 1, fd.isBestFriend);
|
||||||
} else {
|
} else {
|
||||||
fd.isOnline = false;
|
fd.isOnline = false;
|
||||||
fd.zoneID = LWOZONEID();
|
fd.zoneID = LWOZONEID();
|
||||||
}
|
|
||||||
|
|
||||||
player.friends.push_back(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player.friends.push_back(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Now, we need to send the friendlist to the server they came from:
|
//Now, we need to send the friendlist to the server they came from:
|
||||||
@ -75,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +103,8 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto& requestee = Game::playerContainer.GetPlayerDataMutable(playerName);
|
// Intentional copy
|
||||||
|
PlayerData requestee = Game::playerContainer.GetPlayerData(playerName);
|
||||||
|
|
||||||
// Check if player is online first
|
// Check if player is online first
|
||||||
if (isBestFriendRequest && !requestee) {
|
if (isBestFriendRequest && !requestee) {
|
||||||
@ -123,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,24 +189,29 @@ 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) {
|
||||||
requestee.countOfBestFriends += 1;
|
if (requestee.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true);
|
||||||
requestor.countOfBestFriends += 1;
|
if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true);
|
||||||
if (requestee.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true);
|
|
||||||
if (requestor.sysAddr != 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) {
|
||||||
friendData.isBestFriend = true;
|
friendData.isBestFriend = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& friendData : requestee.friends) {
|
requestor.countOfBestFriends += 1;
|
||||||
if (friendData.friendID == requestor.playerID) {
|
|
||||||
friendData.isBestFriend = true;
|
auto& toModify = Game::playerContainer.GetPlayerDataMutable(playerName);
|
||||||
|
if (toModify) {
|
||||||
|
for (auto& friendData : toModify.friends) {
|
||||||
|
if (friendData.friendID == requestor.playerID) {
|
||||||
|
friendData.isBestFriend = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
toModify.countOfBestFriends += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} 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();
|
||||||
@ -380,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +418,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SystemAddress sysAddr = sender.sysAddr;
|
SystemAddress sysAddr = sender.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,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);
|
||||||
@ -533,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,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) {
|
||||||
@ -589,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;
|
||||||
@ -718,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;
|
||||||
}
|
}
|
||||||
@ -768,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -837,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -913,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -955,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,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.
|
||||||
@ -1000,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1014,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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#include "RakNetDefines.h"
|
#include "RakNetDefines.h"
|
||||||
#include "MessageIdentifiers.h"
|
#include "MessageIdentifiers.h"
|
||||||
|
|
||||||
|
#include "ChatWebAPI.h"
|
||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
Logger* logger = nullptr;
|
Logger* logger = nullptr;
|
||||||
dServer* server = nullptr;
|
dServer* server = nullptr;
|
||||||
@ -74,7 +76,8 @@ int main(int argc, char** argv) {
|
|||||||
Game::assetManager = new AssetManager(clientPath);
|
Game::assetManager = new AssetManager(clientPath);
|
||||||
} catch (std::runtime_error& ex) {
|
} catch (std::runtime_error& ex) {
|
||||||
LOG("Got an error while setting up assets: %s", ex.what());
|
LOG("Got an error while setting up assets: %s", ex.what());
|
||||||
|
delete Game::logger;
|
||||||
|
delete Game::config;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,18 +87,32 @@ int main(int argc, char** argv) {
|
|||||||
} catch (std::exception& ex) {
|
} catch (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());
|
||||||
Database::Destroy("ChatServer");
|
Database::Destroy("ChatServer");
|
||||||
delete Game::server;
|
|
||||||
delete Game::logger;
|
delete Game::logger;
|
||||||
|
delete Game::config;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// seyup the chat api web server
|
||||||
|
bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
|
||||||
|
ChatWebAPI chatwebapi;
|
||||||
|
if (web_server_enabled && !chatwebapi.Startup()){
|
||||||
|
// if we want the web api and it fails to start, exit
|
||||||
|
LOG("Failed to start web server, shutting down.");
|
||||||
|
Database::Destroy("ChatServer");
|
||||||
|
delete Game::logger;
|
||||||
|
delete Game::config;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
};
|
||||||
|
|
||||||
//Find out the master's IP:
|
//Find out the master's IP:
|
||||||
std::string masterIP;
|
std::string masterIP;
|
||||||
uint32_t masterPort = 1000;
|
uint32_t masterPort = 1000;
|
||||||
|
std::string masterPassword;
|
||||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||||
if (masterInfo) {
|
if (masterInfo) {
|
||||||
masterIP = masterInfo->ip;
|
masterIP = masterInfo->ip;
|
||||||
masterPort = masterInfo->port;
|
masterPort = masterInfo->port;
|
||||||
|
masterPassword = masterInfo->password;
|
||||||
}
|
}
|
||||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||||
std::string ourIP = "localhost";
|
std::string ourIP = "localhost";
|
||||||
@ -104,7 +121,7 @@ int main(int argc, char** argv) {
|
|||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
|
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal, masterPassword);
|
||||||
|
|
||||||
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);
|
||||||
@ -149,6 +166,11 @@ int main(int argc, char** argv) {
|
|||||||
packet = nullptr;
|
packet = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check and handle web requests:
|
||||||
|
if (web_server_enabled) {
|
||||||
|
chatwebapi.ReceiveRequests();
|
||||||
|
}
|
||||||
|
|
||||||
//Push our log every 30s:
|
//Push our log every 30s:
|
||||||
if (framesSinceLastFlush >= logFlushTime) {
|
if (framesSinceLastFlush >= logFlushTime) {
|
||||||
Game::logger->Flush();
|
Game::logger->Flush();
|
||||||
@ -202,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);
|
||||||
@ -286,12 +312,11 @@ void HandlePacket(Packet* packet) {
|
|||||||
case MessageType::Chat::LOGIN_SESSION_NOTIFY:
|
case MessageType::Chat::LOGIN_SESSION_NOTIFY:
|
||||||
Game::playerContainer.InsertPlayer(packet);
|
Game::playerContainer.InsertPlayer(packet);
|
||||||
break;
|
break;
|
||||||
case MessageType::Chat::GM_ANNOUNCE: {
|
case MessageType::Chat::GM_ANNOUNCE:
|
||||||
// we just forward this packet to every connected server
|
// we just forward this packet to every connected server
|
||||||
inStream.ResetReadPointer();
|
inStream.ResetReadPointer();
|
||||||
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
|
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
case MessageType::Chat::UNEXPECTED_DISCONNECT:
|
case MessageType::Chat::UNEXPECTED_DISCONNECT:
|
||||||
Game::playerContainer.ScheduleRemovePlayer(packet);
|
Game::playerContainer.ScheduleRemovePlayer(packet);
|
||||||
break;
|
break;
|
||||||
@ -301,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:
|
||||||
@ -336,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:
|
||||||
|
197
dChatServer/ChatWebAPI.cpp
Normal file
197
dChatServer/ChatWebAPI.cpp
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#include "ChatWebAPI.h"
|
||||||
|
|
||||||
|
#include "Logger.h"
|
||||||
|
#include "Game.h"
|
||||||
|
#include "json.hpp"
|
||||||
|
#include "dCommonVars.h"
|
||||||
|
#include "MessageType/Chat.h"
|
||||||
|
#include "dServer.h"
|
||||||
|
#include "dConfig.h"
|
||||||
|
#include "PlayerContainer.h"
|
||||||
|
#include "JSONUtils.h"
|
||||||
|
#include "GeneralUtils.h"
|
||||||
|
#include "eHTTPMethod.h"
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
#include "ChatPackets.h"
|
||||||
|
#include "StringifiedEnum.h"
|
||||||
|
#include "Database.h"
|
||||||
|
|
||||||
|
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||||
|
#pragma push_macro("DELETE")
|
||||||
|
#undef DELETE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
typedef struct mg_connection mg_connection;
|
||||||
|
typedef struct mg_http_message mg_http_message;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const char* json_content_type = "Content-Type: application/json\r\n";
|
||||||
|
std::map<std::pair<eHTTPMethod, std::string>, WebAPIHTTPRoute> Routes {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateAuthentication(const mg_http_message* http_msg) {
|
||||||
|
// TO DO: This is just a placeholder for now
|
||||||
|
// use tokens or something at a later point if we want to implement authentication
|
||||||
|
// bit using the listen bind address to limit external access is good enough to start with
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateJSON(std::optional<json> data, HTTPReply& reply) {
|
||||||
|
if (!data) {
|
||||||
|
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||||
|
reply.message = "{\"error\":\"Invalid JSON\"}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePlayersRequest(HTTPReply& reply, std::string body) {
|
||||||
|
const json data = Game::playerContainer;
|
||||||
|
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
|
||||||
|
reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleTeamsRequest(HTTPReply& reply, std::string body) {
|
||||||
|
const json data = Game::playerContainer.GetTeamContainer();
|
||||||
|
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
|
||||||
|
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleAnnounceRequest(HTTPReply& reply, std::string body) {
|
||||||
|
auto data = GeneralUtils::TryParse<json>(body);
|
||||||
|
if (!ValidateJSON(data, reply)) return;
|
||||||
|
|
||||||
|
const auto& good_data = data.value();
|
||||||
|
auto check = JSONUtils::CheckRequiredData(good_data, { "title", "message" });
|
||||||
|
if (!check.empty()) {
|
||||||
|
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||||
|
reply.message = check;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ChatPackets::Announcement announcement;
|
||||||
|
announcement.title = good_data["title"];
|
||||||
|
announcement.message = good_data["message"];
|
||||||
|
announcement.Send();
|
||||||
|
|
||||||
|
reply.status = eHTTPStatusCode::OK;
|
||||||
|
reply.message = "{\"status\":\"Announcement Sent\"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleInvalidRoute(HTTPReply& reply) {
|
||||||
|
reply.status = eHTTPStatusCode::NOT_FOUND;
|
||||||
|
reply.message = "{\"error\":\"Invalid Route\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_msg) {
|
||||||
|
HTTPReply reply;
|
||||||
|
|
||||||
|
if (!http_msg) {
|
||||||
|
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||||
|
reply.message = "{\"error\":\"Invalid Request\"}";
|
||||||
|
} else if (ValidateAuthentication(http_msg)) {
|
||||||
|
|
||||||
|
// convert method from cstring to std string
|
||||||
|
std::string method_string(http_msg->method.buf, http_msg->method.len);
|
||||||
|
// get mehtod from mg to enum
|
||||||
|
const eHTTPMethod method = magic_enum::enum_cast<eHTTPMethod>(method_string).value_or(eHTTPMethod::INVALID);
|
||||||
|
|
||||||
|
// convert uri from cstring to std string
|
||||||
|
std::string uri(http_msg->uri.buf, http_msg->uri.len);
|
||||||
|
std::transform(uri.begin(), uri.end(), uri.begin(), ::tolower);
|
||||||
|
|
||||||
|
// convert body from cstring to std string
|
||||||
|
std::string body(http_msg->body.buf, http_msg->body.len);
|
||||||
|
|
||||||
|
|
||||||
|
const auto routeItr = Routes.find({method, uri});
|
||||||
|
|
||||||
|
if (routeItr != Routes.end()) {
|
||||||
|
const auto& [_, route] = *routeItr;
|
||||||
|
route.handle(reply, body);
|
||||||
|
} else HandleInvalidRoute(reply);
|
||||||
|
} else {
|
||||||
|
reply.status = eHTTPStatusCode::UNAUTHORIZED;
|
||||||
|
reply.message = "{\"error\":\"Unauthorized\"}";
|
||||||
|
}
|
||||||
|
mg_http_reply(connection, static_cast<int>(reply.status), json_content_type, reply.message.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HandleRequests(mg_connection* connection, int request, void* request_data) {
|
||||||
|
switch (request) {
|
||||||
|
case MG_EV_HTTP_MSG:
|
||||||
|
HandleHTTPMessage(connection, static_cast<mg_http_message*>(request_data));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWebAPI::RegisterHTTPRoutes(WebAPIHTTPRoute route) {
|
||||||
|
auto [_, success] = Routes.try_emplace({ route.method, route.path }, route);
|
||||||
|
if (!success) {
|
||||||
|
LOG_DEBUG("Failed to register route %s", route.path.c_str());
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("Registered route %s", route.path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatWebAPI::ChatWebAPI() {
|
||||||
|
mg_log_set(MG_LL_NONE);
|
||||||
|
mg_mgr_init(&mgr); // Initialize event manager
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatWebAPI::~ChatWebAPI() {
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatWebAPI::Startup() {
|
||||||
|
// Make listen address
|
||||||
|
// std::string listen_ip = Game::config->GetValue("web_server_listen_ip");
|
||||||
|
// if (listen_ip == "localhost") listen_ip = "127.0.0.1";
|
||||||
|
|
||||||
|
const std::string& listen_port = Game::config->GetValue("web_server_listen_port");
|
||||||
|
// const std::string& listen_address = "http://" + listen_ip + ":" + listen_port;
|
||||||
|
const std::string& listen_address = "http://localhost:" + listen_port;
|
||||||
|
LOG("Starting web server on %s", listen_address.c_str());
|
||||||
|
|
||||||
|
// Create HTTP listener
|
||||||
|
if (!mg_http_listen(&mgr, listen_address.c_str(), HandleRequests, NULL)) {
|
||||||
|
LOG("Failed to create web server listener on %s", listen_port.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register routes
|
||||||
|
|
||||||
|
// API v1 routes
|
||||||
|
std::string v1_route = "/api/v1/";
|
||||||
|
RegisterHTTPRoutes({
|
||||||
|
.path = v1_route + "players",
|
||||||
|
.method = eHTTPMethod::GET,
|
||||||
|
.handle = HandlePlayersRequest
|
||||||
|
});
|
||||||
|
|
||||||
|
RegisterHTTPRoutes({
|
||||||
|
.path = v1_route + "teams",
|
||||||
|
.method = eHTTPMethod::GET,
|
||||||
|
.handle = HandleTeamsRequest
|
||||||
|
});
|
||||||
|
|
||||||
|
RegisterHTTPRoutes({
|
||||||
|
.path = v1_route + "announce",
|
||||||
|
.method = eHTTPMethod::POST,
|
||||||
|
.handle = HandleAnnounceRequest
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWebAPI::ReceiveRequests() {
|
||||||
|
mg_mgr_poll(&mgr, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||||
|
#pragma pop_macro("DELETE")
|
||||||
|
#endif
|
36
dChatServer/ChatWebAPI.h
Normal file
36
dChatServer/ChatWebAPI.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef __CHATWEBAPI_H__
|
||||||
|
#define __CHATWEBAPI_H__
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
#include "eHTTPStatusCode.h"
|
||||||
|
|
||||||
|
enum class eHTTPMethod;
|
||||||
|
|
||||||
|
typedef struct mg_mgr mg_mgr;
|
||||||
|
|
||||||
|
struct HTTPReply {
|
||||||
|
eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
|
||||||
|
std::string message = "{\"error\":\"Not Found\"}";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebAPIHTTPRoute {
|
||||||
|
std::string path;
|
||||||
|
eHTTPMethod method;
|
||||||
|
std::function<void(HTTPReply&, const std::string&)> handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChatWebAPI {
|
||||||
|
public:
|
||||||
|
ChatWebAPI();
|
||||||
|
~ChatWebAPI();
|
||||||
|
void ReceiveRequests();
|
||||||
|
void RegisterHTTPRoutes(WebAPIHTTPRoute route);
|
||||||
|
bool Startup();
|
||||||
|
private:
|
||||||
|
mg_mgr mgr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __CHATWEBAPI_H__
|
62
dChatServer/JSONUtils.cpp
Normal file
62
dChatServer/JSONUtils.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "JSONUtils.h"
|
||||||
|
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
void to_json(json& data, const PlayerData& playerData) {
|
||||||
|
data["id"] = playerData.playerID;
|
||||||
|
data["name"] = playerData.playerName;
|
||||||
|
data["gm_level"] = playerData.gmLevel;
|
||||||
|
data["muted"] = playerData.GetIsMuted();
|
||||||
|
|
||||||
|
auto& zoneID = data["zone_id"];
|
||||||
|
zoneID["map_id"] = playerData.zoneID.GetMapID();
|
||||||
|
zoneID["instance_id"] = playerData.zoneID.GetInstanceID();
|
||||||
|
zoneID["clone_id"] = playerData.zoneID.GetCloneID();
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_json(json& data, const PlayerContainer& playerContainer) {
|
||||||
|
data = json::array();
|
||||||
|
for(auto& playerData : playerContainer.GetAllPlayers()) {
|
||||||
|
if (playerData.first == LWOOBJID_EMPTY) continue;
|
||||||
|
data.push_back(playerData.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_json(json& data, const TeamContainer& teamContainer) {
|
||||||
|
for (auto& teamData : Game::playerContainer.GetTeams()) {
|
||||||
|
if (!teamData) continue;
|
||||||
|
data.push_back(*teamData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_json(json& data, const TeamData& teamData) {
|
||||||
|
data["id"] = teamData.teamID;
|
||||||
|
data["loot_flag"] = teamData.lootFlag;
|
||||||
|
data["local"] = teamData.local;
|
||||||
|
|
||||||
|
auto& leader = Game::playerContainer.GetPlayerData(teamData.leaderID);
|
||||||
|
data["leader"] = leader.playerName;
|
||||||
|
|
||||||
|
auto& members = data["members"];
|
||||||
|
for (auto& member : teamData.memberIDs) {
|
||||||
|
auto& playerData = Game::playerContainer.GetPlayerData(member);
|
||||||
|
|
||||||
|
if (!playerData) continue;
|
||||||
|
members.push_back(playerData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JSONUtils::CheckRequiredData(const json& data, const std::vector<std::string>& requiredData) {
|
||||||
|
json check;
|
||||||
|
check["error"] = json::array();
|
||||||
|
for (const auto& required : requiredData) {
|
||||||
|
if (!data.contains(required)) {
|
||||||
|
check["error"].push_back("Missing Parameter: " + required);
|
||||||
|
} else if (data[required] == "") {
|
||||||
|
check["error"].push_back("Empty Parameter: " + required);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return check["error"].empty() ? "" : check.dump();
|
||||||
|
}
|
17
dChatServer/JSONUtils.h
Normal file
17
dChatServer/JSONUtils.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef __JSONUTILS_H__
|
||||||
|
#define __JSONUTILS_H__
|
||||||
|
|
||||||
|
#include "json_fwd.hpp"
|
||||||
|
#include "PlayerContainer.h"
|
||||||
|
|
||||||
|
void to_json(nlohmann::json& data, const PlayerData& playerData);
|
||||||
|
void to_json(nlohmann::json& data, const PlayerContainer& playerContainer);
|
||||||
|
void to_json(nlohmann::json& data, const TeamContainer& teamData);
|
||||||
|
void to_json(nlohmann::json& data, const TeamData& teamData);
|
||||||
|
|
||||||
|
namespace JSONUtils {
|
||||||
|
// check required data for reqeust
|
||||||
|
std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __JSONUTILS_H__
|
@ -32,7 +32,10 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto isLogin = !m_Players.contains(playerId);
|
||||||
auto& data = m_Players[playerId];
|
auto& data = m_Players[playerId];
|
||||||
|
data = PlayerData();
|
||||||
|
data.isLogin = isLogin;
|
||||||
data.playerID = playerId;
|
data.playerID = playerId;
|
||||||
|
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
@ -49,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++;
|
||||||
@ -216,7 +219,7 @@ TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
|||||||
team->leaderID = leader;
|
team->leaderID = leader;
|
||||||
team->local = local;
|
team->local = local;
|
||||||
|
|
||||||
mTeams.push_back(team);
|
GetTeamsMut().push_back(team);
|
||||||
|
|
||||||
AddMember(team, leader);
|
AddMember(team, leader);
|
||||||
|
|
||||||
@ -224,7 +227,7 @@ TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
|
TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
|
||||||
for (auto* team : mTeams) {
|
for (auto* team : GetTeams()) {
|
||||||
if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
|
if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
|
||||||
|
|
||||||
return team;
|
return team;
|
||||||
@ -238,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,33 +332,32 @@ 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(mTeams.begin(), mTeams.end(), team);
|
const auto index = std::ranges::find(GetTeams(), team);
|
||||||
|
|
||||||
if (index == mTeams.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);
|
||||||
|
|
||||||
mTeams.erase(index);
|
GetTeamsMut().erase(index);
|
||||||
|
|
||||||
delete team;
|
delete team;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
||||||
const auto index = std::find(mTeams.begin(), mTeams.end(), team);
|
const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
|
||||||
|
|
||||||
if (index == mTeams.end()) return;
|
if (index == GetTeams().end()) return;
|
||||||
|
|
||||||
const auto& leader = GetPlayerData(team->leaderID);
|
const auto& leader = GetPlayerData(team->leaderID);
|
||||||
|
|
||||||
@ -444,5 +444,5 @@ void PlayerContainer::Shutdown() {
|
|||||||
Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
|
Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
|
||||||
m_Players.erase(m_Players.begin());
|
m_Players.erase(m_Players.begin());
|
||||||
}
|
}
|
||||||
for (auto* team : mTeams) if (team) delete team;
|
for (auto* team : GetTeams()) if (team) delete team;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,12 @@
|
|||||||
|
|
||||||
enum class eGameMasterLevel : uint8_t;
|
enum class eGameMasterLevel : uint8_t;
|
||||||
|
|
||||||
|
struct TeamData;
|
||||||
|
|
||||||
|
struct TeamContainer {
|
||||||
|
std::vector<TeamData*> mTeams;
|
||||||
|
};
|
||||||
|
|
||||||
struct IgnoreData {
|
struct IgnoreData {
|
||||||
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
|
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
|
||||||
inline bool operator==(const std::string& other) const noexcept {
|
inline bool operator==(const std::string& other) const noexcept {
|
||||||
@ -36,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;
|
||||||
@ -46,8 +52,10 @@ struct PlayerData {
|
|||||||
std::vector<IgnoreData> ignoredPlayers;
|
std::vector<IgnoreData> ignoredPlayers;
|
||||||
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
|
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
|
||||||
bool isFTP = false;
|
bool isFTP = false;
|
||||||
|
bool isLogin = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct TeamData {
|
struct TeamData {
|
||||||
TeamData();
|
TeamData();
|
||||||
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
|
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
|
||||||
@ -75,7 +83,7 @@ public:
|
|||||||
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
||||||
uint32_t GetPlayerCount() { return m_PlayerCount; };
|
uint32_t GetPlayerCount() { return m_PlayerCount; };
|
||||||
uint32_t GetSimCount() { return m_SimCount; };
|
uint32_t GetSimCount() { return m_SimCount; };
|
||||||
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
|
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() const { return m_Players; };
|
||||||
|
|
||||||
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
||||||
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
||||||
@ -83,13 +91,16 @@ 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);
|
||||||
LWOOBJID GetId(const std::u16string& playerName);
|
LWOOBJID GetId(const std::u16string& playerName);
|
||||||
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
||||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
||||||
|
const TeamContainer& GetTeamContainer() { return m_TeamContainer; }
|
||||||
|
std::vector<TeamData*>& GetTeamsMut() { return m_TeamContainer.mTeams; };
|
||||||
|
const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); };
|
||||||
|
|
||||||
void Update(const float deltaTime);
|
void Update(const float deltaTime);
|
||||||
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
|
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
|
||||||
@ -97,7 +108,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
LWOOBJID m_TeamIDCounter = 0;
|
LWOOBJID m_TeamIDCounter = 0;
|
||||||
std::map<LWOOBJID, PlayerData> m_Players;
|
std::map<LWOOBJID, PlayerData> m_Players;
|
||||||
std::vector<TeamData*> mTeams;
|
TeamContainer m_TeamContainer{};
|
||||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||||
std::map<LWOOBJID, float> m_PlayersToRemove;
|
std::map<LWOOBJID, float> m_PlayersToRemove;
|
||||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ public:
|
|||||||
// AMFValue template class instantiations
|
// AMFValue template class instantiations
|
||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
class AMFValue : public AMFBaseValue {
|
class AMFValue : public AMFBaseValue {
|
||||||
|
static_assert(!std::is_same_v<ValueType, std::string_view>, "AMFValue cannot be instantiated with std::string_view");
|
||||||
public:
|
public:
|
||||||
AMFValue() = default;
|
AMFValue() = default;
|
||||||
AMFValue(const ValueType value) : m_Data{ value } {}
|
AMFValue(const ValueType value) : m_Data{ value } {}
|
||||||
@ -51,6 +53,15 @@ public:
|
|||||||
|
|
||||||
void SetValue(const ValueType value) { m_Data = value; }
|
void SetValue(const ValueType value) { m_Data = value; }
|
||||||
|
|
||||||
|
AMFValue<ValueType>& operator=(const AMFValue<ValueType>& other) {
|
||||||
|
return operator=(other.m_Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
AMFValue<ValueType>& operator=(const ValueType& other) {
|
||||||
|
m_Data = other;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ValueType m_Data;
|
ValueType m_Data;
|
||||||
};
|
};
|
||||||
@ -210,13 +221,17 @@ public:
|
|||||||
* @param key The key to associate with the value
|
* @param key The key to associate with the value
|
||||||
* @param value The value to insert
|
* @param value The value to insert
|
||||||
*/
|
*/
|
||||||
void Insert(const std::string_view key, std::unique_ptr<AMFBaseValue> value) {
|
template<typename AmfType>
|
||||||
|
AmfType& Insert(const std::string_view key, std::unique_ptr<AmfType> value) {
|
||||||
const auto element = m_Associative.find(key);
|
const auto element = m_Associative.find(key);
|
||||||
|
auto& toReturn = *value;
|
||||||
if (element != m_Associative.cend() && element->second) {
|
if (element != m_Associative.cend() && element->second) {
|
||||||
element->second = std::move(value);
|
element->second = std::move(value);
|
||||||
} else {
|
} else {
|
||||||
m_Associative.emplace(key, std::move(value));
|
m_Associative.emplace(key, std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,11 +243,15 @@ public:
|
|||||||
* @param key The key to associate with the value
|
* @param key The key to associate with the value
|
||||||
* @param value The value to insert
|
* @param value The value to insert
|
||||||
*/
|
*/
|
||||||
void Insert(const size_t index, std::unique_ptr<AMFBaseValue> value) {
|
template<typename AmfType>
|
||||||
|
AmfType& Insert(const size_t index, std::unique_ptr<AmfType> value) {
|
||||||
|
auto& toReturn = *value;
|
||||||
if (index >= m_Dense.size()) {
|
if (index >= m_Dense.size()) {
|
||||||
m_Dense.resize(index + 1);
|
m_Dense.resize(index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Dense.at(index) = std::move(value);
|
m_Dense.at(index) = std::move(value);
|
||||||
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -257,10 +276,10 @@ public:
|
|||||||
*
|
*
|
||||||
* @param key The key to remove from the associative portion
|
* @param key The key to remove from the associative portion
|
||||||
*/
|
*/
|
||||||
void Remove(const std::string& key, const bool deleteValue = true) {
|
void Remove(const std::string& key) {
|
||||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||||
if (it != m_Associative.cend()) {
|
if (it != m_Associative.cend()) {
|
||||||
if (deleteValue) m_Associative.erase(it);
|
m_Associative.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +362,18 @@ public:
|
|||||||
return index < m_Dense.size() ? m_Dense.at(index).get() : nullptr;
|
return index < m_Dense.size() ? m_Dense.at(index).get() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
m_Associative.clear();
|
||||||
|
m_Dense.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename AmfType = AMFArrayValue>
|
||||||
|
AmfType& PushDebug(const std::string_view name) {
|
||||||
|
auto* value = PushArray();
|
||||||
|
value->Insert("name", name.data());
|
||||||
|
return value->Insert<AmfType>("value", std::make_unique<AmfType>());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The associative portion. These values are key'd with strings to an AMFValue.
|
* The associative portion. These values are key'd with strings to an AMFValue.
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
|
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
|
||||||
if (size == SIZE_MAX || size > string.size()) {
|
if (size == SIZE_MAX || size > string.size()) {
|
||||||
@ -327,6 +331,17 @@ std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::stri
|
|||||||
return sortedFiles;
|
return sortedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
[[nodiscard]] std::optional<json> GeneralUtils::TryParse(std::string_view str) {
|
||||||
|
try {
|
||||||
|
return json::parse(str);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return std::nullopt;
|
||||||
|
} catch (...) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
||||||
|
|
||||||
// MacOS floating-point parse function specializations
|
// MacOS floating-point parse function specializations
|
||||||
|
@ -201,6 +201,10 @@ namespace GeneralUtils {
|
|||||||
return isParsed ? static_cast<T>(result) : std::optional<T>{};
|
return isParsed ? static_cast<T>(result) : std::optional<T>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires(!Numeric<T>)
|
||||||
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
||||||
|
|
||||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
||||||
|
|
||||||
// MacOS floating-point parse helper function specializations
|
// MacOS floating-point parse helper function specializations
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include "zlib.h"
|
#include "zlib.h"
|
||||||
|
|
||||||
|
constexpr uint32_t CRC32_INIT = 0xFFFFFFFF;
|
||||||
|
constexpr auto NULL_TERMINATOR = std::string_view{"\0\0\0", 4};
|
||||||
|
|
||||||
AssetManager::AssetManager(const std::filesystem::path& path) {
|
AssetManager::AssetManager(const std::filesystem::path& path) {
|
||||||
if (!std::filesystem::is_directory(path)) {
|
if (!std::filesystem::is_directory(path)) {
|
||||||
throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory.");
|
throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory.");
|
||||||
@ -18,12 +21,20 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
|||||||
|
|
||||||
m_RootPath = m_Path;
|
m_RootPath = m_Path;
|
||||||
m_ResPath = (m_Path / "client" / "res");
|
m_ResPath = (m_Path / "client" / "res");
|
||||||
} else if (std::filesystem::exists(m_Path / ".." / "versions") && std::filesystem::exists(m_Path / "res")) {
|
} else if (std::filesystem::exists(m_Path / "res" / "pack")) {
|
||||||
|
if (!std::filesystem::exists(m_Path / ".." / "versions")) {
|
||||||
|
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
m_AssetBundleType = eAssetBundleType::Packed;
|
m_AssetBundleType = eAssetBundleType::Packed;
|
||||||
|
|
||||||
m_RootPath = (m_Path / "..");
|
m_RootPath = (m_Path / "..");
|
||||||
m_ResPath = (m_Path / "res");
|
m_ResPath = (m_Path / "res");
|
||||||
} else if (std::filesystem::exists(m_Path / "pack") && std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
|
} else if (std::filesystem::exists(m_Path / "pack")) {
|
||||||
|
if (!std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
|
||||||
|
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
m_AssetBundleType = eAssetBundleType::Packed;
|
m_AssetBundleType = eAssetBundleType::Packed;
|
||||||
|
|
||||||
m_RootPath = (m_Path / ".." / "..");
|
m_RootPath = (m_Path / ".." / "..");
|
||||||
@ -48,6 +59,7 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eAssetBundleType::None:
|
case eAssetBundleType::None:
|
||||||
|
[[fallthrough]];
|
||||||
case eAssetBundleType::Unpacked: {
|
case eAssetBundleType::Unpacked: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -55,19 +67,10 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AssetManager::LoadPackIndex() {
|
void AssetManager::LoadPackIndex() {
|
||||||
m_PackIndex = new PackIndex(m_RootPath);
|
m_PackIndex = PackIndex(m_RootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path AssetManager::GetResPath() {
|
bool AssetManager::HasFile(std::string fixedName) const {
|
||||||
return m_ResPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
eAssetBundleType AssetManager::GetAssetBundleType() {
|
|
||||||
return m_AssetBundleType;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssetManager::HasFile(const char* name) {
|
|
||||||
auto fixedName = std::string(name);
|
|
||||||
std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
|
std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
|
||||||
|
|
||||||
// Special case for unpacked client have BrickModels in upper case
|
// Special case for unpacked client have BrickModels in upper case
|
||||||
@ -81,8 +84,7 @@ bool AssetManager::HasFile(const char* name) {
|
|||||||
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
||||||
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
|
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
|
||||||
|
|
||||||
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
const auto crc = crc32b(crc32b(CRC32_INIT, fixedName), NULL_TERMINATOR);
|
||||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
|
||||||
|
|
||||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||||
if (item.m_Crc == crc) {
|
if (item.m_Crc == crc) {
|
||||||
@ -93,8 +95,7 @@ bool AssetManager::HasFile(const char* name) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
bool AssetManager::GetFile(std::string fixedName, char** data, uint32_t* len) const {
|
||||||
auto fixedName = std::string(name);
|
|
||||||
std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
|
std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
|
||||||
std::replace(fixedName.begin(), fixedName.end(), '\\', '/'); // On the off chance someone has the wrong slashes, force forward slashes
|
std::replace(fixedName.begin(), fixedName.end(), '\\', '/'); // On the off chance someone has the wrong slashes, force forward slashes
|
||||||
|
|
||||||
@ -129,8 +130,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
|||||||
fixedName = "client\\res\\" + fixedName;
|
fixedName = "client\\res\\" + fixedName;
|
||||||
}
|
}
|
||||||
int32_t packIndex = -1;
|
int32_t packIndex = -1;
|
||||||
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
auto crc = crc32b(crc32b(CRC32_INIT, fixedName), NULL_TERMINATOR);
|
||||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
|
||||||
|
|
||||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||||
if (item.m_Crc == crc) {
|
if (item.m_Crc == crc) {
|
||||||
@ -144,15 +144,13 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto packs = this->m_PackIndex->GetPacks();
|
const auto& pack = this->m_PackIndex->GetPacks().at(packIndex);
|
||||||
auto* pack = packs.at(packIndex);
|
const bool success = pack.ReadFileFromPack(crc, data, len);
|
||||||
|
|
||||||
bool success = pack->ReadFileFromPack(crc, data, len);
|
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetStream AssetManager::GetFile(const char* name) {
|
AssetStream AssetManager::GetFile(const char* name) const {
|
||||||
char* buf; uint32_t len;
|
char* buf; uint32_t len;
|
||||||
|
|
||||||
bool success = this->GetFile(name, &buf, &len);
|
bool success = this->GetFile(name, &buf, &len);
|
||||||
@ -160,23 +158,15 @@ AssetStream AssetManager::GetFile(const char* name) {
|
|||||||
return AssetStream(buf, len, success);
|
return AssetStream(buf, len, success);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
uint32_t AssetManager::crc32b(uint32_t crc, const std::string_view message) {
|
||||||
size_t i, j;
|
for (const auto byte : message) {
|
||||||
uint32_t crc, msb;
|
|
||||||
|
|
||||||
crc = base;
|
|
||||||
for (i = 0; i < l; i++) {
|
|
||||||
// xor next byte to upper bits of crc
|
// xor next byte to upper bits of crc
|
||||||
crc ^= (static_cast<unsigned int>(message[i]) << 24);
|
crc ^= (static_cast<uint32_t>(std::bit_cast<uint8_t>(byte)) << 24);
|
||||||
for (j = 0; j < 8; j++) { // Do eight times.
|
for (size_t _ = 0; _ < 8; _++) { // Do eight times.
|
||||||
msb = crc >> 31;
|
const uint32_t msb = crc >> 31;
|
||||||
crc <<= 1;
|
crc <<= 1;
|
||||||
crc ^= (0 - msb) & 0x04C11DB7;
|
crc ^= (0 - msb) & 0x04C11DB7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return crc; // don't complement crc on output
|
return crc; // don't complement crc on output
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetManager::~AssetManager() {
|
|
||||||
delete m_PackIndex;
|
|
||||||
}
|
|
||||||
|
@ -61,23 +61,32 @@ struct AssetStream : std::istream {
|
|||||||
class AssetManager {
|
class AssetManager {
|
||||||
public:
|
public:
|
||||||
AssetManager(const std::filesystem::path& path);
|
AssetManager(const std::filesystem::path& path);
|
||||||
~AssetManager();
|
|
||||||
|
|
||||||
std::filesystem::path GetResPath();
|
[[nodiscard]]
|
||||||
eAssetBundleType GetAssetBundleType();
|
const std::filesystem::path& GetResPath() const {
|
||||||
|
return m_ResPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
eAssetBundleType GetAssetBundleType() const {
|
||||||
|
return m_AssetBundleType;
|
||||||
|
}
|
||||||
|
|
||||||
bool HasFile(const char* name);
|
[[nodiscard]]
|
||||||
bool GetFile(const char* name, char** data, uint32_t* len);
|
bool HasFile(std::string name) const;
|
||||||
AssetStream GetFile(const char* name);
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool GetFile(std::string name, char** data, uint32_t* len) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
AssetStream GetFile(const char* name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadPackIndex();
|
void LoadPackIndex();
|
||||||
|
|
||||||
// Modified crc algorithm (mpeg2)
|
// Modified crc algorithm (mpeg2)
|
||||||
// Reference: https://stackoverflow.com/questions/54339800/how-to-modify-crc-32-to-crc-32-mpeg-2
|
// Reference: https://stackoverflow.com/questions/54339800/how-to-modify-crc-32-to-crc-32-mpeg-2
|
||||||
inline uint32_t crc32b(uint32_t base, uint8_t* message, size_t l);
|
static inline uint32_t crc32b(uint32_t crc, std::string_view message);
|
||||||
|
|
||||||
bool m_SuccessfullyLoaded;
|
|
||||||
|
|
||||||
std::filesystem::path m_Path;
|
std::filesystem::path m_Path;
|
||||||
std::filesystem::path m_RootPath;
|
std::filesystem::path m_RootPath;
|
||||||
@ -85,5 +94,5 @@ private:
|
|||||||
|
|
||||||
eAssetBundleType m_AssetBundleType = eAssetBundleType::None;
|
eAssetBundleType m_AssetBundleType = eAssetBundleType::None;
|
||||||
|
|
||||||
PackIndex* m_PackIndex;
|
std::optional<PackIndex> m_PackIndex;
|
||||||
};
|
};
|
||||||
|
@ -21,19 +21,20 @@ Pack::Pack(const std::filesystem::path& filePath) {
|
|||||||
|
|
||||||
m_FileStream.seekg(recordCountPos, std::ios::beg);
|
m_FileStream.seekg(recordCountPos, std::ios::beg);
|
||||||
|
|
||||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_RecordCount);
|
uint32_t recordCount = 0;
|
||||||
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, recordCount);
|
||||||
|
|
||||||
for (int i = 0; i < m_RecordCount; i++) {
|
m_Records.reserve(recordCount);
|
||||||
|
std::generate_n(std::back_inserter(m_Records), recordCount, [&] {
|
||||||
PackRecord record;
|
PackRecord record;
|
||||||
BinaryIO::BinaryRead<PackRecord>(m_FileStream, record);
|
BinaryIO::BinaryRead<PackRecord>(m_FileStream, record);
|
||||||
|
return record;
|
||||||
m_Records.push_back(record);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
m_FileStream.close();
|
m_FileStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pack::HasFile(uint32_t crc) {
|
bool Pack::HasFile(const uint32_t crc) const {
|
||||||
for (const auto& record : m_Records) {
|
for (const auto& record : m_Records) {
|
||||||
if (record.m_Crc == crc) {
|
if (record.m_Crc == crc) {
|
||||||
return true;
|
return true;
|
||||||
@ -43,7 +44,7 @@ bool Pack::HasFile(uint32_t crc) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) const {
|
||||||
// Time for some wacky C file reading for speed reasons
|
// Time for some wacky C file reading for speed reasons
|
||||||
|
|
||||||
PackRecord pkRecord{};
|
PackRecord pkRecord{};
|
||||||
|
@ -24,16 +24,17 @@ struct PackRecord {
|
|||||||
class Pack {
|
class Pack {
|
||||||
public:
|
public:
|
||||||
Pack(const std::filesystem::path& filePath);
|
Pack(const std::filesystem::path& filePath);
|
||||||
~Pack() = default;
|
|
||||||
|
|
||||||
bool HasFile(uint32_t crc);
|
[[nodiscard]]
|
||||||
bool ReadFileFromPack(uint32_t crc, char** data, uint32_t* len);
|
bool HasFile(uint32_t crc) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) const;
|
||||||
private:
|
private:
|
||||||
std::ifstream m_FileStream;
|
std::ifstream m_FileStream;
|
||||||
std::filesystem::path m_FilePath;
|
std::filesystem::path m_FilePath;
|
||||||
|
|
||||||
char m_Version[7];
|
char m_Version[7];
|
||||||
|
|
||||||
uint32_t m_RecordCount;
|
|
||||||
std::vector<PackRecord> m_Records;
|
std::vector<PackRecord> m_Records;
|
||||||
};
|
};
|
||||||
|
@ -6,38 +6,32 @@
|
|||||||
PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
||||||
m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
|
m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
|
||||||
|
|
||||||
|
uint32_t packPathCount = 0;
|
||||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
|
||||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, packPathCount);
|
||||||
|
|
||||||
m_PackPaths.resize(m_PackPathCount);
|
m_PackPaths.resize(packPathCount);
|
||||||
for (auto& item : m_PackPaths) {
|
for (auto& item : m_PackPaths) {
|
||||||
BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
|
BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
|
uint32_t packFileIndexCount = 0;
|
||||||
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, packFileIndexCount);
|
||||||
|
|
||||||
for (int i = 0; i < m_PackFileIndexCount; i++) {
|
m_PackFileIndices.reserve(packFileIndexCount);
|
||||||
|
std::generate_n(std::back_inserter(m_PackFileIndices), packFileIndexCount, [&] {
|
||||||
PackFileIndex packFileIndex;
|
PackFileIndex packFileIndex;
|
||||||
BinaryIO::BinaryRead<PackFileIndex>(m_FileStream, packFileIndex);
|
BinaryIO::BinaryRead<PackFileIndex>(m_FileStream, packFileIndex);
|
||||||
|
return packFileIndex;
|
||||||
m_PackFileIndices.push_back(packFileIndex);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
LOG("Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size());
|
LOG("Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size());
|
||||||
|
|
||||||
|
m_Packs.reserve(m_PackPaths.size());
|
||||||
for (auto& item : m_PackPaths) {
|
for (auto& item : m_PackPaths) {
|
||||||
std::replace(item.begin(), item.end(), '\\', '/');
|
std::replace(item.begin(), item.end(), '\\', '/');
|
||||||
|
m_Packs.emplace_back(filePath / item);
|
||||||
auto* pack = new Pack(filePath / item);
|
|
||||||
|
|
||||||
m_Packs.push_back(pack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_FileStream.close();
|
m_FileStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
PackIndex::~PackIndex() {
|
|
||||||
for (const auto* item : m_Packs) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -21,20 +21,23 @@ struct PackFileIndex {
|
|||||||
class PackIndex {
|
class PackIndex {
|
||||||
public:
|
public:
|
||||||
PackIndex(const std::filesystem::path& filePath);
|
PackIndex(const std::filesystem::path& filePath);
|
||||||
~PackIndex();
|
|
||||||
|
|
||||||
const std::vector<std::string>& GetPackPaths() { return m_PackPaths; }
|
[[nodiscard]]
|
||||||
const std::vector<PackFileIndex>& GetPackFileIndices() { return m_PackFileIndices; }
|
const std::vector<std::string>& GetPackPaths() const { return m_PackPaths; }
|
||||||
const std::vector<Pack*>& GetPacks() { return m_Packs; }
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::vector<PackFileIndex>& GetPackFileIndices() const { return m_PackFileIndices; }
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::vector<Pack>& GetPacks() const { return m_Packs; }
|
||||||
private:
|
private:
|
||||||
std::ifstream m_FileStream;
|
std::ifstream m_FileStream;
|
||||||
|
|
||||||
uint32_t m_Version;
|
uint32_t m_Version;
|
||||||
|
|
||||||
uint32_t m_PackPathCount;
|
|
||||||
std::vector<std::string> m_PackPaths;
|
std::vector<std::string> m_PackPaths;
|
||||||
uint32_t m_PackFileIndexCount;
|
|
||||||
std::vector<PackFileIndex> m_PackFileIndices;
|
std::vector<PackFileIndex> m_PackFileIndices;
|
||||||
|
|
||||||
std::vector<Pack*> m_Packs;
|
std::vector<Pack> m_Packs;
|
||||||
};
|
};
|
||||||
|
@ -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...
|
||||||
|
@ -16,7 +16,9 @@ enum class eCharacterVersion : uint32_t {
|
|||||||
VAULT_SIZE,
|
VAULT_SIZE,
|
||||||
// Fixes speed base value in level component
|
// Fixes speed base value in level component
|
||||||
SPEED_BASE,
|
SPEED_BASE,
|
||||||
UP_TO_DATE, // will become NJ_JAYMISSIONS
|
// Fixes nexus force explorer missions
|
||||||
|
NJ_JAYMISSIONS,
|
||||||
|
UP_TO_DATE, // will become NEXUS_FORCE_EXPLORER
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__ECHARACTERVERSION__H__
|
#endif //!__ECHARACTERVERSION__H__
|
||||||
|
@ -7,7 +7,8 @@ enum class eConnectionType : uint16_t {
|
|||||||
CHAT,
|
CHAT,
|
||||||
WORLD = 4,
|
WORLD = 4,
|
||||||
CLIENT,
|
CLIENT,
|
||||||
MASTER
|
MASTER,
|
||||||
|
UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__ECONNECTIONTYPE__H__
|
#endif //!__ECONNECTIONTYPE__H__
|
||||||
|
26
dCommon/dEnums/eHTTPMethod.h
Normal file
26
dCommon/dEnums/eHTTPMethod.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef __EHTTPMETHODS__H__
|
||||||
|
#define __EHTTPMETHODS__H__
|
||||||
|
|
||||||
|
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||||
|
#pragma push_macro("DELETE")
|
||||||
|
#undef DELETE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class eHTTPMethod {
|
||||||
|
GET,
|
||||||
|
POST,
|
||||||
|
PUT,
|
||||||
|
DELETE,
|
||||||
|
HEAD,
|
||||||
|
CONNECT,
|
||||||
|
OPTIONS,
|
||||||
|
TRACE,
|
||||||
|
PATCH,
|
||||||
|
INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||||
|
#pragma pop_macro("DELETE")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __EHTTPMETHODS__H__
|
72
dCommon/dEnums/eHTTPStatusCode.h
Normal file
72
dCommon/dEnums/eHTTPStatusCode.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#ifndef __EHTTPSTATUSCODE__H__
|
||||||
|
#define __EHTTPSTATUSCODE__H__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
// verbose list of http codes
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
||||||
|
enum class eHTTPStatusCode : uint32_t {
|
||||||
|
CONTINUE = 100,
|
||||||
|
SWITCHING_PROTOCOLS = 101,
|
||||||
|
PROCESSING = 102,
|
||||||
|
EARLY_HINTS = 103,
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
NON_AUTHORITATIVE_INFORMATION = 203,
|
||||||
|
NO_CONTENT = 204,
|
||||||
|
RESET_CONTENT = 205,
|
||||||
|
PARTIAL_CONTENT = 206,
|
||||||
|
MULTI_STATUS = 207,
|
||||||
|
ALREADY_REPORTED = 208,
|
||||||
|
IM_USED = 226,
|
||||||
|
MULTIPLE_CHOICES = 300,
|
||||||
|
MOVED_PERMANENTLY = 301,
|
||||||
|
FOUND = 302,
|
||||||
|
SEE_OTHER = 303,
|
||||||
|
NOT_MODIFIED = 304,
|
||||||
|
USE_PROXY = 305,
|
||||||
|
SWITCH_PROXY = 306,
|
||||||
|
TEMPORARY_REDIRECT = 307,
|
||||||
|
PERMANENT_REDIRECT = 308,
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
PAYMENT_REQUIRED = 402,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
METHOD_NOT_ALLOWED = 405,
|
||||||
|
NOT_ACCEPTABLE = 406,
|
||||||
|
PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||||
|
REQUEST_TIMEOUT = 408,
|
||||||
|
CONFLICT = 409,
|
||||||
|
GONE = 410,
|
||||||
|
LENGTH_REQUIRED = 411,
|
||||||
|
PRECONDITION_FAILED = 412,
|
||||||
|
PAYLOAD_TOO_LARGE = 413,
|
||||||
|
URI_TOO_LONG = 414,
|
||||||
|
UNSUPPORTED_MEDIA_TYPE = 415,
|
||||||
|
RANGE_NOT_SATISFIABLE = 416,
|
||||||
|
EXPECTATION_FAILED = 417,
|
||||||
|
I_AM_A_TEAPOT = 418,
|
||||||
|
MISDIRECTED_REQUEST = 421,
|
||||||
|
UNPROCESSABLE_ENTITY = 422,
|
||||||
|
LOCKED = 423,
|
||||||
|
FAILED_DEPENDENCY = 424,
|
||||||
|
UPGRADE_REQUIRED = 426,
|
||||||
|
PRECONDITION_REQUIRED = 428,
|
||||||
|
TOO_MANY_REQUESTS = 429,
|
||||||
|
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||||
|
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||||
|
INTERNAL_SERVER_ERROR = 500,
|
||||||
|
NOT_IMPLEMENTED = 501,
|
||||||
|
BAD_GATEWAY = 502,
|
||||||
|
SERVICE_UNAVAILABLE = 503,
|
||||||
|
GATEWAY_TIMEOUT = 504,
|
||||||
|
HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||||
|
VARIANT_ALSO_NEGOTIATES = 506,
|
||||||
|
INSUFFICIENT_STORAGE = 507,
|
||||||
|
LOOP_DETECTED = 508,
|
||||||
|
NOT_EXTENDED = 510,
|
||||||
|
NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !__EHTTPSTATUSCODE__H__
|
@ -28,7 +28,8 @@ enum eInventoryType : uint32_t {
|
|||||||
DONATION,
|
DONATION,
|
||||||
VAULT_MODELS,
|
VAULT_MODELS,
|
||||||
ITEM_SETS, //internal, technically this is BankBehaviors.
|
ITEM_SETS, //internal, technically this is BankBehaviors.
|
||||||
INVALID // made up, for internal use!!!, Technically this called the ALL inventory.
|
INVALID, // made up, for internal use!!!, Technically this called the ALL inventory.
|
||||||
|
ALL, // Use this to search all inventories instead of a specific one.
|
||||||
};
|
};
|
||||||
|
|
||||||
class InventoryType {
|
class InventoryType {
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
#include "CDPlayerFlagsTable.h"
|
||||||
|
|
||||||
|
#include "CDClientDatabase.h"
|
||||||
|
|
||||||
|
namespace CDPlayerFlagsTable {
|
||||||
|
Table entries;
|
||||||
|
|
||||||
|
void ReadEntry(CppSQLite3Query& table) {
|
||||||
|
Entry entry;
|
||||||
|
entry.sessionOnly = table.getIntField("SessionOnly") == 1;
|
||||||
|
entry.onlySetByServer = table.getIntField("OnlySetByServer") == 1;
|
||||||
|
entry.sessionZoneOnly = table.getIntField("SessionZoneOnly") == 1;
|
||||||
|
entries[table.getIntField("id")] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadValuesFromDatabase() {
|
||||||
|
auto table = CDClientDatabase::ExecuteQuery("SELECT * FROM PlayerFlags;");
|
||||||
|
|
||||||
|
if (!table.eof()) {
|
||||||
|
do {
|
||||||
|
ReadEntry(table);
|
||||||
|
} while (!table.nextRow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<Entry> GetEntry(const FlagId flagId) {
|
||||||
|
if (!entries.contains(flagId)) {
|
||||||
|
auto table = CDClientDatabase::CreatePreppedStmt("SELECT * FROM PlayerFlags WHERE id = ?;");
|
||||||
|
table.bind(1, static_cast<int>(flagId));
|
||||||
|
auto result = table.execQuery();
|
||||||
|
if (!result.eof()) {
|
||||||
|
ReadEntry(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries[flagId];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef CDPLAYERFLAGSTABLE_H
|
||||||
|
#define CDPLAYERFLAGSTABLE_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace CDPlayerFlagsTable {
|
||||||
|
struct Entry {
|
||||||
|
bool sessionOnly{};
|
||||||
|
bool onlySetByServer{};
|
||||||
|
bool sessionZoneOnly{};
|
||||||
|
};
|
||||||
|
|
||||||
|
using FlagId = uint32_t;
|
||||||
|
using Table = std::map<FlagId, std::optional<Entry>>;
|
||||||
|
|
||||||
|
void LoadValuesFromDatabase();
|
||||||
|
const std::optional<Entry> GetEntry(const FlagId flagId);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!CDPLAYERFLAGSTABLE_H
|
@ -25,6 +25,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
|
|||||||
"CDObjectsTable.cpp"
|
"CDObjectsTable.cpp"
|
||||||
"CDPetComponentTable.cpp"
|
"CDPetComponentTable.cpp"
|
||||||
"CDPackageComponentTable.cpp"
|
"CDPackageComponentTable.cpp"
|
||||||
|
"CDPlayerFlagsTable.cpp"
|
||||||
"CDPhysicsComponentTable.cpp"
|
"CDPhysicsComponentTable.cpp"
|
||||||
"CDPropertyEntranceComponentTable.cpp"
|
"CDPropertyEntranceComponentTable.cpp"
|
||||||
"CDPropertyTemplateTable.cpp"
|
"CDPropertyTemplateTable.cpp"
|
||||||
|
@ -44,6 +44,8 @@ public:
|
|||||||
|
|
||||||
// Updates the given character ids last login to be right now.
|
// Updates the given character ids last login to be right now.
|
||||||
virtual void UpdateLastLoggedInCharacter(const uint32_t characterId) = 0;
|
virtual void UpdateLastLoggedInCharacter(const uint32_t characterId) = 0;
|
||||||
|
|
||||||
|
virtual bool IsNameInUse(const std::string_view name) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__ICHARINFO__H__
|
#endif //!__ICHARINFO__H__
|
||||||
|
@ -8,27 +8,10 @@
|
|||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
#include "NiQuaternion.h"
|
#include "NiQuaternion.h"
|
||||||
#include "NiPoint3.h"
|
#include "NiPoint3.h"
|
||||||
|
#include "MailInfo.h"
|
||||||
|
|
||||||
class IMail {
|
class IMail {
|
||||||
public:
|
public:
|
||||||
struct MailInfo {
|
|
||||||
std::string senderUsername;
|
|
||||||
std::string recipient;
|
|
||||||
std::string subject;
|
|
||||||
std::string body;
|
|
||||||
uint64_t id{};
|
|
||||||
uint32_t senderId{};
|
|
||||||
uint32_t receiverId{};
|
|
||||||
uint64_t timeSent{};
|
|
||||||
bool wasRead{};
|
|
||||||
struct {
|
|
||||||
LWOOBJID itemID{};
|
|
||||||
int32_t itemCount{};
|
|
||||||
LOT itemLOT{};
|
|
||||||
LWOOBJID itemSubkey{};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Insert a new mail into the database.
|
// Insert a new mail into the database.
|
||||||
virtual void InsertNewMail(const MailInfo& mail) = 0;
|
virtual void InsertNewMail(const MailInfo& mail) = 0;
|
||||||
|
|
||||||
|
@ -53,6 +53,9 @@ public:
|
|||||||
// Update the property details for the given property id.
|
// Update the property details for the given property id.
|
||||||
virtual void UpdatePropertyDetails(const IProperty::Info& info) = 0;
|
virtual void UpdatePropertyDetails(const IProperty::Info& info) = 0;
|
||||||
|
|
||||||
|
// Update the last updated time for the given property id.
|
||||||
|
virtual void UpdateLastSave(const IProperty::Info& info) = 0;
|
||||||
|
|
||||||
// Update the property performance cost for the given property id.
|
// Update the property performance cost for the given property id.
|
||||||
virtual void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) = 0;
|
virtual void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) = 0;
|
||||||
|
|
||||||
|
@ -9,10 +9,11 @@ public:
|
|||||||
struct MasterInfo {
|
struct MasterInfo {
|
||||||
std::string ip;
|
std::string ip;
|
||||||
uint32_t port{};
|
uint32_t port{};
|
||||||
|
std::string password{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set the master server ip and port.
|
// Set the master server ip and port.
|
||||||
virtual void SetMasterIp(const std::string_view ip, const uint32_t port) = 0;
|
virtual void SetMasterInfo(const MasterInfo& info) = 0;
|
||||||
|
|
||||||
// Get the master server info.
|
// Get the master server info.
|
||||||
virtual std::optional<MasterInfo> GetMasterInfo() = 0;
|
virtual std::optional<MasterInfo> GetMasterInfo() = 0;
|
||||||
|
@ -70,6 +70,7 @@ public:
|
|||||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||||
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
||||||
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
||||||
|
void UpdateLastSave(const IProperty::Info& info) override;
|
||||||
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
||||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||||
void RemoveUnreferencedUgcModels() override;
|
void RemoveUnreferencedUgcModels() override;
|
||||||
@ -79,14 +80,14 @@ public:
|
|||||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||||
void InsertNewMail(const IMail::MailInfo& mail) override;
|
void InsertNewMail(const MailInfo& mail) override;
|
||||||
void InsertNewUgcModel(
|
void InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data,
|
std::istringstream& sd0Data,
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) override;
|
const uint32_t characterId) override;
|
||||||
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||||
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
|
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||||
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
||||||
void MarkMailRead(const uint64_t mailId) override;
|
void MarkMailRead(const uint64_t mailId) override;
|
||||||
void DeleteMail(const uint64_t mailId) override;
|
void DeleteMail(const uint64_t mailId) override;
|
||||||
@ -96,7 +97,7 @@ public:
|
|||||||
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
|
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
|
||||||
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
|
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
|
||||||
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
|
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
|
||||||
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
|
void SetMasterInfo(const IServers::MasterInfo& info) override;
|
||||||
std::optional<uint32_t> GetCurrentPersistentId() override;
|
std::optional<uint32_t> GetCurrentPersistentId() override;
|
||||||
void InsertDefaultPersistentId() override;
|
void InsertDefaultPersistentId() override;
|
||||||
void UpdatePersistentId(const uint32_t id) override;
|
void UpdatePersistentId(const uint32_t id) override;
|
||||||
@ -124,8 +125,9 @@ public:
|
|||||||
void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override;
|
void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override;
|
||||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
|
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
|
||||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||||
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
|
||||||
uint32_t GetAccountCount() override;
|
uint32_t GetAccountCount() override;
|
||||||
|
bool IsNameInUse(const std::string_view name) override;
|
||||||
|
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Generic query functions that can be used for any query.
|
// Generic query functions that can be used for any query.
|
||||||
|
@ -76,3 +76,9 @@ void MySQLDatabase::SetPendingCharacterName(const uint32_t characterId, const st
|
|||||||
void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
|
void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
|
||||||
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast<uint32_t>(time(NULL)), characterId);
|
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast<uint32_t>(time(NULL)), characterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MySQLDatabase::IsNameInUse(const std::string_view name) {
|
||||||
|
auto result = ExecuteSelect("SELECT name FROM charinfo WHERE name = ? or pending_name = ? LIMIT 1;", name, name);
|
||||||
|
|
||||||
|
return result->next();
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "MySQLDatabase.h"
|
#include "MySQLDatabase.h"
|
||||||
|
|
||||||
void MySQLDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
|
||||||
|
void MySQLDatabase::InsertNewMail(const MailInfo& mail) {
|
||||||
ExecuteInsert(
|
ExecuteInsert(
|
||||||
"INSERT INTO `mail` "
|
"INSERT INTO `mail` "
|
||||||
"(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)"
|
"(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)"
|
||||||
@ -18,17 +19,17 @@ void MySQLDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
|||||||
mail.itemCount);
|
mail.itemCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IMail::MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
std::vector<MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||||
auto res = ExecuteSelect(
|
auto res = ExecuteSelect(
|
||||||
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
|
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
|
||||||
" FROM mail WHERE receiver_id=? limit ?;",
|
" FROM mail WHERE receiver_id=? limit ?;",
|
||||||
characterId, numberOfMail);
|
characterId, numberOfMail);
|
||||||
|
|
||||||
std::vector<IMail::MailInfo> toReturn;
|
std::vector<MailInfo> toReturn;
|
||||||
toReturn.reserve(res->rowsCount());
|
toReturn.reserve(res->rowsCount());
|
||||||
|
|
||||||
while (res->next()) {
|
while (res->next()) {
|
||||||
IMail::MailInfo mail;
|
MailInfo mail;
|
||||||
mail.id = res->getUInt64("id");
|
mail.id = res->getUInt64("id");
|
||||||
mail.subject = res->getString("subject").c_str();
|
mail.subject = res->getString("subject").c_str();
|
||||||
mail.body = res->getString("body").c_str();
|
mail.body = res->getString("body").c_str();
|
||||||
@ -46,14 +47,14 @@ std::vector<IMail::MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t char
|
|||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IMail::MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
|
std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
|
||||||
auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
||||||
|
|
||||||
if (!res->next()) {
|
if (!res->next()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMail::MailInfo toReturn;
|
MailInfo toReturn;
|
||||||
toReturn.itemLOT = res->getInt("attachment_lot");
|
toReturn.itemLOT = res->getInt("attachment_lot");
|
||||||
toReturn.itemCount = res->getInt("attachment_count");
|
toReturn.itemCount = res->getInt("attachment_count");
|
||||||
|
|
||||||
|
@ -173,6 +173,10 @@ void MySQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
|||||||
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ? LIMIT 1;", info.name, info.description, info.id);
|
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ? LIMIT 1;", info.name, info.description, info.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::UpdateLastSave(const IProperty::Info& info) {
|
||||||
|
ExecuteUpdate("UPDATE properties SET last_updated = ? WHERE id = ?;", info.lastUpdatedTime, info.id);
|
||||||
|
}
|
||||||
|
|
||||||
void MySQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
void MySQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
||||||
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ? LIMIT 1;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ? LIMIT 1;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include "MySQLDatabase.h"
|
#include "MySQLDatabase.h"
|
||||||
|
|
||||||
void MySQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
|
void MySQLDatabase::SetMasterInfo(const MasterInfo& info) {
|
||||||
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
|
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
|
||||||
// since it would be two queries anyways.
|
// since it would be two queries anyways.
|
||||||
ExecuteDelete("TRUNCATE TABLE servers;");
|
ExecuteDelete("TRUNCATE TABLE servers;");
|
||||||
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
|
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022, ?)", info.ip, info.port, info.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
|
std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
|
||||||
auto result = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
|
auto result = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
|
||||||
|
|
||||||
if (!result->next()) {
|
if (!result->next()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
|
|||||||
|
|
||||||
toReturn.ip = result->getString("ip").c_str();
|
toReturn.ip = result->getString("ip").c_str();
|
||||||
toReturn.port = result->getInt("port");
|
toReturn.port = result->getInt("port");
|
||||||
|
toReturn.password = result->getString("master_password").c_str();
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ public:
|
|||||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||||
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
||||||
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
||||||
|
void UpdateLastSave(const IProperty::Info& info) override;
|
||||||
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
||||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||||
void RemoveUnreferencedUgcModels() override;
|
void RemoveUnreferencedUgcModels() override;
|
||||||
@ -77,14 +78,14 @@ public:
|
|||||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||||
void InsertNewMail(const IMail::MailInfo& mail) override;
|
void InsertNewMail(const MailInfo& mail) override;
|
||||||
void InsertNewUgcModel(
|
void InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data,
|
std::istringstream& sd0Data,
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) override;
|
const uint32_t characterId) override;
|
||||||
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||||
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
|
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||||
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
||||||
void MarkMailRead(const uint64_t mailId) override;
|
void MarkMailRead(const uint64_t mailId) override;
|
||||||
void DeleteMail(const uint64_t mailId) override;
|
void DeleteMail(const uint64_t mailId) override;
|
||||||
@ -94,7 +95,7 @@ public:
|
|||||||
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
|
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
|
||||||
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
|
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
|
||||||
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
|
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
|
||||||
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
|
void SetMasterInfo(const IServers::MasterInfo& info) override;
|
||||||
std::optional<uint32_t> GetCurrentPersistentId() override;
|
std::optional<uint32_t> GetCurrentPersistentId() override;
|
||||||
void InsertDefaultPersistentId() override;
|
void InsertDefaultPersistentId() override;
|
||||||
void UpdatePersistentId(const uint32_t id) override;
|
void UpdatePersistentId(const uint32_t id) override;
|
||||||
@ -123,6 +124,7 @@ public:
|
|||||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
|
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
|
||||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||||
uint32_t GetAccountCount() override;
|
uint32_t GetAccountCount() override;
|
||||||
|
bool IsNameInUse(const std::string_view name) override;
|
||||||
private:
|
private:
|
||||||
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
|
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
|
||||||
|
|
||||||
|
@ -77,3 +77,9 @@ void SQLiteDatabase::SetPendingCharacterName(const uint32_t characterId, const s
|
|||||||
void SQLiteDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
|
void SQLiteDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
|
||||||
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ?;", static_cast<uint32_t>(time(NULL)), characterId);
|
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ?;", static_cast<uint32_t>(time(NULL)), characterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SQLiteDatabase::IsNameInUse(const std::string_view name) {
|
||||||
|
auto [_, result] = ExecuteSelect("SELECT name FROM charinfo WHERE name = ? or pending_name = ? LIMIT 1;", name, name);
|
||||||
|
|
||||||
|
return !result.eof();
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "SQLiteDatabase.h"
|
#include "SQLiteDatabase.h"
|
||||||
|
|
||||||
void SQLiteDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
void SQLiteDatabase::InsertNewMail(const MailInfo& mail) {
|
||||||
ExecuteInsert(
|
ExecuteInsert(
|
||||||
"INSERT INTO `mail` "
|
"INSERT INTO `mail` "
|
||||||
"(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)"
|
"(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)"
|
||||||
@ -18,16 +18,16 @@ void SQLiteDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
|||||||
mail.itemCount);
|
mail.itemCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IMail::MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
std::vector<MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||||
auto [_, res] = ExecuteSelect(
|
auto [_, res] = ExecuteSelect(
|
||||||
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
|
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
|
||||||
" FROM mail WHERE receiver_id=? limit ?;",
|
" FROM mail WHERE receiver_id=? limit ?;",
|
||||||
characterId, numberOfMail);
|
characterId, numberOfMail);
|
||||||
|
|
||||||
std::vector<IMail::MailInfo> toReturn;
|
std::vector<MailInfo> toReturn;
|
||||||
|
|
||||||
while (!res.eof()) {
|
while (!res.eof()) {
|
||||||
IMail::MailInfo mail;
|
MailInfo mail;
|
||||||
mail.id = res.getInt64Field("id");
|
mail.id = res.getInt64Field("id");
|
||||||
mail.subject = res.getStringField("subject");
|
mail.subject = res.getStringField("subject");
|
||||||
mail.body = res.getStringField("body");
|
mail.body = res.getStringField("body");
|
||||||
@ -46,14 +46,14 @@ std::vector<IMail::MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t cha
|
|||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IMail::MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
|
std::optional<MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
|
||||||
auto [_, res] = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
auto [_, res] = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
||||||
|
|
||||||
if (res.eof()) {
|
if (res.eof()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMail::MailInfo toReturn;
|
MailInfo toReturn;
|
||||||
toReturn.itemLOT = res.getIntField("attachment_lot");
|
toReturn.itemLOT = res.getIntField("attachment_lot");
|
||||||
toReturn.itemCount = res.getIntField("attachment_count");
|
toReturn.itemCount = res.getIntField("attachment_count");
|
||||||
|
|
||||||
|
@ -175,6 +175,10 @@ void SQLiteDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
|||||||
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ?;", info.name, info.description, info.id);
|
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ?;", info.name, info.description, info.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SQLiteDatabase::UpdateLastSave(const IProperty::Info& info) {
|
||||||
|
ExecuteUpdate("UPDATE properties SET last_updated = ? WHERE id = ?;", info.lastUpdatedTime, info.id);
|
||||||
|
}
|
||||||
|
|
||||||
void SQLiteDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
void SQLiteDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
||||||
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ?;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ?;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include "SQLiteDatabase.h"
|
#include "SQLiteDatabase.h"
|
||||||
|
|
||||||
void SQLiteDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
|
void SQLiteDatabase::SetMasterInfo(const MasterInfo& info) {
|
||||||
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
|
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
|
||||||
// since it would be two queries anyways.
|
// since it would be two queries anyways.
|
||||||
ExecuteDelete("DELETE FROM servers;");
|
ExecuteDelete("DELETE FROM servers;");
|
||||||
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
|
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022, ?)", info.ip, info.port, info.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
|
std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
|
||||||
auto [_, result] = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
|
auto [_, result] = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
|
||||||
|
|
||||||
if (result.eof()) {
|
if (result.eof()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
|
|||||||
|
|
||||||
toReturn.ip = result.getStringField("ip");
|
toReturn.ip = result.getStringField("ip");
|
||||||
toReturn.port = result.getIntField("port");
|
toReturn.port = result.getIntField("port");
|
||||||
|
toReturn.password = result.getStringField("master_password");
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
@ -148,6 +148,10 @@ void TestSQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestSQLDatabase::UpdateLastSave(const IProperty::Info& info) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void TestSQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) {
|
void TestSQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -184,7 +188,7 @@ void TestSQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& i
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSQLDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
void TestSQLDatabase::InsertNewMail(const MailInfo& mail) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,11 +196,11 @@ void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint3
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IMail::MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
std::vector<MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IMail::MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
|
std::optional<MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +240,7 @@ void TestSQLDatabase::InsertNewAccount(const std::string_view username, const st
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
|
void TestSQLDatabase::SetMasterInfo(const IServers::MasterInfo& info) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||||
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
||||||
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
||||||
|
void UpdateLastSave(const IProperty::Info& info) override;
|
||||||
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
||||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||||
void RemoveUnreferencedUgcModels() override;
|
void RemoveUnreferencedUgcModels() override;
|
||||||
@ -56,14 +57,14 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||||
void InsertNewMail(const IMail::MailInfo& mail) override;
|
void InsertNewMail(const MailInfo& mail) override;
|
||||||
void InsertNewUgcModel(
|
void InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data,
|
std::istringstream& sd0Data,
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) override;
|
const uint32_t characterId) override;
|
||||||
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||||
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
|
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||||
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
||||||
void MarkMailRead(const uint64_t mailId) override;
|
void MarkMailRead(const uint64_t mailId) override;
|
||||||
void DeleteMail(const uint64_t mailId) override;
|
void DeleteMail(const uint64_t mailId) override;
|
||||||
@ -73,7 +74,7 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
|
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
|
||||||
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
|
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
|
||||||
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
|
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
|
||||||
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
|
void SetMasterInfo(const IServers::MasterInfo& info) override;
|
||||||
std::optional<uint32_t> GetCurrentPersistentId() override;
|
std::optional<uint32_t> GetCurrentPersistentId() override;
|
||||||
void InsertDefaultPersistentId() override;
|
void InsertDefaultPersistentId() override;
|
||||||
void UpdatePersistentId(const uint32_t id) override;
|
void UpdatePersistentId(const uint32_t id) override;
|
||||||
@ -102,6 +103,8 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override {};
|
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override {};
|
||||||
void DeleteUgcBuild(const LWOOBJID bigId) override {};
|
void DeleteUgcBuild(const LWOOBJID bigId) override {};
|
||||||
uint32_t GetAccountCount() override { return 0; };
|
uint32_t GetAccountCount() override { return 0; };
|
||||||
|
|
||||||
|
bool IsNameInUse(const std::string_view name) override { return false; };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!TESTSQLDATABASE_H
|
#endif //!TESTSQLDATABASE_H
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "eObjectBits.h"
|
#include "eObjectBits.h"
|
||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
#include "ePlayerFlag.h"
|
#include "ePlayerFlag.h"
|
||||||
|
#include "CDPlayerFlagsTable.h"
|
||||||
|
|
||||||
Character::Character(uint32_t id, User* parentUser) {
|
Character::Character(uint32_t id, User* parentUser) {
|
||||||
//First load the name, etc:
|
//First load the name, etc:
|
||||||
@ -203,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;
|
||||||
@ -211,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();
|
||||||
}
|
}
|
||||||
@ -231,6 +236,12 @@ void Character::SetBuildMode(bool buildMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Character::SaveXMLToDatabase() {
|
void Character::SaveXMLToDatabase() {
|
||||||
|
// Check that we can actually _save_ before saving
|
||||||
|
if (!m_OurEntity) {
|
||||||
|
LOG("%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//For metrics, we'll record the time it took to save:
|
//For metrics, we'll record the time it took to save:
|
||||||
auto start = std::chrono::system_clock::now();
|
auto start = std::chrono::system_clock::now();
|
||||||
|
|
||||||
@ -277,33 +288,19 @@ void Character::SaveXMLToDatabase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
|
flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
|
||||||
for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) {
|
for (const auto& [index, flagBucket] : m_PlayerFlags) {
|
||||||
auto* f = m_Doc.NewElement("f");
|
auto* f = flags->InsertNewChildElement("f");
|
||||||
f->SetAttribute("id", flag.first);
|
f->SetAttribute("id", index);
|
||||||
|
f->SetAttribute("v", flagBucket);
|
||||||
//Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute.
|
|
||||||
//Only signed 64-bits ints would work.
|
|
||||||
std::string v = std::to_string(flag.second);
|
|
||||||
f->SetAttribute("v", v.c_str());
|
|
||||||
|
|
||||||
flags->LinkEndChild(f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevents the news feed from showing up on world transfers
|
for (const auto& sessionFlag : m_SessionFlags) {
|
||||||
if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) {
|
auto* s = flags->InsertNewChildElement("s");
|
||||||
auto* s = m_Doc.NewElement("s");
|
s->SetAttribute("si", sessionFlag);
|
||||||
s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE);
|
|
||||||
flags->LinkEndChild(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveXmlRespawnCheckpoints();
|
SaveXmlRespawnCheckpoints();
|
||||||
|
|
||||||
//Call upon the entity to update our xmlDoc:
|
|
||||||
if (!m_OurEntity) {
|
|
||||||
LOG("%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_OurEntity->UpdateXMLDoc(m_Doc);
|
m_OurEntity->UpdateXMLDoc(m_Doc);
|
||||||
|
|
||||||
WriteToDatabase();
|
WriteToDatabase();
|
||||||
@ -323,8 +320,8 @@ void Character::SetIsNewLogin() {
|
|||||||
while (currentChild) {
|
while (currentChild) {
|
||||||
auto* nextChild = currentChild->NextSiblingElement();
|
auto* nextChild = currentChild->NextSiblingElement();
|
||||||
if (currentChild->Attribute("si")) {
|
if (currentChild->Attribute("si")) {
|
||||||
|
LOG("Removed session flag (%s) from character %i:%s, saving character to database", currentChild->Attribute("si"), GetID(), GetName().c_str());
|
||||||
flags->DeleteChild(currentChild);
|
flags->DeleteChild(currentChild);
|
||||||
LOG("Removed isLoggedIn flag from character %i:%s, saving character to database", GetID(), GetName().c_str());
|
|
||||||
WriteToDatabase();
|
WriteToDatabase();
|
||||||
}
|
}
|
||||||
currentChild = nextChild;
|
currentChild = nextChild;
|
||||||
@ -357,49 +354,64 @@ void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the index first
|
const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);
|
||||||
auto flagIndex = uint32_t(std::floor(flagId / 64));
|
|
||||||
|
|
||||||
const auto shiftedValue = 1ULL << flagId % 64;
|
if (flagEntry && flagEntry->sessionOnly) {
|
||||||
|
if (value) m_SessionFlags.insert(flagId);
|
||||||
auto it = m_PlayerFlags.find(flagIndex);
|
else m_SessionFlags.erase(flagId);
|
||||||
|
|
||||||
// Check if flag index exists
|
|
||||||
if (it != m_PlayerFlags.end()) {
|
|
||||||
// Update the value
|
|
||||||
if (value) {
|
|
||||||
it->second |= shiftedValue;
|
|
||||||
} else {
|
|
||||||
it->second &= ~shiftedValue;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (value) {
|
// Calculate the index first
|
||||||
// Otherwise, insert the value
|
auto flagIndex = uint32_t(std::floor(flagId / 64));
|
||||||
uint64_t flagValue = 0;
|
|
||||||
|
|
||||||
flagValue |= shiftedValue;
|
const auto shiftedValue = 1ULL << flagId % 64;
|
||||||
|
|
||||||
m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue));
|
auto it = m_PlayerFlags.find(flagIndex);
|
||||||
|
|
||||||
|
// Check if flag index exists
|
||||||
|
if (it != m_PlayerFlags.end()) {
|
||||||
|
// Update the value
|
||||||
|
if (value) {
|
||||||
|
it->second |= shiftedValue;
|
||||||
|
} else {
|
||||||
|
it->second &= ~shiftedValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (value) {
|
||||||
|
// Otherwise, insert the value
|
||||||
|
uint64_t flagValue = 0;
|
||||||
|
|
||||||
|
flagValue |= shiftedValue;
|
||||||
|
|
||||||
|
m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the client that a flag has changed server-side
|
// Notify the client that a flag has changed server-side
|
||||||
GameMessages::SendNotifyClientFlagChange(m_ObjectID, flagId, value, m_ParentUser->GetSystemAddress());
|
GameMessages::SendNotifyClientFlagChange(m_ObjectID, flagId, value, m_ParentUser->GetSystemAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Character::GetPlayerFlag(const uint32_t flagId) const {
|
bool Character::GetPlayerFlag(const uint32_t flagId) const {
|
||||||
// Calculate the index first
|
using enum ePlayerFlag;
|
||||||
const auto flagIndex = uint32_t(std::floor(flagId / 64));
|
|
||||||
|
|
||||||
const auto shiftedValue = 1ULL << flagId % 64;
|
bool toReturn = false; //by def, return false.
|
||||||
|
|
||||||
auto it = m_PlayerFlags.find(flagIndex);
|
const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);
|
||||||
if (it != m_PlayerFlags.end()) {
|
if (flagEntry && flagEntry->sessionOnly) {
|
||||||
// Don't set the data if we don't have to
|
toReturn = m_SessionFlags.contains(flagId);
|
||||||
return (it->second & shiftedValue) != 0;
|
} else {
|
||||||
|
// Calculate the index first
|
||||||
|
const auto flagIndex = uint32_t(std::floor(flagId / 64));
|
||||||
|
|
||||||
|
const auto shiftedValue = 1ULL << flagId % 64;
|
||||||
|
|
||||||
|
auto it = m_PlayerFlags.find(flagIndex);
|
||||||
|
if (it != m_PlayerFlags.end()) {
|
||||||
|
// Don't set the data if we don't have to
|
||||||
|
toReturn = (it->second & shiftedValue) != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; //by def, return false.
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Character::SetRetroactiveFlags() {
|
void Character::SetRetroactiveFlags() {
|
||||||
|
@ -620,6 +620,12 @@ private:
|
|||||||
*/
|
*/
|
||||||
uint64_t m_LastLogin{};
|
uint64_t m_LastLogin{};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags only set for the duration of a session
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
std::set<uint32_t> m_SessionFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The gameplay flags this character has (not just true values)
|
* The gameplay flags this character has (not just true values)
|
||||||
*/
|
*/
|
||||||
|
@ -386,6 +386,9 @@ void Entity::Initialize() {
|
|||||||
if (m_Character) {
|
if (m_Character) {
|
||||||
comp->LoadFromXml(m_Character->GetXMLDoc());
|
comp->LoadFromXml(m_Character->GetXMLDoc());
|
||||||
} else {
|
} else {
|
||||||
|
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
|
||||||
|
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
|
||||||
|
|
||||||
if (componentID > 0) {
|
if (componentID > 0) {
|
||||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||||
|
|
||||||
@ -420,9 +423,6 @@ void Entity::Initialize() {
|
|||||||
comp->SetMinCoins(currencyValues[0].minvalue);
|
comp->SetMinCoins(currencyValues[0].minvalue);
|
||||||
comp->SetMaxCoins(currencyValues[0].maxvalue);
|
comp->SetMaxCoins(currencyValues[0].maxvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
|
|
||||||
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
comp->SetHealth(1);
|
comp->SetHealth(1);
|
||||||
@ -860,6 +860,9 @@ void Entity::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, co
|
|||||||
auto* destroyableComponent = GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = GetComponent<DestroyableComponent>();
|
||||||
if (!destroyableComponent) return;
|
if (!destroyableComponent) return;
|
||||||
destroyableComponent->Subscribe(scriptObjId, scriptToAdd);
|
destroyableComponent->Subscribe(scriptObjId, scriptToAdd);
|
||||||
|
} else if (notificationName == "PlayerResurrectionFinished") {
|
||||||
|
LOG("Subscribing to PlayerResurrectionFinished");
|
||||||
|
m_Subscriptions[scriptObjId][notificationName] = scriptToAdd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,6 +871,9 @@ void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationNa
|
|||||||
auto* destroyableComponent = GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = GetComponent<DestroyableComponent>();
|
||||||
if (!destroyableComponent) return;
|
if (!destroyableComponent) return;
|
||||||
destroyableComponent->Unsubscribe(scriptObjId);
|
destroyableComponent->Unsubscribe(scriptObjId);
|
||||||
|
} else if (notificationName == "PlayerResurrectionFinished") {
|
||||||
|
LOG("Unsubscribing from PlayerResurrectionFinished");
|
||||||
|
m_Subscriptions[scriptObjId].erase(notificationName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1511,6 +1517,15 @@ void Entity::OnChildLoaded(GameMessages::ChildLoaded& childLoaded) {
|
|||||||
GetScript()->OnChildLoaded(*this, childLoaded);
|
GetScript()->OnChildLoaded(*this, childLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Entity::NotifyPlayerResurrectionFinished(GameMessages::PlayerResurrectionFinished& msg) {
|
||||||
|
for (const auto& [id, scriptList] : m_Subscriptions) {
|
||||||
|
auto it = scriptList.find("PlayerResurrectionFinished");
|
||||||
|
if (it == scriptList.end()) continue;
|
||||||
|
|
||||||
|
it->second->NotifyPlayerResurrectionFinished(*this, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) {
|
void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) {
|
||||||
GetScript()->OnRequestActivityExit(sender, player, canceled);
|
GetScript()->OnRequestActivityExit(sender, player, canceled);
|
||||||
}
|
}
|
||||||
@ -1550,7 +1565,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
|
|||||||
|
|
||||||
m_DieCallbacks.clear();
|
m_DieCallbacks.clear();
|
||||||
|
|
||||||
//OMAI WA MOU, SHINDERIU
|
//お前はもう死んでいる
|
||||||
|
|
||||||
GetScript()->OnDie(this, murderer);
|
GetScript()->OnDie(this, murderer);
|
||||||
|
|
||||||
@ -2193,7 +2208,7 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) {
|
|||||||
|
|
||||||
int32_t Entity::GetCollisionGroup() const {
|
int32_t Entity::GetCollisionGroup() const {
|
||||||
for (const auto* component : m_Components | std::views::values) {
|
for (const auto* component : m_Components | std::views::values) {
|
||||||
auto* compToCheck = dynamic_cast<const PhysicsComponent*>(component);
|
auto* compToCheck = dynamic_cast<const PhysicsComponent*>(component);
|
||||||
if (compToCheck) {
|
if (compToCheck) {
|
||||||
return compToCheck->GetCollisionGroup();
|
return compToCheck->GetCollisionGroup();
|
||||||
}
|
}
|
||||||
@ -2201,3 +2216,17 @@ int32_t Entity::GetCollisionGroup() const {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entity::HandleMsg(GameMessages::GameMsg& msg) const {
|
||||||
|
bool handled = false;
|
||||||
|
const auto [beg, end] = m_MsgHandlers.equal_range(msg.msgId);
|
||||||
|
for (auto it = beg; it != end; ++it) {
|
||||||
|
if (it->second) handled |= it->second(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entity::RegisterMsg(const MessageType::Game msgId, std::function<bool(GameMessages::GameMsg&)> handler) {
|
||||||
|
m_MsgHandlers.emplace(msgId, handler);
|
||||||
|
}
|
||||||
|
@ -14,11 +14,17 @@
|
|||||||
#include "Observable.h"
|
#include "Observable.h"
|
||||||
|
|
||||||
namespace GameMessages {
|
namespace GameMessages {
|
||||||
|
struct GameMsg;
|
||||||
struct ActivityNotify;
|
struct ActivityNotify;
|
||||||
struct ShootingGalleryFire;
|
struct ShootingGalleryFire;
|
||||||
struct ChildLoaded;
|
struct ChildLoaded;
|
||||||
|
struct PlayerResurrectionFinished;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace MessageType {
|
||||||
|
enum class Game : uint16_t;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Loot {
|
namespace Loot {
|
||||||
class Info;
|
class Info;
|
||||||
};
|
};
|
||||||
@ -219,6 +225,7 @@ public:
|
|||||||
void OnActivityNotify(GameMessages::ActivityNotify& notify);
|
void OnActivityNotify(GameMessages::ActivityNotify& notify);
|
||||||
void OnShootingGalleryFire(GameMessages::ShootingGalleryFire& notify);
|
void OnShootingGalleryFire(GameMessages::ShootingGalleryFire& notify);
|
||||||
void OnChildLoaded(GameMessages::ChildLoaded& childLoaded);
|
void OnChildLoaded(GameMessages::ChildLoaded& childLoaded);
|
||||||
|
void NotifyPlayerResurrectionFinished(GameMessages::PlayerResurrectionFinished& msg);
|
||||||
|
|
||||||
void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData);
|
void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData);
|
||||||
void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier);
|
void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier);
|
||||||
@ -314,6 +321,10 @@ public:
|
|||||||
// Scale will only be communicated to the client when the construction packet is sent
|
// Scale will only be communicated to the client when the construction packet is sent
|
||||||
void SetScale(const float scale) { m_Scale = scale; };
|
void SetScale(const float scale) { m_Scale = scale; };
|
||||||
|
|
||||||
|
void RegisterMsg(const MessageType::Game msgId, std::function<bool(GameMessages::GameMsg&)> handler);
|
||||||
|
|
||||||
|
bool HandleMsg(GameMessages::GameMsg& msg) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The observable for player entity position updates.
|
* @brief The observable for player entity position updates.
|
||||||
*/
|
*/
|
||||||
@ -372,6 +383,11 @@ protected:
|
|||||||
* Collision
|
* Collision
|
||||||
*/
|
*/
|
||||||
std::vector<LWOOBJID> m_TargetsInPhantom;
|
std::vector<LWOOBJID> m_TargetsInPhantom;
|
||||||
|
|
||||||
|
// objectID of receiver and map of notification name to script
|
||||||
|
std::map<LWOOBJID, std::map<std::string, CppScripts::Script*>> m_Subscriptions;
|
||||||
|
|
||||||
|
std::multimap<MessageType::Game, std::function<bool(GameMessages::GameMsg&)>> m_MsgHandlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -305,13 +305,13 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
|
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
|
||||||
LOT pantsLOT = FindCharPantsID(pantsColor);
|
LOT pantsLOT = FindCharPantsID(pantsColor);
|
||||||
|
|
||||||
if (!name.empty() && Database::Get()->GetCharacterInfo(name)) {
|
if (!name.empty() && Database::Get()->IsNameInUse(name)) {
|
||||||
LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str());
|
LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str());
|
||||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE);
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Database::Get()->GetCharacterInfo(predefinedName)) {
|
if (Database::Get()->IsNameInUse(predefinedName)) {
|
||||||
LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
||||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE);
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE);
|
||||||
return;
|
return;
|
||||||
@ -324,7 +324,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Now that the name is ok, we can get an objectID from Master:
|
//Now that the name is ok, we can get an objectID from Master:
|
||||||
ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) mutable {
|
ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) {
|
||||||
if (Database::Get()->GetCharacterInfo(objectID)) {
|
if (Database::Get()->GetCharacterInfo(objectID)) {
|
||||||
LOG("Character object id unavailable, check object_id_tracker!");
|
LOG("Character object id unavailable, check object_id_tracker!");
|
||||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
||||||
@ -369,13 +369,14 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
|
|
||||||
// If predefined name is invalid, change it to be their object id
|
// If predefined name is invalid, change it to be their object id
|
||||||
// that way more than one player can create characters if the predefined name files are not provided
|
// that way more than one player can create characters if the predefined name files are not provided
|
||||||
if (predefinedName == "INVALID") {
|
auto assignedPredefinedName = predefinedName;
|
||||||
|
if (assignedPredefinedName == "INVALID") {
|
||||||
std::stringstream nameObjID;
|
std::stringstream nameObjID;
|
||||||
nameObjID << "minifig" << objectID;
|
nameObjID << "minifig" << objectID;
|
||||||
predefinedName = nameObjID.str();
|
assignedPredefinedName = nameObjID.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName;
|
std::string_view nameToAssign = !name.empty() && nameOk ? name : assignedPredefinedName;
|
||||||
std::string pendingName = !name.empty() && !nameOk ? name : "";
|
std::string pendingName = !name.empty() && !nameOk ? name : "";
|
||||||
|
|
||||||
ICharInfo::Info info;
|
ICharInfo::Info info;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "Mail.h"
|
#include "Mail.h"
|
||||||
#include "ZoneInstanceManager.h"
|
#include "ZoneInstanceManager.h"
|
||||||
#include "WorldPackets.h"
|
#include "WorldPackets.h"
|
||||||
|
#include "MessageType/Game.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
|
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
|
||||||
@ -47,6 +48,45 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character, con
|
|||||||
m_CountryCode = 0;
|
m_CountryCode = 0;
|
||||||
m_LastUpdateTimestamp = std::time(nullptr);
|
m_LastUpdateTimestamp = std::time(nullptr);
|
||||||
m_SystemAddress = systemAddress;
|
m_SystemAddress = systemAddress;
|
||||||
|
|
||||||
|
RegisterMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, this, &CharacterComponent::OnRequestServerObjectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||||
|
auto& request = static_cast<GameMessages::RequestServerObjectInfo&>(msg);
|
||||||
|
AMFArrayValue response;
|
||||||
|
|
||||||
|
response.Insert("visible", true);
|
||||||
|
response.Insert("objectID", std::to_string(request.targetForReport));
|
||||||
|
response.Insert("serverInfo", true);
|
||||||
|
|
||||||
|
auto& data = *response.InsertArray("data");
|
||||||
|
auto& cmptType = data.PushDebug("Character");
|
||||||
|
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Component ID") = GeneralUtils::ToUnderlying(ComponentType);
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Character's account ID") = m_Character->GetParentUser()->GetAccountID();
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Last log out time") = m_Character->GetLastLogin();
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Seconds played this session") = 0;
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Editor enabled") = false;
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Total number of seconds played") = m_TotalTimePlayed;
|
||||||
|
cmptType.PushDebug<AMFStringValue>("Total currency") = std::to_string(m_Character->GetCoins());
|
||||||
|
cmptType.PushDebug<AMFStringValue>("Currency able to be picked up") = std::to_string(m_DroppedCoins);
|
||||||
|
cmptType.PushDebug<AMFStringValue>("Tooltip flags value") = "0";
|
||||||
|
// visited locations
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("is a GM") = m_GMLevel > eGameMasterLevel::CIVILIAN;
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Has PVP flag turned on") = m_PvpEnabled;
|
||||||
|
cmptType.PushDebug<AMFIntValue>("GM Level") = GeneralUtils::ToUnderlying(m_GMLevel);
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Editor level") = GeneralUtils::ToUnderlying(m_EditorLevel);
|
||||||
|
cmptType.PushDebug<AMFStringValue>("Guild ID") = "0";
|
||||||
|
cmptType.PushDebug<AMFStringValue>("Guild Name") = "";
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Reputation") = m_Reputation;
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Current Activity Type") = GeneralUtils::ToUnderlying(m_CurrentActivity);
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Property Clone ID") = m_Character->GetPropertyCloneID();
|
||||||
|
|
||||||
|
GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, m_Parent->GetSystemAddress());
|
||||||
|
|
||||||
|
LOG("Handled!");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterComponent::LandingAnimDisabled(int zoneID) {
|
bool CharacterComponent::LandingAnimDisabled(int zoneID) {
|
||||||
@ -55,15 +95,21 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) {
|
|||||||
case 556:
|
case 556:
|
||||||
case 1101:
|
case 1101:
|
||||||
case 1202:
|
case 1202:
|
||||||
|
case 1150:
|
||||||
|
case 1151:
|
||||||
case 1203:
|
case 1203:
|
||||||
case 1204:
|
case 1204:
|
||||||
|
case 1250:
|
||||||
|
case 1251:
|
||||||
case 1261:
|
case 1261:
|
||||||
case 1301:
|
case 1301:
|
||||||
case 1302:
|
case 1302:
|
||||||
case 1303:
|
case 1303:
|
||||||
|
case 1350:
|
||||||
case 1401:
|
case 1401:
|
||||||
case 1402:
|
case 1402:
|
||||||
case 1403:
|
case 1403:
|
||||||
|
case 1450:
|
||||||
case 1603:
|
case 1603:
|
||||||
case 2001:
|
case 2001:
|
||||||
return true;
|
return true;
|
||||||
@ -81,6 +127,8 @@ CharacterComponent::~CharacterComponent() {
|
|||||||
void CharacterComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
void CharacterComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||||
|
|
||||||
if (bIsInitialUpdate) {
|
if (bIsInitialUpdate) {
|
||||||
|
if (!m_Character || !m_Character->GetParentUser()) return;
|
||||||
|
|
||||||
outBitStream.Write(m_ClaimCodes[0] != 0);
|
outBitStream.Write(m_ClaimCodes[0] != 0);
|
||||||
if (m_ClaimCodes[0] != 0) outBitStream.Write(m_ClaimCodes[0]);
|
if (m_ClaimCodes[0] != 0) outBitStream.Write(m_ClaimCodes[0]);
|
||||||
outBitStream.Write(m_ClaimCodes[1] != 0);
|
outBitStream.Write(m_ClaimCodes[1] != 0);
|
||||||
@ -100,7 +148,7 @@ void CharacterComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInit
|
|||||||
outBitStream.Write(m_Character->GetEyebrows());
|
outBitStream.Write(m_Character->GetEyebrows());
|
||||||
outBitStream.Write(m_Character->GetEyes());
|
outBitStream.Write(m_Character->GetEyes());
|
||||||
outBitStream.Write(m_Character->GetMouth());
|
outBitStream.Write(m_Character->GetMouth());
|
||||||
outBitStream.Write<uint64_t>(0); //AccountID, trying out if 0 works.
|
outBitStream.Write<uint64_t>(m_Character->GetParentUser()->GetAccountID());
|
||||||
outBitStream.Write(m_Character->GetLastLogin()); //Last login
|
outBitStream.Write(m_Character->GetLastLogin()); //Last login
|
||||||
outBitStream.Write<uint64_t>(0); //"prop mod last display time"
|
outBitStream.Write<uint64_t>(0); //"prop mod last display time"
|
||||||
outBitStream.Write<uint64_t>(m_Uscore); //u-score
|
outBitStream.Write<uint64_t>(m_Uscore); //u-score
|
||||||
@ -786,7 +834,7 @@ void CharacterComponent::AwardClaimCodes() {
|
|||||||
subject << "%[RewardCodes_" << rewardCode << "_subjectText]";
|
subject << "%[RewardCodes_" << rewardCode << "_subjectText]";
|
||||||
std::ostringstream body;
|
std::ostringstream body;
|
||||||
body << "%[RewardCodes_" << rewardCode << "_bodyText]";
|
body << "%[RewardCodes_" << rewardCode << "_bodyText]";
|
||||||
Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1);
|
Mail::SendMail(m_Parent, subject.str(), body.str(), attachmentLOT, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,6 +323,8 @@ public:
|
|||||||
Character* m_Character;
|
Character* m_Character;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool OnRequestServerObjectInfo(GameMessages::GameMsg& msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The map of active venture vision effects
|
* The map of active venture vision effects
|
||||||
*/
|
*/
|
||||||
|
@ -8,6 +8,10 @@ namespace RakNet {
|
|||||||
class BitStream;
|
class BitStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameMessages {
|
||||||
|
struct GameMsg;
|
||||||
|
}
|
||||||
|
|
||||||
class Entity;
|
class Entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,6 +56,10 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
void RegisterMsg(const MessageType::Game msgId, auto* self, const auto handler) {
|
||||||
|
m_Parent->RegisterMsg(msgId, std::bind(handler, self, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The entity that owns this component
|
* The entity that owns this component
|
||||||
*/
|
*/
|
||||||
|
@ -150,11 +150,11 @@ uint32_t InventoryComponent::GetLotCount(const LOT lot) const {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InventoryComponent::GetLotCountNonTransfer(LOT lot) const {
|
uint32_t InventoryComponent::GetLotCountNonTransfer(LOT lot, bool includeVault) const {
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
||||||
for (const auto& inventory : m_Inventories) {
|
for (const auto& inventory : m_Inventories) {
|
||||||
if (IsTransferInventory(inventory.second->GetType())) continue;
|
if (IsTransferInventory(inventory.second->GetType(), includeVault)) continue;
|
||||||
|
|
||||||
count += inventory.second->GetLotCount(lot);
|
count += inventory.second->GetLotCount(lot);
|
||||||
}
|
}
|
||||||
@ -274,7 +274,7 @@ void InventoryComponent::AddItem(
|
|||||||
|
|
||||||
switch (sourceType) {
|
switch (sourceType) {
|
||||||
case 0:
|
case 0:
|
||||||
Mail::SendMail(LWOOBJID_EMPTY, "Darkflame Universe", m_Parent, "Lost Reward", "You received an item and didn't have room for it.", lot, size);
|
Mail::SendMail(m_Parent, "%[MAIL_ACTIVITY_OVERFLOW_HEADER]", "%[MAIL_ACTIVITY_OVERFLOW_BODY]", lot, size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
@ -305,21 +305,35 @@ bool InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInvent
|
|||||||
LOG("Attempted to remove 0 of item (%i) from the inventory!", lot);
|
LOG("Attempted to remove 0 of item (%i) from the inventory!", lot);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot);
|
if (inventoryType != eInventoryType::ALL) {
|
||||||
auto* inventory = GetInventory(inventoryType);
|
if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot);
|
||||||
if (!inventory) return false;
|
auto* inventory = GetInventory(inventoryType);
|
||||||
|
if (!inventory) return false;
|
||||||
|
|
||||||
auto left = std::min<uint32_t>(count, inventory->GetLotCount(lot));
|
auto left = std::min<uint32_t>(count, inventory->GetLotCount(lot));
|
||||||
if (left != count) return false;
|
if (left != count) return false;
|
||||||
|
|
||||||
while (left > 0) {
|
while (left > 0) {
|
||||||
auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound);
|
auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound);
|
||||||
if (!item) break;
|
if (!item) break;
|
||||||
const auto delta = std::min<uint32_t>(left, item->GetCount());
|
const auto delta = std::min<uint32_t>(left, item->GetCount());
|
||||||
item->SetCount(item->GetCount() - delta, silent);
|
item->SetCount(item->GetCount() - delta, silent);
|
||||||
left -= delta;
|
left -= delta;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
auto left = count;
|
||||||
|
for (const auto& inventory : m_Inventories | std::views::values) {
|
||||||
|
while (left > 0 && inventory->GetLotCount(lot) > 0) {
|
||||||
|
auto* item = inventory->FindItemByLot(lot, false, ignoreBound);
|
||||||
|
if (!item) break;
|
||||||
|
const auto delta = std::min<uint32_t>(item->GetCount(), left);
|
||||||
|
item->SetCount(item->GetCount() - delta, silent);
|
||||||
|
left -= delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left == 0;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType inventory, const uint32_t count, const bool showFlyingLot, bool isModMoveAndEquip, const bool ignoreEquipped, const int32_t preferredSlot) {
|
void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType inventory, const uint32_t count, const bool showFlyingLot, bool isModMoveAndEquip, const bool ignoreEquipped, const int32_t preferredSlot) {
|
||||||
@ -1318,8 +1332,8 @@ BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InventoryComponent::IsTransferInventory(eInventoryType type) {
|
bool InventoryComponent::IsTransferInventory(eInventoryType type, bool includeVault) {
|
||||||
return type == VENDOR_BUYBACK || type == VAULT_ITEMS || type == VAULT_MODELS || type == TEMP_ITEMS || type == TEMP_MODELS || type == MODELS_IN_BBB;
|
return type == VENDOR_BUYBACK || (includeVault && (type == VAULT_ITEMS || type == VAULT_MODELS)) || type == TEMP_ITEMS || type == TEMP_MODELS || type == MODELS_IN_BBB;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InventoryComponent::FindSkill(const LOT lot) {
|
uint32_t InventoryComponent::FindSkill(const LOT lot) {
|
||||||
|
@ -100,7 +100,7 @@ public:
|
|||||||
* @param lot the lot to search for
|
* @param lot the lot to search for
|
||||||
* @return the amount of items this entity possesses of the specified lot
|
* @return the amount of items this entity possesses of the specified lot
|
||||||
*/
|
*/
|
||||||
uint32_t GetLotCountNonTransfer(LOT lot) const;
|
uint32_t GetLotCountNonTransfer(LOT lot, bool includeVault = true) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the items that are currently equipped by this entity
|
* Returns the items that are currently equipped by this entity
|
||||||
@ -373,7 +373,7 @@ public:
|
|||||||
* @param type the inventory type to check
|
* @param type the inventory type to check
|
||||||
* @return if the inventory type is a temp inventory
|
* @return if the inventory type is a temp inventory
|
||||||
*/
|
*/
|
||||||
static bool IsTransferInventory(eInventoryType type);
|
static bool IsTransferInventory(eInventoryType type, bool includeVault = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the skill related to the passed LOT from the ObjectSkills table
|
* Finds the skill related to the passed LOT from the ObjectSkills table
|
||||||
|
@ -99,17 +99,8 @@ void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipCh
|
|||||||
mission->Accept();
|
mission->Accept();
|
||||||
|
|
||||||
this->m_Missions.insert_or_assign(missionId, mission);
|
this->m_Missions.insert_or_assign(missionId, mission);
|
||||||
|
|
||||||
if (missionId == 1728) {
|
|
||||||
//Needs to send a mail
|
|
||||||
|
|
||||||
auto address = m_Parent->GetSystemAddress();
|
|
||||||
|
|
||||||
Mail::HandleNotificationRequest(address, m_Parent->GetObjectID());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MissionComponent::CompleteMission(const uint32_t missionId, const bool skipChecks, const bool yieldRewards) {
|
void MissionComponent::CompleteMission(const uint32_t missionId, const bool skipChecks, const bool yieldRewards) {
|
||||||
// Get the mission first
|
// Get the mission first
|
||||||
auto* mission = this->GetMission(missionId);
|
auto* mission = this->GetMission(missionId);
|
||||||
@ -521,7 +512,7 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
|
|||||||
|
|
||||||
auto* mission = new Mission(this, missionId);
|
auto* mission = new Mission(this, missionId);
|
||||||
|
|
||||||
mission->LoadFromXml(*doneM);
|
mission->LoadFromXmlDone(*doneM);
|
||||||
|
|
||||||
doneM = doneM->NextSiblingElement();
|
doneM = doneM->NextSiblingElement();
|
||||||
|
|
||||||
@ -536,9 +527,9 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
|
|||||||
|
|
||||||
currentM->QueryAttribute("id", &missionId);
|
currentM->QueryAttribute("id", &missionId);
|
||||||
|
|
||||||
auto* mission = new Mission(this, missionId);
|
auto* mission = m_Missions.contains(missionId) ? m_Missions[missionId] : new Mission(this, missionId);
|
||||||
|
|
||||||
mission->LoadFromXml(*currentM);
|
mission->LoadFromXmlCur(*currentM);
|
||||||
|
|
||||||
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
|
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
|
||||||
mission->SetUniqueMissionOrderID(missionOrder);
|
mission->SetUniqueMissionOrderID(missionOrder);
|
||||||
@ -574,20 +565,23 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
|
|||||||
auto* mission = pair.second;
|
auto* mission = pair.second;
|
||||||
|
|
||||||
if (mission) {
|
if (mission) {
|
||||||
const auto complete = mission->IsComplete();
|
const auto completions = mission->GetCompletions();
|
||||||
|
|
||||||
auto* m = doc.NewElement("m");
|
auto* m = doc.NewElement("m");
|
||||||
|
|
||||||
if (complete) {
|
if (completions > 0) {
|
||||||
mission->UpdateXml(*m);
|
mission->UpdateXmlDone(*m);
|
||||||
|
|
||||||
done->LinkEndChild(m);
|
done->LinkEndChild(m);
|
||||||
|
|
||||||
continue;
|
if (mission->IsComplete()) continue;
|
||||||
|
|
||||||
|
m = doc.NewElement("m");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
|
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
|
||||||
|
|
||||||
mission->UpdateXml(*m);
|
mission->UpdateXmlCur(*m);
|
||||||
|
|
||||||
cur->LinkEndChild(m);
|
cur->LinkEndChild(m);
|
||||||
}
|
}
|
||||||
|
@ -84,11 +84,12 @@ dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
|
|||||||
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
|
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
|
||||||
toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
|
||||||
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
|
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
|
||||||
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume1_CaveExit.hkx") {
|
// Leaving these out for now since they cause more issues than they solve in racing tracks without proper OBB checks.
|
||||||
|
} /* else if (info->physicsAsset == "env\\GFTrack_DeathVolume1_CaveExit.hkx") {
|
||||||
toReturn = new dpEntity(m_Parent->GetObjectID(), 112.416870f, 50.363434f, 87.679268f);
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 112.416870f, 50.363434f, 87.679268f);
|
||||||
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume2_RoadGaps.hkx") {
|
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume2_RoadGaps.hkx") {
|
||||||
toReturn = new dpEntity(m_Parent->GetObjectID(), 48.386536f, 50.363434f, 259.361755f);
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 48.386536f, 50.363434f, 259.361755f);
|
||||||
} else {
|
} */ else {
|
||||||
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
|
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
|
||||||
|
|
||||||
//add fallback cube:
|
//add fallback cube:
|
||||||
|
@ -165,7 +165,9 @@ void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::s
|
|||||||
info.id = propertyId;
|
info.id = propertyId;
|
||||||
info.name = propertyName;
|
info.name = propertyName;
|
||||||
info.description = propertyDescription;
|
info.description = propertyDescription;
|
||||||
|
info.lastUpdatedTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
Database::Get()->UpdateLastSave(info);
|
||||||
Database::Get()->UpdatePropertyDetails(info);
|
Database::Get()->UpdatePropertyDetails(info);
|
||||||
|
|
||||||
OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS);
|
OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
@ -688,6 +690,10 @@ void PropertyManagementComponent::Save() {
|
|||||||
|
|
||||||
Database::Get()->RemoveModel(model.id);
|
Database::Get()->RemoveModel(model.id);
|
||||||
}
|
}
|
||||||
|
IProperty::Info info;
|
||||||
|
info.id = propertyId;
|
||||||
|
info.lastUpdatedTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
Database::Get()->UpdateLastSave(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyManagementComponent::AddModel(LWOOBJID modelId, LWOOBJID spawnerId) {
|
void PropertyManagementComponent::AddModel(LWOOBJID modelId, LWOOBJID spawnerId) {
|
||||||
|
@ -65,14 +65,7 @@ void QuickBuildComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIni
|
|||||||
|
|
||||||
outBitStream.Write(false);
|
outBitStream.Write(false);
|
||||||
}
|
}
|
||||||
// If build state is completed and we've already serialized once in the completed state,
|
|
||||||
// don't serializing this component anymore as this will cause the build to jump again.
|
|
||||||
// If state changes, serialization will begin again.
|
|
||||||
if (!m_StateDirty && m_State == eQuickBuildState::COMPLETED) {
|
|
||||||
outBitStream.Write0();
|
|
||||||
outBitStream.Write0();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// BEGIN Scripted Activity
|
// BEGIN Scripted Activity
|
||||||
outBitStream.Write1();
|
outBitStream.Write1();
|
||||||
|
|
||||||
@ -90,36 +83,27 @@ void QuickBuildComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIni
|
|||||||
}
|
}
|
||||||
// END Scripted Activity
|
// END Scripted Activity
|
||||||
|
|
||||||
outBitStream.Write1();
|
outBitStream.Write(m_StateDirty || bIsInitialUpdate);
|
||||||
|
if (m_StateDirty || bIsInitialUpdate) {
|
||||||
|
outBitStream.Write(m_State);
|
||||||
|
|
||||||
outBitStream.Write(m_State);
|
outBitStream.Write(m_ShowResetEffect);
|
||||||
|
outBitStream.Write(m_Activator != nullptr);
|
||||||
|
|
||||||
outBitStream.Write(m_ShowResetEffect);
|
outBitStream.Write(m_Timer);
|
||||||
outBitStream.Write(m_Activator != nullptr);
|
outBitStream.Write(m_TimerIncomplete);
|
||||||
|
|
||||||
outBitStream.Write(m_Timer);
|
if (bIsInitialUpdate) {
|
||||||
outBitStream.Write(m_TimerIncomplete);
|
outBitStream.Write(false); // IsChoiceBuild
|
||||||
|
outBitStream.Write(m_ActivatorPosition);
|
||||||
if (bIsInitialUpdate) {
|
outBitStream.Write(m_RepositionPlayer);
|
||||||
outBitStream.Write(false);
|
}
|
||||||
outBitStream.Write(m_ActivatorPosition);
|
m_StateDirty = false;
|
||||||
outBitStream.Write(m_RepositionPlayer);
|
|
||||||
}
|
}
|
||||||
m_StateDirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickBuildComponent::Update(float deltaTime) {
|
void QuickBuildComponent::Update(float deltaTime) {
|
||||||
m_Activator = GetActivator();
|
SetActivator(GetActivator());
|
||||||
|
|
||||||
// Serialize the quickbuild every so often, fixes the odd bug where the quickbuild is not buildable
|
|
||||||
/*if (m_SoftTimer > 0.0f) {
|
|
||||||
m_SoftTimer -= deltaTime;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_SoftTimer = 5.0f;
|
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
switch (m_State) {
|
switch (m_State) {
|
||||||
case eQuickBuildState::OPEN: {
|
case eQuickBuildState::OPEN: {
|
||||||
@ -130,12 +114,12 @@ void QuickBuildComponent::Update(float deltaTime) {
|
|||||||
const bool isSmashGroup = spawner != nullptr ? spawner->GetIsSpawnSmashGroup() : false;
|
const bool isSmashGroup = spawner != nullptr ? spawner->GetIsSpawnSmashGroup() : false;
|
||||||
|
|
||||||
if (isSmashGroup) {
|
if (isSmashGroup) {
|
||||||
m_TimerIncomplete += deltaTime;
|
ModifyIncompleteTimer(deltaTime);
|
||||||
|
|
||||||
// For reset times < 0 this has to be handled manually
|
// For reset times < 0 this has to be handled manually
|
||||||
if (m_TimeBeforeSmash > 0) {
|
if (m_TimeBeforeSmash > 0) {
|
||||||
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f) {
|
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f && !m_ShowResetEffect) {
|
||||||
m_ShowResetEffect = true;
|
SetShowResetEffect(true);
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
}
|
}
|
||||||
@ -153,21 +137,19 @@ void QuickBuildComponent::Update(float deltaTime) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eQuickBuildState::COMPLETED: {
|
case eQuickBuildState::COMPLETED: {
|
||||||
m_Timer += deltaTime;
|
ModifyTimer(deltaTime);
|
||||||
|
|
||||||
// For reset times < 0 this has to be handled manually
|
// For reset times < 0 this has to be handled manually
|
||||||
if (m_ResetTime > 0) {
|
if (m_ResetTime > 0) {
|
||||||
if (m_Timer >= m_ResetTime - 4.0f) {
|
if (m_Timer >= m_ResetTime - 4.0f && !m_ShowResetEffect) {
|
||||||
if (!m_ShowResetEffect) {
|
SetShowResetEffect(true);
|
||||||
m_ShowResetEffect = true;
|
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Timer >= m_ResetTime) {
|
if (m_Timer >= m_ResetTime) {
|
||||||
|
|
||||||
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true);
|
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 7.0f, false, true);
|
||||||
|
|
||||||
ResetQuickBuild(false);
|
ResetQuickBuild(false);
|
||||||
}
|
}
|
||||||
@ -185,9 +167,9 @@ void QuickBuildComponent::Update(float deltaTime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_TimeBeforeDrain -= deltaTime;
|
m_TimeBeforeDrain -= deltaTime;
|
||||||
m_Timer += deltaTime;
|
ModifyTimer(deltaTime);
|
||||||
m_TimerIncomplete = 0;
|
SetIncompleteTimer(0.0f);
|
||||||
m_ShowResetEffect = false;
|
SetShowResetEffect(false);
|
||||||
|
|
||||||
if (m_TimeBeforeDrain <= 0.0f) {
|
if (m_TimeBeforeDrain <= 0.0f) {
|
||||||
m_TimeBeforeDrain = m_CompleteTime / static_cast<float>(m_TakeImagination);
|
m_TimeBeforeDrain = m_CompleteTime / static_cast<float>(m_TakeImagination);
|
||||||
@ -215,12 +197,12 @@ void QuickBuildComponent::Update(float deltaTime) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eQuickBuildState::INCOMPLETE: {
|
case eQuickBuildState::INCOMPLETE: {
|
||||||
m_TimerIncomplete += deltaTime;
|
ModifyIncompleteTimer(deltaTime);
|
||||||
|
|
||||||
// For reset times < 0 this has to be handled manually
|
// For reset times < 0 this has to be handled manually
|
||||||
if (m_TimeBeforeSmash > 0) {
|
if (m_TimeBeforeSmash > 0) {
|
||||||
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f) {
|
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f && !m_ShowResetEffect) {
|
||||||
m_ShowResetEffect = true;
|
SetShowResetEffect(true);
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
}
|
}
|
||||||
@ -260,7 +242,7 @@ void QuickBuildComponent::SpawnActivator() {
|
|||||||
info.spawnerID = m_Parent->GetObjectID();
|
info.spawnerID = m_Parent->GetObjectID();
|
||||||
info.pos = m_ActivatorPosition == NiPoint3Constant::ZERO ? m_Parent->GetPosition() : m_ActivatorPosition;
|
info.pos = m_ActivatorPosition == NiPoint3Constant::ZERO ? m_Parent->GetPosition() : m_ActivatorPosition;
|
||||||
|
|
||||||
m_Activator = Game::entityManager->CreateEntity(info, nullptr, m_Parent);
|
SetActivator(Game::entityManager->CreateEntity(info, nullptr, m_Parent));
|
||||||
if (m_Activator) {
|
if (m_Activator) {
|
||||||
m_ActivatorId = m_Activator->GetObjectID();
|
m_ActivatorId = m_Activator->GetObjectID();
|
||||||
Game::entityManager->ConstructEntity(m_Activator);
|
Game::entityManager->ConstructEntity(m_Activator);
|
||||||
@ -277,7 +259,7 @@ void QuickBuildComponent::DespawnActivator() {
|
|||||||
|
|
||||||
m_Activator->ScheduleKillAfterUpdate();
|
m_Activator->ScheduleKillAfterUpdate();
|
||||||
|
|
||||||
m_Activator = nullptr;
|
SetActivator(nullptr);
|
||||||
|
|
||||||
m_ActivatorId = LWOOBJID_EMPTY;
|
m_ActivatorId = LWOOBJID_EMPTY;
|
||||||
}
|
}
|
||||||
@ -405,8 +387,7 @@ void QuickBuildComponent::StartQuickBuild(Entity* const user) {
|
|||||||
GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::BUILDING, user->GetObjectID());
|
GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::BUILDING, user->GetObjectID());
|
||||||
GameMessages::SendEnableQuickBuild(m_Parent, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID());
|
GameMessages::SendEnableQuickBuild(m_Parent, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID());
|
||||||
|
|
||||||
m_State = eQuickBuildState::BUILDING;
|
SetState(eQuickBuildState::BUILDING);
|
||||||
m_StateDirty = true;
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
|
|
||||||
auto* movingPlatform = m_Parent->GetComponent<MovingPlatformComponent>();
|
auto* movingPlatform = m_Parent->GetComponent<MovingPlatformComponent>();
|
||||||
@ -444,9 +425,8 @@ void QuickBuildComponent::CompleteQuickBuild(Entity* const user) {
|
|||||||
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||||
|
|
||||||
|
|
||||||
m_State = eQuickBuildState::COMPLETED;
|
SetState(eQuickBuildState::COMPLETED);
|
||||||
m_StateDirty = true;
|
SetTimer(0.0f);
|
||||||
m_Timer = 0.0f;
|
|
||||||
m_DrainedImagination = 0;
|
m_DrainedImagination = 0;
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
@ -526,11 +506,10 @@ void QuickBuildComponent::ResetQuickBuild(const bool failed) {
|
|||||||
|
|
||||||
GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::RESETTING, LWOOBJID_EMPTY);
|
GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::RESETTING, LWOOBJID_EMPTY);
|
||||||
|
|
||||||
m_State = eQuickBuildState::RESETTING;
|
SetState(eQuickBuildState::RESETTING);
|
||||||
m_StateDirty = true;
|
SetTimer(0.0f);
|
||||||
m_Timer = 0.0f;
|
SetIncompleteTimer(0.0f);
|
||||||
m_TimerIncomplete = 0.0f;
|
SetShowResetEffect(false);
|
||||||
m_ShowResetEffect = false;
|
|
||||||
m_DrainedImagination = 0;
|
m_DrainedImagination = 0;
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
@ -563,8 +542,7 @@ void QuickBuildComponent::CancelQuickBuild(Entity* const entity, const eQuickBui
|
|||||||
GameMessages::SendTerminateInteraction(m_Parent->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
GameMessages::SendTerminateInteraction(m_Parent->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||||
|
|
||||||
// Now update the component itself
|
// Now update the component itself
|
||||||
m_State = eQuickBuildState::INCOMPLETE;
|
SetState(eQuickBuildState::INCOMPLETE);
|
||||||
m_StateDirty = true;
|
|
||||||
|
|
||||||
// Notify scripts and possible subscribers
|
// Notify scripts and possible subscribers
|
||||||
m_Parent->GetScript()->OnQuickBuildNotifyState(m_Parent, m_State);
|
m_Parent->GetScript()->OnQuickBuildNotifyState(m_Parent, m_State);
|
||||||
|
@ -218,6 +218,48 @@ public:
|
|||||||
* @param skipChecks whether or not to skip the check for the quickbuild not being completed
|
* @param skipChecks whether or not to skip the check for the quickbuild not being completed
|
||||||
*/
|
*/
|
||||||
void CancelQuickBuild(Entity* const builder, const eQuickBuildFailReason failReason, const bool skipChecks = false);
|
void CancelQuickBuild(Entity* const builder, const eQuickBuildFailReason failReason, const bool skipChecks = false);
|
||||||
|
|
||||||
|
void SetState(const eQuickBuildState state) {
|
||||||
|
if (m_State == state) return;
|
||||||
|
m_State = state;
|
||||||
|
m_StateDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetShowResetEffect(const bool value) {
|
||||||
|
if (m_ShowResetEffect == value) return;
|
||||||
|
m_ShowResetEffect = value;
|
||||||
|
m_StateDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetActivator(Entity* const activator) {
|
||||||
|
if (m_Activator == activator) return;
|
||||||
|
m_Activator = activator;
|
||||||
|
m_StateDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimer(const float value) {
|
||||||
|
if (m_Timer == value) return;
|
||||||
|
m_Timer = value;
|
||||||
|
m_StateDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModifyTimer(const float value) {
|
||||||
|
if (value == 0.0f) return;
|
||||||
|
m_Timer += value;
|
||||||
|
m_StateDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIncompleteTimer(const float value) {
|
||||||
|
if (m_TimerIncomplete == value) return;
|
||||||
|
m_TimerIncomplete = value;
|
||||||
|
m_StateDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModifyIncompleteTimer(const float value) {
|
||||||
|
if (value == 0.0f) return;
|
||||||
|
m_TimerIncomplete += value;
|
||||||
|
m_StateDirty = true;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Whether or not the quickbuild state has been changed since we last serialized it.
|
* Whether or not the quickbuild state has been changed since we last serialized it.
|
||||||
|
@ -37,8 +37,19 @@
|
|||||||
#include "ePlayerFlag.h"
|
#include "ePlayerFlag.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
#include "GhostComponent.h"
|
#include "GhostComponent.h"
|
||||||
|
#include "eGameMasterLevel.h"
|
||||||
#include "StringifiedEnum.h"
|
#include "StringifiedEnum.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
using enum MessageType::Game;
|
||||||
|
using namespace GameMessages;
|
||||||
|
using MessageCreator = std::function<std::unique_ptr<GameMessages::GameMsg>()>;
|
||||||
|
std::map<MessageType::Game, MessageCreator> g_MessageHandlers = {
|
||||||
|
{ REQUEST_SERVER_OBJECT_INFO, []() { return std::make_unique<RequestServerObjectInfo>(); } },
|
||||||
|
{ SHOOTING_GALLERY_FIRE, []() { return std::make_unique<ShootingGalleryFire>(); } },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const SystemAddress& sysAddr, LWOOBJID objectID, MessageType::Game messageID) {
|
void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const SystemAddress& sysAddr, LWOOBJID objectID, MessageType::Game messageID) {
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
@ -55,6 +66,24 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
|
|
||||||
if (messageID != MessageType::Game::READY_FOR_UPDATES) LOG_DEBUG("Received GM with ID and name: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
|
if (messageID != MessageType::Game::READY_FOR_UPDATES) LOG_DEBUG("Received GM with ID and name: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
|
||||||
|
|
||||||
|
auto handler = g_MessageHandlers.find(messageID);
|
||||||
|
if (handler != g_MessageHandlers.end()) {
|
||||||
|
auto msg = handler->second();
|
||||||
|
|
||||||
|
// Verify that the system address user is able to use this message.
|
||||||
|
if (msg->requiredGmLevel > eGameMasterLevel::CIVILIAN) {
|
||||||
|
auto* usingEntity = Game::entityManager->GetEntity(usr->GetLoggedInChar());
|
||||||
|
if (!usingEntity || usingEntity->GetGMLevel() < msg->requiredGmLevel) {
|
||||||
|
LOG("User %s (%llu) does not have the required GM level to execute this command.", usingEntity->GetCharacter()->GetName().c_str(), usingEntity->GetObjectID());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->Deserialize(inStream);
|
||||||
|
msg->Handle(*entity, sysAddr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (messageID) {
|
switch (messageID) {
|
||||||
|
|
||||||
case MessageType::Game::UN_USE_BBB_MODEL: {
|
case MessageType::Game::UN_USE_BBB_MODEL: {
|
||||||
@ -104,19 +133,20 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently not actually used for our implementation, however its used right now to get around invisible inventory items in the client.
|
// Currently not actually used for our implementation, however its used right now to get around invisible inventory items in the client.
|
||||||
case MessageType::Game::SELECT_SKILL: {
|
case MessageType::Game::SELECT_SKILL: {
|
||||||
auto var = entity->GetVar<bool>(u"dlu_first_time_load");
|
auto var = entity->GetVar<bool>(u"dlu_first_time_load");
|
||||||
if (var) {
|
if (var) {
|
||||||
entity->SetVar<bool>(u"dlu_first_time_load", false);
|
entity->SetVar<bool>(u"dlu_first_time_load", false);
|
||||||
InventoryComponent* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
InventoryComponent* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||||
|
|
||||||
if (inventoryComponent) inventoryComponent->FixInvisibleItems();
|
if (inventoryComponent) inventoryComponent->FixInvisibleItems();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Game::PLAYER_LOADED: {
|
case MessageType::Game::PLAYER_LOADED: {
|
||||||
|
GameMessages::SendPlayerReady(entity, sysAddr);
|
||||||
entity->SetPlayerReadyForUpdates();
|
entity->SetPlayerReadyForUpdates();
|
||||||
|
|
||||||
auto* ghostComponent = entity->GetComponent<GhostComponent>();
|
auto* ghostComponent = entity->GetComponent<GhostComponent>();
|
||||||
@ -183,7 +213,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
LOG("Player %s (%llu) loaded.", entity->GetCharacter()->GetName().c_str(), entity->GetObjectID());
|
LOG("Player %s (%llu) loaded.", entity->GetCharacter()->GetName().c_str(), entity->GetObjectID());
|
||||||
|
|
||||||
// After we've done our thing, tell the client they're ready
|
// After we've done our thing, tell the client they're ready
|
||||||
GameMessages::SendPlayerReady(entity, sysAddr);
|
|
||||||
GameMessages::SendPlayerReady(Game::zoneManager->GetZoneControlObject(), sysAddr);
|
GameMessages::SendPlayerReady(Game::zoneManager->GetZoneControlObject(), sysAddr);
|
||||||
|
|
||||||
if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1"
|
if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1"
|
||||||
@ -704,12 +733,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
case MessageType::Game::UPDATE_INVENTORY_GROUP_CONTENTS:
|
case MessageType::Game::UPDATE_INVENTORY_GROUP_CONTENTS:
|
||||||
GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr);
|
GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr);
|
||||||
break;
|
break;
|
||||||
case MessageType::Game::SHOOTING_GALLERY_FIRE: {
|
|
||||||
GameMessages::ShootingGalleryFire fire{};
|
|
||||||
fire.Deserialize(inStream);
|
|
||||||
fire.Handle(*entity, sysAddr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
|
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
|
||||||
|
@ -48,8 +48,6 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include "RakString.h"
|
#include "RakString.h"
|
||||||
|
|
||||||
#include "httplib.h" //sorry not sorry.
|
|
||||||
|
|
||||||
//CDB includes:
|
//CDB includes:
|
||||||
#include "CDClientManager.h"
|
#include "CDClientManager.h"
|
||||||
#include "CDEmoteTable.h"
|
#include "CDEmoteTable.h"
|
||||||
@ -845,8 +843,10 @@ void GameMessages::SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, c
|
|||||||
|
|
||||||
bitStream.Write(entity->GetObjectID());
|
bitStream.Write(entity->GetObjectID());
|
||||||
bitStream.Write(MessageType::Game::DIE);
|
bitStream.Write(MessageType::Game::DIE);
|
||||||
|
|
||||||
bitStream.Write(bClientDeath);
|
bitStream.Write(bClientDeath);
|
||||||
bitStream.Write(bSpawnLoot);
|
bitStream.Write(bSpawnLoot);
|
||||||
|
bitStream.Write<uint32_t>(deathType.size());
|
||||||
bitStream.Write(deathType);
|
bitStream.Write(deathType);
|
||||||
bitStream.Write(directionRelative_AngleXZ);
|
bitStream.Write(directionRelative_AngleXZ);
|
||||||
bitStream.Write(directionRelative_AngleY);
|
bitStream.Write(directionRelative_AngleY);
|
||||||
@ -856,7 +856,10 @@ void GameMessages::SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, c
|
|||||||
if (killType != eKillType::VIOLENT) bitStream.Write(killType);
|
if (killType != eKillType::VIOLENT) bitStream.Write(killType);
|
||||||
|
|
||||||
bitStream.Write(killerID);
|
bitStream.Write(killerID);
|
||||||
bitStream.Write(lootOwnerID);
|
bitStream.Write(lootOwnerID != LWOOBJID_EMPTY);
|
||||||
|
if (lootOwnerID != LWOOBJID_EMPTY) {
|
||||||
|
bitStream.Write(lootOwnerID);
|
||||||
|
}
|
||||||
|
|
||||||
SEND_PACKET_BROADCAST;
|
SEND_PACKET_BROADCAST;
|
||||||
}
|
}
|
||||||
@ -968,6 +971,8 @@ void GameMessages::SendResurrect(Entity* entity) {
|
|||||||
// and just make sure the client has time to be ready.
|
// and just make sure the client has time to be ready.
|
||||||
constexpr float respawnTime = 3.66700005531311f + 0.5f;
|
constexpr float respawnTime = 3.66700005531311f + 0.5f;
|
||||||
entity->AddCallbackTimer(respawnTime, [=]() {
|
entity->AddCallbackTimer(respawnTime, [=]() {
|
||||||
|
GameMessages::PlayerResurrectionFinished msg;
|
||||||
|
entity->NotifyPlayerResurrectionFinished(msg);
|
||||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
if (destroyableComponent != nullptr && entity->GetLOT() == 1) {
|
if (destroyableComponent != nullptr && entity->GetLOT() == 1) {
|
||||||
@ -6427,4 +6432,16 @@ namespace GameMessages {
|
|||||||
void ShootingGalleryFire::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
void ShootingGalleryFire::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
||||||
entity.OnShootingGalleryFire(*this);
|
entity.OnShootingGalleryFire(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RequestServerObjectInfo::Deserialize(RakNet::BitStream& bitStream) {
|
||||||
|
if (!bitStream.Read(bVerbose)) return false;
|
||||||
|
if (!bitStream.Read(clientId)) return false;
|
||||||
|
if (!bitStream.Read(targetForReport)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestServerObjectInfo::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
||||||
|
auto* handlingEntity = Game::entityManager->GetEntity(targetForReport);
|
||||||
|
if (handlingEntity) handlingEntity->HandleMsg(*this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "eLootSourceType.h"
|
#include "eLootSourceType.h"
|
||||||
#include "Brick.h"
|
#include "Brick.h"
|
||||||
#include "MessageType/Game.h"
|
#include "MessageType/Game.h"
|
||||||
|
#include "eGameMasterLevel.h"
|
||||||
|
|
||||||
class AMFBaseValue;
|
class AMFBaseValue;
|
||||||
class Entity;
|
class Entity;
|
||||||
@ -50,7 +51,8 @@ enum class eCameraTargetCyclingMode : int32_t {
|
|||||||
|
|
||||||
namespace GameMessages {
|
namespace GameMessages {
|
||||||
struct GameMsg {
|
struct GameMsg {
|
||||||
GameMsg(MessageType::Game gmId) : msgId{ gmId } {}
|
GameMsg(MessageType::Game gmId, eGameMasterLevel lvl) : msgId{ gmId }, requiredGmLevel{ lvl } {}
|
||||||
|
GameMsg(MessageType::Game gmId) : GameMsg(gmId, eGameMasterLevel::CIVILIAN) {}
|
||||||
virtual ~GameMsg() = default;
|
virtual ~GameMsg() = default;
|
||||||
void Send(const SystemAddress& sysAddr) const;
|
void Send(const SystemAddress& sysAddr) const;
|
||||||
virtual void Serialize(RakNet::BitStream& bitStream) const {}
|
virtual void Serialize(RakNet::BitStream& bitStream) const {}
|
||||||
@ -58,6 +60,7 @@ namespace GameMessages {
|
|||||||
virtual void Handle(Entity& entity, const SystemAddress& sysAddr) {};
|
virtual void Handle(Entity& entity, const SystemAddress& sysAddr) {};
|
||||||
MessageType::Game msgId;
|
MessageType::Game msgId;
|
||||||
LWOOBJID target{ LWOOBJID_EMPTY };
|
LWOOBJID target{ LWOOBJID_EMPTY };
|
||||||
|
eGameMasterLevel requiredGmLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PropertyDataMessage;
|
class PropertyDataMessage;
|
||||||
@ -765,6 +768,20 @@ namespace GameMessages {
|
|||||||
LOT templateID{};
|
LOT templateID{};
|
||||||
LWOOBJID childID{};
|
LWOOBJID childID{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PlayerResurrectionFinished : public GameMsg {
|
||||||
|
PlayerResurrectionFinished() : GameMsg(MessageType::Game::PLAYER_RESURRECTION_FINISHED) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RequestServerObjectInfo : public GameMsg {
|
||||||
|
bool bVerbose{};
|
||||||
|
LWOOBJID clientId{};
|
||||||
|
LWOOBJID targetForReport{};
|
||||||
|
|
||||||
|
RequestServerObjectInfo() : GameMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, eGameMasterLevel::DEVELOPER) {}
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
||||||
|
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GAMEMESSAGES_H
|
#endif // GAMEMESSAGES_H
|
||||||
|
@ -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;
|
||||||
@ -65,7 +67,7 @@ Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mission::LoadFromXml(const tinyxml2::XMLElement& element) {
|
void Mission::LoadFromXmlDone(const tinyxml2::XMLElement& element) {
|
||||||
// Start custom XML
|
// Start custom XML
|
||||||
if (element.Attribute("state") != nullptr) {
|
if (element.Attribute("state") != nullptr) {
|
||||||
m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
|
m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
|
||||||
@ -76,11 +78,15 @@ void Mission::LoadFromXml(const tinyxml2::XMLElement& element) {
|
|||||||
m_Completions = std::stoul(element.Attribute("cct"));
|
m_Completions = std::stoul(element.Attribute("cct"));
|
||||||
|
|
||||||
m_Timestamp = std::stoul(element.Attribute("cts"));
|
m_Timestamp = std::stoul(element.Attribute("cts"));
|
||||||
|
|
||||||
if (IsComplete()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
||||||
|
// Start custom XML
|
||||||
|
if (element.Attribute("state") != nullptr) {
|
||||||
|
m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
|
||||||
|
}
|
||||||
|
// End custom XML
|
||||||
|
|
||||||
auto* task = element.FirstChildElement();
|
auto* task = element.FirstChildElement();
|
||||||
|
|
||||||
@ -132,7 +138,7 @@ void Mission::LoadFromXml(const tinyxml2::XMLElement& element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mission::UpdateXml(tinyxml2::XMLElement& element) {
|
void Mission::UpdateXmlDone(tinyxml2::XMLElement& element) {
|
||||||
// Start custom XML
|
// Start custom XML
|
||||||
element.SetAttribute("state", static_cast<unsigned int>(m_State));
|
element.SetAttribute("state", static_cast<unsigned int>(m_State));
|
||||||
// End custom XML
|
// End custom XML
|
||||||
@ -141,15 +147,21 @@ void Mission::UpdateXml(tinyxml2::XMLElement& element) {
|
|||||||
|
|
||||||
element.SetAttribute("id", static_cast<unsigned int>(info.id));
|
element.SetAttribute("id", static_cast<unsigned int>(info.id));
|
||||||
|
|
||||||
if (m_Completions > 0) {
|
element.SetAttribute("cct", static_cast<unsigned int>(m_Completions));
|
||||||
element.SetAttribute("cct", static_cast<unsigned int>(m_Completions));
|
|
||||||
|
|
||||||
element.SetAttribute("cts", static_cast<unsigned int>(m_Timestamp));
|
element.SetAttribute("cts", static_cast<unsigned int>(m_Timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
if (IsComplete()) {
|
void Mission::UpdateXmlCur(tinyxml2::XMLElement& element) {
|
||||||
return;
|
// Start custom XML
|
||||||
}
|
element.SetAttribute("state", static_cast<unsigned int>(m_State));
|
||||||
}
|
// End custom XML
|
||||||
|
|
||||||
|
element.DeleteChildren();
|
||||||
|
|
||||||
|
element.SetAttribute("id", static_cast<unsigned int>(info.id));
|
||||||
|
|
||||||
|
if (IsComplete()) return;
|
||||||
|
|
||||||
for (auto* task : m_Tasks) {
|
for (auto* task : m_Tasks) {
|
||||||
if (task->GetType() == eMissionTaskType::COLLECTION ||
|
if (task->GetType() == eMissionTaskType::COLLECTION ||
|
||||||
@ -345,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,13 @@ public:
|
|||||||
Mission(MissionComponent* missionComponent, uint32_t missionId);
|
Mission(MissionComponent* missionComponent, uint32_t missionId);
|
||||||
~Mission();
|
~Mission();
|
||||||
|
|
||||||
void LoadFromXml(const tinyxml2::XMLElement& element);
|
// XML functions to load and save completed mission state to xml
|
||||||
void UpdateXml(tinyxml2::XMLElement& element);
|
void LoadFromXmlDone(const tinyxml2::XMLElement& element);
|
||||||
|
void UpdateXmlDone(tinyxml2::XMLElement& element);
|
||||||
|
|
||||||
|
// XML functions to load and save current mission state and task data to xml
|
||||||
|
void LoadFromXmlCur(const tinyxml2::XMLElement& element);
|
||||||
|
void UpdateXmlCur(tinyxml2::XMLElement& element);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ID of this mission
|
* Returns the ID of this mission
|
||||||
|
@ -26,12 +26,279 @@
|
|||||||
#include "eMissionTaskType.h"
|
#include "eMissionTaskType.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
#include "eConnectionType.h"
|
#include "eConnectionType.h"
|
||||||
|
#include "User.h"
|
||||||
|
#include "StringifiedEnum.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const std::string DefaultSender = "%[MAIL_SYSTEM_NOTIFICATION]";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Mail {
|
||||||
|
std::map<eMessageID, std::function<std::unique_ptr<MailLUBitStream>()>> g_Handlers = {
|
||||||
|
{eMessageID::SendRequest, []() {
|
||||||
|
return std::make_unique<SendRequest>();
|
||||||
|
}},
|
||||||
|
{eMessageID::DataRequest, []() {
|
||||||
|
return std::make_unique<DataRequest>();
|
||||||
|
}},
|
||||||
|
{eMessageID::AttachmentCollectRequest, []() {
|
||||||
|
return std::make_unique<AttachmentCollectRequest>();
|
||||||
|
}},
|
||||||
|
{eMessageID::DeleteRequest, []() {
|
||||||
|
return std::make_unique<DeleteRequest>();
|
||||||
|
}},
|
||||||
|
{eMessageID::ReadRequest, []() {
|
||||||
|
return std::make_unique<ReadRequest>();
|
||||||
|
}},
|
||||||
|
{eMessageID::NotificationRequest, []() {
|
||||||
|
return std::make_unique<NotificationRequest>();
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
|
||||||
|
void MailLUBitStream::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
bitStream.Write(messageID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MailLUBitStream::Deserialize(RakNet::BitStream& bitstream) {
|
||||||
|
VALIDATE_READ(bitstream.Read(messageID));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SendRequest::Deserialize(RakNet::BitStream& bitStream) {
|
||||||
|
VALIDATE_READ(mailInfo.Deserialize(bitStream));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendRequest::Handle() {
|
||||||
|
SendResponse response;
|
||||||
|
auto* character = player->GetCharacter();
|
||||||
|
if (character && !(character->HasPermission(ePermissionMap::RestrictedMailAccess) || character->GetParentUser()->GetIsMuted())) {
|
||||||
|
mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), "");
|
||||||
|
auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient);
|
||||||
|
|
||||||
|
if (!receiverID) {
|
||||||
|
response.status = eSendResponse::RecipientNotFound;
|
||||||
|
} else if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) {
|
||||||
|
response.status = eSendResponse::CannotMailSelf;
|
||||||
|
} else {
|
||||||
|
uint32_t mailCost = Game::zoneManager->GetWorldConfig()->mailBaseFee;
|
||||||
|
uint32_t stackSize = 0;
|
||||||
|
|
||||||
|
auto inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||||
|
Item* item = nullptr;
|
||||||
|
|
||||||
|
bool hasAttachment = mailInfo.itemID != 0 && mailInfo.itemCount > 0;
|
||||||
|
|
||||||
|
if (hasAttachment) {
|
||||||
|
item = inventoryComponent->FindItemById(mailInfo.itemID);
|
||||||
|
if (item) {
|
||||||
|
mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig()->mailPercentAttachmentFee);
|
||||||
|
mailInfo.itemLOT = item->GetLot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAttachment && !item) {
|
||||||
|
response.status = eSendResponse::AttachmentNotFound;
|
||||||
|
} else if (player->GetCharacter()->GetCoins() - mailCost < 0) {
|
||||||
|
response.status = eSendResponse::NotEnoughCoins;
|
||||||
|
} else {
|
||||||
|
bool removeSuccess = true;
|
||||||
|
// Remove coins and items from the sender
|
||||||
|
player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL);
|
||||||
|
if (inventoryComponent && hasAttachment && item) {
|
||||||
|
removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, INVALID, true);
|
||||||
|
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||||
|
if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we passed all the checks, now we can actully send the mail
|
||||||
|
if (removeSuccess) {
|
||||||
|
mailInfo.senderId = character->GetID();
|
||||||
|
mailInfo.senderUsername = character->GetName();
|
||||||
|
mailInfo.receiverId = receiverID->id;
|
||||||
|
mailInfo.itemSubkey = LWOOBJID_EMPTY;
|
||||||
|
|
||||||
|
//clear out the attachementID
|
||||||
|
mailInfo.itemID = 0;
|
||||||
|
|
||||||
|
Database::Get()->InsertNewMail(mailInfo);
|
||||||
|
response.status = eSendResponse::Success;
|
||||||
|
character->SaveXMLToDatabase();
|
||||||
|
} else {
|
||||||
|
response.status = eSendResponse::AttachmentNotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response.status = eSendResponse::SenderAccountIsMuted;
|
||||||
|
}
|
||||||
|
response.Send(sysAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendResponse::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
MailLUBitStream::Serialize(bitStream);
|
||||||
|
bitStream.Write(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationResponse::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
MailLUBitStream::Serialize(bitStream);
|
||||||
|
bitStream.Write(status);
|
||||||
|
bitStream.Write<uint64_t>(0); // unused
|
||||||
|
bitStream.Write<uint64_t>(0); // unused
|
||||||
|
bitStream.Write(auctionID);
|
||||||
|
bitStream.Write<uint64_t>(0); // unused
|
||||||
|
bitStream.Write(mailCount);
|
||||||
|
bitStream.Write<uint32_t>(0); // packing
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataRequest::Handle() {
|
||||||
|
const auto* character = player->GetCharacter();
|
||||||
|
if (!character) return;
|
||||||
|
auto playerMail = Database::Get()->GetMailForPlayer(character->GetID(), 20);
|
||||||
|
DataResponse response;
|
||||||
|
response.playerMail = playerMail;
|
||||||
|
response.Send(sysAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataResponse::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
MailLUBitStream::Serialize(bitStream);
|
||||||
|
bitStream.Write(this->throttled);
|
||||||
|
|
||||||
|
bitStream.Write<uint16_t>(this->playerMail.size());
|
||||||
|
bitStream.Write<uint16_t>(0); // packing
|
||||||
|
for (const auto& mail : this->playerMail) {
|
||||||
|
mail.Serialize(bitStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttachmentCollectRequest::Deserialize(RakNet::BitStream& bitStream) {
|
||||||
|
uint32_t unknown;
|
||||||
|
VALIDATE_READ(bitStream.Read(unknown));
|
||||||
|
VALIDATE_READ(bitStream.Read(mailID));
|
||||||
|
VALIDATE_READ(bitStream.Read(playerID));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentCollectRequest::Handle() {
|
||||||
|
AttachmentCollectResponse response;
|
||||||
|
response.mailID = mailID;
|
||||||
|
auto inv = player->GetComponent<InventoryComponent>();
|
||||||
|
|
||||||
|
if (mailID > 0 && playerID == player->GetObjectID() && inv) {
|
||||||
|
auto playerMail = Database::Get()->GetMail(mailID);
|
||||||
|
if (!playerMail) {
|
||||||
|
response.status = eAttachmentCollectResponse::MailNotFound;
|
||||||
|
} else if (!inv->HasSpaceForLoot({ {playerMail->itemLOT, playerMail->itemCount} })) {
|
||||||
|
response.status = eAttachmentCollectResponse::NoSpaceInInventory;
|
||||||
|
} else {
|
||||||
|
inv->AddItem(playerMail->itemLOT, playerMail->itemCount, eLootSourceType::MAIL);
|
||||||
|
Database::Get()->ClaimMailItem(mailID);
|
||||||
|
response.status = eAttachmentCollectResponse::Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.Send(sysAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentCollectResponse::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
MailLUBitStream::Serialize(bitStream);
|
||||||
|
bitStream.Write(status);
|
||||||
|
bitStream.Write(mailID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeleteRequest::Deserialize(RakNet::BitStream& bitStream) {
|
||||||
|
int32_t unknown;
|
||||||
|
VALIDATE_READ(bitStream.Read(unknown));
|
||||||
|
VALIDATE_READ(bitStream.Read(mailID));
|
||||||
|
VALIDATE_READ(bitStream.Read(playerID));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteRequest::Handle() {
|
||||||
|
DeleteResponse response;
|
||||||
|
response.mailID = mailID;
|
||||||
|
|
||||||
|
auto mailData = Database::Get()->GetMail(mailID);
|
||||||
|
if (mailData && !(mailData->itemLOT != 0 && mailData->itemCount > 0)) {
|
||||||
|
Database::Get()->DeleteMail(mailID);
|
||||||
|
response.status = eDeleteResponse::Success;
|
||||||
|
} else if (mailData && mailData->itemLOT != 0 && mailData->itemCount > 0) {
|
||||||
|
response.status = eDeleteResponse::HasAttachments;
|
||||||
|
} else {
|
||||||
|
response.status = eDeleteResponse::NotFound;
|
||||||
|
}
|
||||||
|
response.Send(sysAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteResponse::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
MailLUBitStream::Serialize(bitStream);
|
||||||
|
bitStream.Write(status);
|
||||||
|
bitStream.Write(mailID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadRequest::Deserialize(RakNet::BitStream& bitStream) {
|
||||||
|
int32_t unknown;
|
||||||
|
VALIDATE_READ(bitStream.Read(unknown));
|
||||||
|
VALIDATE_READ(bitStream.Read(mailID));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadRequest::Handle() {
|
||||||
|
ReadResponse response;
|
||||||
|
response.mailID = mailID;
|
||||||
|
|
||||||
|
if (Database::Get()->GetMail(mailID)) {
|
||||||
|
response.status = eReadResponse::Success;
|
||||||
|
Database::Get()->MarkMailRead(mailID);
|
||||||
|
}
|
||||||
|
response.Send(sysAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadResponse::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
MailLUBitStream::Serialize(bitStream);
|
||||||
|
bitStream.Write(status);
|
||||||
|
bitStream.Write(mailID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationRequest::Handle() {
|
||||||
|
NotificationResponse response;
|
||||||
|
auto character = player->GetCharacter();
|
||||||
|
if (character) {
|
||||||
|
auto unreadMailCount = Database::Get()->GetUnreadMailCount(character->GetID());
|
||||||
|
response.status = eNotificationResponse::NewMail;
|
||||||
|
response.mailCount = unreadMailCount;
|
||||||
|
}
|
||||||
|
response.Send(sysAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non Stuct Functions
|
||||||
|
void Mail::HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr, Entity* player) {
|
||||||
|
MailLUBitStream data;
|
||||||
|
if (!data.Deserialize(inStream)) {
|
||||||
|
LOG_DEBUG("Error Reading Mail header");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = g_Handlers.find(data.messageID);
|
||||||
|
if (it != g_Handlers.end()) {
|
||||||
|
auto request = it->second();
|
||||||
|
request->sysAddr = sysAddr;
|
||||||
|
request->player = player;
|
||||||
|
if (!request->Deserialize(inStream)) {
|
||||||
|
LOG_DEBUG("Error Reading Mail Request: %s", StringifiedEnum::ToString(data.messageID).data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request->Handle();
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("Unhandled Mail Request with ID: %i", data.messageID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Mail::SendMail(const Entity* recipient, const std::string& subject, const std::string& body, const LOT attachment,
|
void Mail::SendMail(const Entity* recipient, const std::string& subject, const std::string& body, const LOT attachment,
|
||||||
const uint16_t attachmentCount) {
|
const uint16_t attachmentCount) {
|
||||||
SendMail(
|
SendMail(
|
||||||
LWOOBJID_EMPTY,
|
LWOOBJID_EMPTY,
|
||||||
ServerName,
|
DefaultSender,
|
||||||
recipient->GetObjectID(),
|
recipient->GetObjectID(),
|
||||||
recipient->GetCharacter()->GetName(),
|
recipient->GetCharacter()->GetName(),
|
||||||
subject,
|
subject,
|
||||||
@ -46,7 +313,7 @@ void Mail::SendMail(const LWOOBJID recipient, const std::string& recipientName,
|
|||||||
const std::string& body, const LOT attachment, const uint16_t attachmentCount, const SystemAddress& sysAddr) {
|
const std::string& body, const LOT attachment, const uint16_t attachmentCount, const SystemAddress& sysAddr) {
|
||||||
SendMail(
|
SendMail(
|
||||||
LWOOBJID_EMPTY,
|
LWOOBJID_EMPTY,
|
||||||
ServerName,
|
DefaultSender,
|
||||||
recipient,
|
recipient,
|
||||||
recipientName,
|
recipientName,
|
||||||
subject,
|
subject,
|
||||||
@ -75,7 +342,7 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, const
|
|||||||
void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJID recipient,
|
void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJID recipient,
|
||||||
const std::string& recipientName, const std::string& subject, const std::string& body, const LOT attachment,
|
const std::string& recipientName, const std::string& subject, const std::string& body, const LOT attachment,
|
||||||
const uint16_t attachmentCount, const SystemAddress& sysAddr) {
|
const uint16_t attachmentCount, const SystemAddress& sysAddr) {
|
||||||
IMail::MailInfo mailInsert;
|
MailInfo mailInsert;
|
||||||
mailInsert.senderUsername = senderName;
|
mailInsert.senderUsername = senderName;
|
||||||
mailInsert.recipient = recipientName;
|
mailInsert.recipient = recipientName;
|
||||||
mailInsert.subject = subject;
|
mailInsert.subject = subject;
|
||||||
@ -90,316 +357,7 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJ
|
|||||||
Database::Get()->InsertNewMail(mailInsert);
|
Database::Get()->InsertNewMail(mailInsert);
|
||||||
|
|
||||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) return; // TODO: Echo to chat server
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) return; // TODO: Echo to chat server
|
||||||
|
NotificationResponse response;
|
||||||
SendNotification(sysAddr, 1); //Show the "one new mail" message
|
response.status = eNotificationResponse::NewMail;
|
||||||
}
|
response.Send(sysAddr);
|
||||||
|
|
||||||
void Mail::HandleMailStuff(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) {
|
|
||||||
int mailStuffID = 0;
|
|
||||||
packet.Read(mailStuffID);
|
|
||||||
|
|
||||||
auto returnVal = std::async(std::launch::async, [&packet, &sysAddr, entity, mailStuffID]() {
|
|
||||||
Mail::MailMessageID stuffID = MailMessageID(mailStuffID);
|
|
||||||
switch (stuffID) {
|
|
||||||
case MailMessageID::AttachmentCollect:
|
|
||||||
Mail::HandleAttachmentCollect(packet, sysAddr, entity);
|
|
||||||
break;
|
|
||||||
case MailMessageID::DataRequest:
|
|
||||||
Mail::HandleDataRequest(packet, sysAddr, entity);
|
|
||||||
break;
|
|
||||||
case MailMessageID::MailDelete:
|
|
||||||
Mail::HandleMailDelete(packet, sysAddr);
|
|
||||||
break;
|
|
||||||
case MailMessageID::MailRead:
|
|
||||||
Mail::HandleMailRead(packet, sysAddr);
|
|
||||||
break;
|
|
||||||
case MailMessageID::NotificationRequest:
|
|
||||||
Mail::HandleNotificationRequest(sysAddr, entity->GetObjectID());
|
|
||||||
break;
|
|
||||||
case MailMessageID::Send:
|
|
||||||
Mail::HandleSendMail(packet, sysAddr, entity);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG("Unhandled and possibly undefined MailStuffID: %i", int(stuffID));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) {
|
|
||||||
//std::string subject = GeneralUtils::WStringToString(ReadFromPacket(packet, 50));
|
|
||||||
//std::string body = GeneralUtils::WStringToString(ReadFromPacket(packet, 400));
|
|
||||||
//std::string recipient = GeneralUtils::WStringToString(ReadFromPacket(packet, 32));
|
|
||||||
|
|
||||||
// Check if the player has restricted mail access
|
|
||||||
auto* character = entity->GetCharacter();
|
|
||||||
|
|
||||||
if (!character) return;
|
|
||||||
|
|
||||||
if (character->HasPermission(ePermissionMap::RestrictedMailAccess)) {
|
|
||||||
// Send a message to the player
|
|
||||||
ChatPackets::SendSystemMessage(
|
|
||||||
sysAddr,
|
|
||||||
u"This character has restricted mail access."
|
|
||||||
);
|
|
||||||
|
|
||||||
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::AccountIsMuted);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LUWString subjectRead(50);
|
|
||||||
packet.Read(subjectRead);
|
|
||||||
|
|
||||||
LUWString bodyRead(400);
|
|
||||||
packet.Read(bodyRead);
|
|
||||||
|
|
||||||
LUWString recipientRead(32);
|
|
||||||
packet.Read(recipientRead);
|
|
||||||
|
|
||||||
const std::string subject = subjectRead.GetAsString();
|
|
||||||
const std::string body = bodyRead.GetAsString();
|
|
||||||
|
|
||||||
//Cleanse recipient:
|
|
||||||
const std::string recipient = std::regex_replace(recipientRead.GetAsString(), std::regex("[^0-9a-zA-Z]+"), "");
|
|
||||||
|
|
||||||
uint64_t unknown64 = 0;
|
|
||||||
LWOOBJID attachmentID;
|
|
||||||
uint16_t attachmentCount;
|
|
||||||
|
|
||||||
packet.Read(unknown64);
|
|
||||||
packet.Read(attachmentID);
|
|
||||||
packet.Read(attachmentCount); //We don't care about the rest of the packet.
|
|
||||||
uint32_t itemID = static_cast<uint32_t>(attachmentID);
|
|
||||||
LOT itemLOT = 0;
|
|
||||||
//Inventory::InventoryType itemType;
|
|
||||||
int mailCost = Game::zoneManager->GetWorldConfig()->mailBaseFee;
|
|
||||||
int stackSize = 0;
|
|
||||||
auto inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
||||||
Item* item = nullptr;
|
|
||||||
|
|
||||||
if (itemID > 0 && attachmentCount > 0 && inv) {
|
|
||||||
item = inv->FindItemById(attachmentID);
|
|
||||||
if (item) {
|
|
||||||
mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig()->mailPercentAttachmentFee);
|
|
||||||
stackSize = item->GetCount();
|
|
||||||
itemLOT = item->GetLot();
|
|
||||||
} else {
|
|
||||||
Mail::SendSendResponse(sysAddr, MailSendResponse::AttachmentNotFound);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if we can even send this mail (negative coins bug):
|
|
||||||
if (entity->GetCharacter()->GetCoins() - mailCost < 0) {
|
|
||||||
Mail::SendSendResponse(sysAddr, MailSendResponse::NotEnoughCoins);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get the receiver's id:
|
|
||||||
auto receiverID = Database::Get()->GetCharacterInfo(recipient);
|
|
||||||
|
|
||||||
if (!receiverID) {
|
|
||||||
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::RecipientNotFound);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if we have a valid receiver:
|
|
||||||
if (GeneralUtils::CaseInsensitiveStringCompare(recipient, character->GetName()) || receiverID->id == character->GetID()) {
|
|
||||||
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::CannotMailSelf);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
IMail::MailInfo mailInsert;
|
|
||||||
mailInsert.senderUsername = character->GetName();
|
|
||||||
mailInsert.recipient = recipient;
|
|
||||||
mailInsert.subject = subject;
|
|
||||||
mailInsert.body = body;
|
|
||||||
mailInsert.senderId = character->GetID();
|
|
||||||
mailInsert.receiverId = receiverID->id;
|
|
||||||
mailInsert.itemCount = attachmentCount;
|
|
||||||
mailInsert.itemID = itemID;
|
|
||||||
mailInsert.itemLOT = itemLOT;
|
|
||||||
mailInsert.itemSubkey = LWOOBJID_EMPTY;
|
|
||||||
Database::Get()->InsertNewMail(mailInsert);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::Success);
|
|
||||||
entity->GetCharacter()->SetCoins(entity->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL);
|
|
||||||
|
|
||||||
LOG("Seeing if we need to remove item with ID/count/LOT: %i %i %i", itemID, attachmentCount, itemLOT);
|
|
||||||
|
|
||||||
if (inv && itemLOT != 0 && attachmentCount > 0 && item) {
|
|
||||||
LOG("Trying to remove item with ID/count/LOT: %i %i %i", itemID, attachmentCount, itemLOT);
|
|
||||||
inv->RemoveItem(itemLOT, attachmentCount, INVALID, true);
|
|
||||||
|
|
||||||
auto* missionCompoent = entity->GetComponent<MissionComponent>();
|
|
||||||
|
|
||||||
if (missionCompoent != nullptr) {
|
|
||||||
missionCompoent->Progress(eMissionTaskType::GATHER, itemLOT, LWOOBJID_EMPTY, "", -attachmentCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
character->SaveXMLToDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player) {
|
|
||||||
auto playerMail = Database::Get()->GetMailForPlayer(player->GetCharacter()->GetID(), 20);
|
|
||||||
|
|
||||||
RakNet::BitStream bitStream;
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL);
|
|
||||||
bitStream.Write(int(MailMessageID::MailData));
|
|
||||||
bitStream.Write(int(0)); // throttled
|
|
||||||
|
|
||||||
bitStream.Write<uint16_t>(playerMail.size()); // size
|
|
||||||
bitStream.Write<uint16_t>(0);
|
|
||||||
|
|
||||||
for (const auto& mail : playerMail) {
|
|
||||||
bitStream.Write(mail.id); //MailID
|
|
||||||
|
|
||||||
const LUWString subject(mail.subject, 50);
|
|
||||||
bitStream.Write(subject); //subject
|
|
||||||
const LUWString body(mail.body, 400);
|
|
||||||
bitStream.Write(body); //body
|
|
||||||
const LUWString sender(mail.senderUsername, 32);
|
|
||||||
bitStream.Write(sender); //sender
|
|
||||||
bitStream.Write(uint32_t(0)); // packing
|
|
||||||
|
|
||||||
bitStream.Write(uint64_t(0)); // attachedCurrency
|
|
||||||
|
|
||||||
bitStream.Write(mail.itemID); //Attachment ID
|
|
||||||
LOT lot = mail.itemLOT;
|
|
||||||
if (lot <= 0) bitStream.Write(LOT(-1));
|
|
||||||
else bitStream.Write(lot);
|
|
||||||
bitStream.Write(uint32_t(0)); // packing
|
|
||||||
|
|
||||||
bitStream.Write(mail.itemSubkey); // Attachment subKey
|
|
||||||
|
|
||||||
bitStream.Write<uint16_t>(mail.itemCount); // Attachment count
|
|
||||||
bitStream.Write(uint8_t(0)); // subject type (used for auction)
|
|
||||||
bitStream.Write(uint8_t(0)); // packing
|
|
||||||
bitStream.Write(uint32_t(0)); // packing
|
|
||||||
|
|
||||||
bitStream.Write<uint64_t>(mail.timeSent); // expiration date
|
|
||||||
bitStream.Write<uint64_t>(mail.timeSent);// send date
|
|
||||||
bitStream.Write<uint8_t>(mail.wasRead); //was read
|
|
||||||
|
|
||||||
bitStream.Write(uint8_t(0)); // isLocalized
|
|
||||||
bitStream.Write(uint16_t(0)); // packing
|
|
||||||
bitStream.Write(uint32_t(0)); // packing
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::server->Send(bitStream, sysAddr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::HandleAttachmentCollect(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player) {
|
|
||||||
int unknown;
|
|
||||||
uint64_t mailID;
|
|
||||||
LWOOBJID playerID;
|
|
||||||
packet.Read(unknown);
|
|
||||||
packet.Read(mailID);
|
|
||||||
packet.Read(playerID);
|
|
||||||
|
|
||||||
if (mailID > 0 && playerID == player->GetObjectID()) {
|
|
||||||
auto playerMail = Database::Get()->GetMail(mailID);
|
|
||||||
|
|
||||||
LOT attachmentLOT = 0;
|
|
||||||
uint32_t attachmentCount = 0;
|
|
||||||
|
|
||||||
if (playerMail) {
|
|
||||||
attachmentLOT = playerMail->itemLOT;
|
|
||||||
attachmentCount = playerMail->itemCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto inv = player->GetComponent<InventoryComponent>();
|
|
||||||
if (!inv) return;
|
|
||||||
|
|
||||||
inv->AddItem(attachmentLOT, attachmentCount, eLootSourceType::MAIL);
|
|
||||||
|
|
||||||
Mail::SendAttachmentRemoveConfirm(sysAddr, mailID);
|
|
||||||
|
|
||||||
Database::Get()->ClaimMailItem(mailID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::HandleMailDelete(RakNet::BitStream& packet, const SystemAddress& sysAddr) {
|
|
||||||
int unknown;
|
|
||||||
uint64_t mailID;
|
|
||||||
LWOOBJID playerID;
|
|
||||||
packet.Read(unknown);
|
|
||||||
packet.Read(mailID);
|
|
||||||
packet.Read(playerID);
|
|
||||||
|
|
||||||
if (mailID > 0) Mail::SendDeleteConfirm(sysAddr, mailID, playerID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::HandleMailRead(RakNet::BitStream& packet, const SystemAddress& sysAddr) {
|
|
||||||
int unknown;
|
|
||||||
uint64_t mailID;
|
|
||||||
packet.Read(unknown);
|
|
||||||
packet.Read(mailID);
|
|
||||||
|
|
||||||
if (mailID > 0) Mail::SendReadConfirm(sysAddr, mailID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t objectID) {
|
|
||||||
auto unreadMailCount = Database::Get()->GetUnreadMailCount(objectID);
|
|
||||||
|
|
||||||
if (unreadMailCount > 0) Mail::SendNotification(sysAddr, unreadMailCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response) {
|
|
||||||
RakNet::BitStream bitStream;
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL);
|
|
||||||
bitStream.Write(int(MailMessageID::SendResponse));
|
|
||||||
bitStream.Write(int(response));
|
|
||||||
Game::server->Send(bitStream, sysAddr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::SendNotification(const SystemAddress& sysAddr, int mailCount) {
|
|
||||||
RakNet::BitStream bitStream;
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL);
|
|
||||||
uint64_t messageType = 2;
|
|
||||||
uint64_t s1 = 0;
|
|
||||||
uint64_t s2 = 0;
|
|
||||||
uint64_t s3 = 0;
|
|
||||||
uint64_t s4 = 0;
|
|
||||||
|
|
||||||
bitStream.Write(messageType);
|
|
||||||
bitStream.Write(s1);
|
|
||||||
bitStream.Write(s2);
|
|
||||||
bitStream.Write(s3);
|
|
||||||
bitStream.Write(s4);
|
|
||||||
bitStream.Write(mailCount);
|
|
||||||
bitStream.Write(int(0)); //Unknown
|
|
||||||
Game::server->Send(bitStream, sysAddr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::SendAttachmentRemoveConfirm(const SystemAddress& sysAddr, uint64_t mailID) {
|
|
||||||
RakNet::BitStream bitStream;
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL);
|
|
||||||
bitStream.Write(int(MailMessageID::AttachmentCollectConfirm));
|
|
||||||
bitStream.Write(int(0)); //unknown
|
|
||||||
bitStream.Write(mailID);
|
|
||||||
Game::server->Send(bitStream, sysAddr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOOBJID playerID) {
|
|
||||||
RakNet::BitStream bitStream;
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL);
|
|
||||||
bitStream.Write(int(MailMessageID::MailDeleteConfirm));
|
|
||||||
bitStream.Write(int(0)); //unknown
|
|
||||||
bitStream.Write(mailID);
|
|
||||||
Game::server->Send(bitStream, sysAddr, false);
|
|
||||||
|
|
||||||
Database::Get()->DeleteMail(mailID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) {
|
|
||||||
RakNet::BitStream bitStream;
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL);
|
|
||||||
bitStream.Write(int(MailMessageID::MailReadConfirm));
|
|
||||||
bitStream.Write(int(0)); //unknown
|
|
||||||
bitStream.Write(mailID);
|
|
||||||
Game::server->Send(bitStream, sysAddr, false);
|
|
||||||
|
|
||||||
Database::Get()->MarkMailRead(mailID);
|
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,210 @@
|
|||||||
#pragma once
|
#ifndef __MAIL_H__
|
||||||
|
#define __MAIL_H__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include "RakNetTypes.h"
|
#include "RakNetTypes.h"
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
|
#include "BitStreamUtils.h"
|
||||||
|
#include "MailInfo.h"
|
||||||
|
|
||||||
class Entity;
|
class Entity;
|
||||||
|
|
||||||
namespace Mail {
|
namespace Mail {
|
||||||
enum class MailMessageID {
|
enum class eMessageID : uint32_t {
|
||||||
Send = 0x00,
|
SendRequest = 0,
|
||||||
SendResponse = 0x01,
|
SendResponse,
|
||||||
DataRequest = 0x03,
|
NotificationResponse,
|
||||||
MailData = 0x04,
|
DataRequest,
|
||||||
AttachmentCollect = 0x05,
|
DataResponse,
|
||||||
AttachmentCollectConfirm = 0x06,
|
AttachmentCollectRequest,
|
||||||
MailDelete = 0x07,
|
AttachmentCollectResponse,
|
||||||
MailDeleteConfirm = 0x08,
|
DeleteRequest,
|
||||||
MailRead = 0x09,
|
DeleteResponse,
|
||||||
MailReadConfirm = 0x0a,
|
ReadRequest,
|
||||||
NotificationRequest = 0x0b
|
ReadResponse,
|
||||||
|
NotificationRequest,
|
||||||
|
AuctionCreate,
|
||||||
|
AuctionCreationResponse,
|
||||||
|
AuctionCancel,
|
||||||
|
AuctionCancelResponse,
|
||||||
|
AuctionList,
|
||||||
|
AuctionListResponse,
|
||||||
|
AuctionBid,
|
||||||
|
AuctionBidResponse,
|
||||||
|
UnknownError
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class MailSendResponse {
|
enum class eSendResponse : uint32_t {
|
||||||
Success = 0,
|
Success = 0,
|
||||||
NotEnoughCoins,
|
NotEnoughCoins,
|
||||||
AttachmentNotFound,
|
AttachmentNotFound,
|
||||||
ItemCannotBeMailed,
|
ItemCannotBeMailed,
|
||||||
CannotMailSelf,
|
CannotMailSelf,
|
||||||
RecipientNotFound,
|
RecipientNotFound,
|
||||||
DifferentFaction,
|
RecipientDifferentFaction,
|
||||||
Unknown,
|
UnHandled7,
|
||||||
ModerationFailure,
|
ModerationFailure,
|
||||||
AccountIsMuted,
|
SenderAccountIsMuted,
|
||||||
UnknownFailure,
|
UnHandled10,
|
||||||
RecipientIsIgnored,
|
RecipientIsIgnored,
|
||||||
UnknownFailure3,
|
UnHandled12,
|
||||||
RecipientIsFTP
|
RecipientIsFTP,
|
||||||
|
UnknownError
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string ServerName = "Darkflame Universe";
|
enum class eDeleteResponse : uint32_t {
|
||||||
|
Success = 0,
|
||||||
|
HasAttachments,
|
||||||
|
NotFound,
|
||||||
|
Throttled,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class eAttachmentCollectResponse : uint32_t {
|
||||||
|
Success = 0,
|
||||||
|
AttachmentNotFound,
|
||||||
|
NoSpaceInInventory,
|
||||||
|
MailNotFound,
|
||||||
|
Throttled,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class eNotificationResponse : uint32_t {
|
||||||
|
NewMail = 0,
|
||||||
|
UnHandled,
|
||||||
|
AuctionWon,
|
||||||
|
AuctionSold,
|
||||||
|
AuctionOutbided,
|
||||||
|
AuctionExpired,
|
||||||
|
AuctionCancelled,
|
||||||
|
AuctionUpdated,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class eReadResponse : uint32_t {
|
||||||
|
Success = 0,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class eAuctionCreateResponse : uint32_t {
|
||||||
|
Success = 0,
|
||||||
|
NotEnoughMoney,
|
||||||
|
ItemNotFound,
|
||||||
|
ItemNotSellable,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class eAuctionCancelResponse : uint32_t {
|
||||||
|
NotFound = 0,
|
||||||
|
NotYours,
|
||||||
|
HasBid,
|
||||||
|
NoLongerExists,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MailLUBitStream : public LUBitStream {
|
||||||
|
eMessageID messageID = eMessageID::UnknownError;
|
||||||
|
SystemAddress sysAddr = UNASSIGNED_SYSTEM_ADDRESS;
|
||||||
|
Entity* player = nullptr;
|
||||||
|
|
||||||
|
MailLUBitStream() = default;
|
||||||
|
MailLUBitStream(eMessageID _messageID) : LUBitStream(eConnectionType::CLIENT, MessageType::Client::MAIL), messageID{_messageID} {};
|
||||||
|
|
||||||
|
virtual void Serialize(RakNet::BitStream& bitStream) const override;
|
||||||
|
virtual bool Deserialize(RakNet::BitStream& bitStream) override;
|
||||||
|
virtual void Handle() override {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SendRequest : public MailLUBitStream {
|
||||||
|
MailInfo mailInfo;
|
||||||
|
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
||||||
|
void Handle() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SendResponse :public MailLUBitStream {
|
||||||
|
eSendResponse status = eSendResponse::UnknownError;
|
||||||
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NotificationResponse : public MailLUBitStream {
|
||||||
|
eNotificationResponse status = eNotificationResponse::UnknownError;
|
||||||
|
LWOOBJID auctionID = LWOOBJID_EMPTY;
|
||||||
|
uint32_t mailCount = 1;
|
||||||
|
NotificationResponse() : MailLUBitStream(eMessageID::NotificationResponse) {};
|
||||||
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DataRequest : public MailLUBitStream {
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream) override { return true; };
|
||||||
|
void Handle() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DataResponse : public MailLUBitStream {
|
||||||
|
uint32_t throttled = 0;
|
||||||
|
std::vector<MailInfo> playerMail;
|
||||||
|
|
||||||
|
DataResponse() : MailLUBitStream(eMessageID::DataResponse) {};
|
||||||
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AttachmentCollectRequest : public MailLUBitStream {
|
||||||
|
uint64_t mailID = 0;
|
||||||
|
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||||
|
|
||||||
|
AttachmentCollectRequest() : MailLUBitStream(eMessageID::AttachmentCollectRequest) {};
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
||||||
|
void Handle() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AttachmentCollectResponse : public MailLUBitStream {
|
||||||
|
eAttachmentCollectResponse status = eAttachmentCollectResponse::UnknownError;
|
||||||
|
uint64_t mailID = 0;
|
||||||
|
AttachmentCollectResponse() : MailLUBitStream(eMessageID::AttachmentCollectResponse) {};
|
||||||
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DeleteRequest : public MailLUBitStream {
|
||||||
|
uint64_t mailID = 0;
|
||||||
|
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||||
|
|
||||||
|
DeleteRequest() : MailLUBitStream(eMessageID::DeleteRequest) {};
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
||||||
|
void Handle() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DeleteResponse : public MailLUBitStream {
|
||||||
|
eDeleteResponse status = eDeleteResponse::UnknownError;
|
||||||
|
uint64_t mailID = 0;
|
||||||
|
DeleteResponse() : MailLUBitStream(eMessageID::DeleteResponse) {};
|
||||||
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReadRequest : public MailLUBitStream {
|
||||||
|
uint64_t mailID = 0;
|
||||||
|
|
||||||
|
ReadRequest() : MailLUBitStream(eMessageID::ReadRequest) {};
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
||||||
|
void Handle() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReadResponse : public MailLUBitStream {
|
||||||
|
uint64_t mailID = 0;
|
||||||
|
eReadResponse status = eReadResponse::UnknownError;
|
||||||
|
|
||||||
|
ReadResponse() : MailLUBitStream(eMessageID::ReadResponse) {};
|
||||||
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NotificationRequest : public MailLUBitStream {
|
||||||
|
NotificationRequest() : MailLUBitStream(eMessageID::NotificationRequest) {};
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream) override { return true; };
|
||||||
|
void Handle() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
void HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr, Entity* player);
|
||||||
|
|
||||||
void SendMail(
|
void SendMail(
|
||||||
const Entity* recipient,
|
const Entity* recipient,
|
||||||
@ -78,18 +245,6 @@ namespace Mail {
|
|||||||
uint16_t attachmentCount,
|
uint16_t attachmentCount,
|
||||||
const SystemAddress& sysAddr
|
const SystemAddress& sysAddr
|
||||||
);
|
);
|
||||||
|
|
||||||
void HandleMailStuff(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity);
|
|
||||||
void HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity);
|
|
||||||
void HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player);
|
|
||||||
void HandleAttachmentCollect(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player);
|
|
||||||
void HandleMailDelete(RakNet::BitStream& packet, const SystemAddress& sysAddr);
|
|
||||||
void HandleMailRead(RakNet::BitStream& packet, const SystemAddress& sysAddr);
|
|
||||||
void HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t objectID);
|
|
||||||
|
|
||||||
void SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response);
|
|
||||||
void SendNotification(const SystemAddress& sysAddr, int mailCount);
|
|
||||||
void SendAttachmentRemoveConfirm(const SystemAddress& sysAddr, uint64_t mailID);
|
|
||||||
void SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOOBJID playerID);
|
|
||||||
void SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // !__MAIL_H__
|
||||||
|
@ -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);
|
||||||
|
|
||||||
@ -996,7 +996,7 @@ void SlashCommandHandler::Startup() {
|
|||||||
Command RequestMailCountCommand{
|
Command RequestMailCountCommand{
|
||||||
.help = "Gets the players mail count",
|
.help = "Gets the players mail count",
|
||||||
.info = "Sends notification with number of unread messages in the player's mailbox",
|
.info = "Sends notification with number of unread messages in the player's mailbox",
|
||||||
.aliases = { "requestmailcount" },
|
.aliases = { "requestmailcount", "checkmail" },
|
||||||
.handle = GMZeroCommands::RequestMailCount,
|
.handle = GMZeroCommands::RequestMailCount,
|
||||||
.requiredLevel = eGameMasterLevel::CIVILIAN
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
};
|
};
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -102,7 +102,7 @@ namespace GMGreaterThanZeroCommands {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMail::MailInfo mailInsert;
|
MailInfo mailInsert;
|
||||||
mailInsert.senderId = entity->GetObjectID();
|
mailInsert.senderId = entity->GetObjectID();
|
||||||
mailInsert.senderUsername = "Darkflame Universe";
|
mailInsert.senderUsername = "Darkflame Universe";
|
||||||
mailInsert.receiverId = receiverID;
|
mailInsert.receiverId = receiverID;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "VanityUtilities.h"
|
#include "VanityUtilities.h"
|
||||||
#include "WorldPackets.h"
|
#include "WorldPackets.h"
|
||||||
#include "ZoneInstanceManager.h"
|
#include "ZoneInstanceManager.h"
|
||||||
|
#include "Database.h"
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
#include "BuffComponent.h"
|
#include "BuffComponent.h"
|
||||||
@ -216,7 +217,10 @@ namespace GMZeroCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||||
Mail::HandleNotificationRequest(entity->GetSystemAddress(), entity->GetObjectID());
|
Mail::NotificationResponse response;
|
||||||
|
response.status = Mail::eNotificationResponse::NewMail;
|
||||||
|
response.mailCount = Database::Get()->GetUnreadMailCount(entity->GetCharacter()->GetID());
|
||||||
|
response.Send(sysAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -42,6 +42,14 @@
|
|||||||
#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
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
Logger* logger = nullptr;
|
Logger* logger = nullptr;
|
||||||
@ -62,6 +70,14 @@ std::map<uint32_t, std::string> activeSessions;
|
|||||||
SystemAddress authServerMasterPeerSysAddr;
|
SystemAddress authServerMasterPeerSysAddr;
|
||||||
SystemAddress chatServerMasterPeerSysAddr;
|
SystemAddress chatServerMasterPeerSysAddr;
|
||||||
|
|
||||||
|
int GenerateBCryptPassword(const std::string& password, const int workFactor, char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE]) {
|
||||||
|
int32_t bcryptState = ::bcrypt_gensalt(workFactor, salt);
|
||||||
|
assert(bcryptState == 0);
|
||||||
|
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
|
||||||
|
assert(bcryptState == 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
constexpr uint32_t masterFramerate = mediumFramerate;
|
constexpr uint32_t masterFramerate = mediumFramerate;
|
||||||
constexpr uint32_t masterFrameDelta = mediumFrameDelta;
|
constexpr uint32_t masterFrameDelta = mediumFrameDelta;
|
||||||
@ -94,7 +110,7 @@ int main(int argc, char** argv) {
|
|||||||
std::string(folder) +
|
std::string(folder) +
|
||||||
") folder from your download to the binary directory or re-run cmake.";
|
") folder from your download to the binary directory or re-run cmake.";
|
||||||
LOG("%s", msg.c_str());
|
LOG("%s", msg.c_str());
|
||||||
// toss an error box up for windows users running the download
|
// toss an error box up for windows users running the download
|
||||||
#ifdef DARKFLAME_PLATFORM_WIN32
|
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||||
MessageBoxA(nullptr, msg.c_str(), "Missing Folder", MB_OK | MB_ICONERROR);
|
MessageBoxA(nullptr, msg.c_str(), "Missing Folder", MB_OK | MB_ICONERROR);
|
||||||
#endif
|
#endif
|
||||||
@ -197,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";
|
||||||
@ -238,10 +261,8 @@ int main(int argc, char** argv) {
|
|||||||
// Regenerate hash based on new password
|
// Regenerate hash based on new password
|
||||||
char salt[BCRYPT_HASHSIZE];
|
char salt[BCRYPT_HASHSIZE];
|
||||||
char hash[BCRYPT_HASHSIZE];
|
char hash[BCRYPT_HASHSIZE];
|
||||||
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
|
int res = GenerateBCryptPassword(password, 12, salt, hash);
|
||||||
assert(bcryptState == 0);
|
assert(res == 0);
|
||||||
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
|
|
||||||
assert(bcryptState == 0);
|
|
||||||
|
|
||||||
Database::Get()->UpdateAccountPassword(accountId->id, std::string(hash, BCRYPT_HASHSIZE));
|
Database::Get()->UpdateAccountPassword(accountId->id, std::string(hash, BCRYPT_HASHSIZE));
|
||||||
|
|
||||||
@ -279,10 +300,8 @@ int main(int argc, char** argv) {
|
|||||||
//Generate new hash for bcrypt
|
//Generate new hash for bcrypt
|
||||||
char salt[BCRYPT_HASHSIZE];
|
char salt[BCRYPT_HASHSIZE];
|
||||||
char hash[BCRYPT_HASHSIZE];
|
char hash[BCRYPT_HASHSIZE];
|
||||||
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
|
int res = GenerateBCryptPassword(password, 12, salt, hash);
|
||||||
assert(bcryptState == 0);
|
assert(res == 0);
|
||||||
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
|
|
||||||
assert(bcryptState == 0);
|
|
||||||
|
|
||||||
//Create account
|
//Create account
|
||||||
try {
|
try {
|
||||||
@ -318,15 +337,25 @@ int main(int argc, char** argv) {
|
|||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal);
|
char salt[BCRYPT_HASHSIZE];
|
||||||
|
char hash[BCRYPT_HASHSIZE];
|
||||||
|
const auto& cfgPassword = Game::config->GetValue("master_password");
|
||||||
|
int res = GenerateBCryptPassword(!cfgPassword.empty() ? cfgPassword : "3.25DARKFLAME1", 13, salt, hash);
|
||||||
|
assert(res == 0);
|
||||||
|
|
||||||
|
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal, hash);
|
||||||
|
|
||||||
std::string master_server_ip = "localhost";
|
std::string master_server_ip = "localhost";
|
||||||
const auto masterServerIPString = Game::config->GetValue("master_ip");
|
const auto masterServerIPString = Game::config->GetValue("master_ip");
|
||||||
if (!masterServerIPString.empty()) master_server_ip = masterServerIPString;
|
if (!masterServerIPString.empty()) master_server_ip = masterServerIPString;
|
||||||
|
|
||||||
if (master_server_ip == "") master_server_ip = Game::server->GetIP();
|
if (master_server_ip == "") master_server_ip = Game::server->GetIP();
|
||||||
|
IServers::MasterInfo info;
|
||||||
|
info.ip = master_server_ip;
|
||||||
|
info.port = Game::server->GetPort();
|
||||||
|
info.password = hash;
|
||||||
|
|
||||||
Database::Get()->SetMasterIp(master_server_ip, Game::server->GetPort());
|
Database::Get()->SetMasterInfo(info);
|
||||||
|
|
||||||
//Create additional objects here:
|
//Create additional objects here:
|
||||||
PersistentIDManager::Initialize();
|
PersistentIDManager::Initialize();
|
||||||
@ -441,6 +470,12 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DARKFLAME_PLATFORM_UNIX
|
||||||
|
// kill off dead zombie instances
|
||||||
|
int status{};
|
||||||
|
waitpid(static_cast<pid_t>(-1), &status, WNOHANG);
|
||||||
|
#endif
|
||||||
|
|
||||||
t += std::chrono::milliseconds(masterFrameDelta);
|
t += std::chrono::milliseconds(masterFrameDelta);
|
||||||
std::this_thread::sleep_until(t);
|
std::this_thread::sleep_until(t);
|
||||||
}
|
}
|
||||||
@ -529,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
|
||||||
@ -570,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);
|
||||||
}
|
}
|
||||||
@ -586,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;
|
||||||
}
|
}
|
||||||
@ -665,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;
|
||||||
}
|
}
|
||||||
@ -706,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;
|
||||||
}
|
}
|
||||||
@ -808,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;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
|
|||||||
if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
|
if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
|
||||||
else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
|
else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
|
||||||
else bitStream.Write(ServiceId::General);
|
else bitStream.Write(ServiceId::General);
|
||||||
bitStream.Write<uint64_t>(219818241584);
|
bitStream.Write<uint64_t>(219818307120);
|
||||||
|
|
||||||
server->Send(bitStream, sysAddr, false);
|
server->Send(bitStream, sysAddr, false);
|
||||||
}
|
}
|
||||||
|
30
dNet/BitStreamUtils.cpp
Normal file
30
dNet/BitStreamUtils.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "BitStreamUtils.h"
|
||||||
|
#include "dServer.h"
|
||||||
|
#include "BitStream.h"
|
||||||
|
#include "PacketUtils.h"
|
||||||
|
|
||||||
|
|
||||||
|
void LUBitStream::WriteHeader(RakNet::BitStream& bitStream) const {
|
||||||
|
bitStream.Write<MessageID>(ID_USER_PACKET_ENUM);
|
||||||
|
bitStream.Write(this->connectionType);
|
||||||
|
bitStream.Write(this->internalPacketID);
|
||||||
|
bitStream.Write<uint8_t>(0); // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LUBitStream::ReadHeader(RakNet::BitStream& bitStream) {
|
||||||
|
MessageID messageID;
|
||||||
|
bitStream.Read(messageID);
|
||||||
|
if (messageID != ID_USER_PACKET_ENUM) return false;
|
||||||
|
VALIDATE_READ(bitStream.Read(this->connectionType));
|
||||||
|
VALIDATE_READ(bitStream.Read(this->internalPacketID));
|
||||||
|
uint8_t padding;
|
||||||
|
VALIDATE_READ(bitStream.Read<uint8_t>(padding));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LUBitStream::Send(const SystemAddress& sysAddr) const {
|
||||||
|
RakNet::BitStream bitStream;
|
||||||
|
this->WriteHeader(bitStream);
|
||||||
|
this->Serialize(bitStream);
|
||||||
|
Game::server->Send(bitStream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
|
}
|
@ -2,12 +2,13 @@
|
|||||||
#define __BITSTREAMUTILS__H__
|
#define __BITSTREAMUTILS__H__
|
||||||
|
|
||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
#include "MessageIdentifiers.h"
|
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
|
#include "MessageIdentifiers.h"
|
||||||
|
#include "eConnectionType.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
enum class eConnectionType : uint16_t;
|
#define VALIDATE_READ(x) do { if (!x) return false; } while (0)
|
||||||
|
|
||||||
struct LUString {
|
struct LUString {
|
||||||
std::string string;
|
std::string string;
|
||||||
@ -45,6 +46,28 @@ struct LUWString {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LUBitStream {
|
||||||
|
eConnectionType connectionType = eConnectionType::UNKNOWN;
|
||||||
|
uint32_t internalPacketID = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
LUBitStream() = default;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
LUBitStream(eConnectionType connectionType, T internalPacketID) {
|
||||||
|
this->connectionType = connectionType;
|
||||||
|
this->internalPacketID = static_cast<uint32_t>(internalPacketID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteHeader(RakNet::BitStream& bitStream) const;
|
||||||
|
bool ReadHeader(RakNet::BitStream& bitStream);
|
||||||
|
void Send(const SystemAddress& sysAddr) const;
|
||||||
|
|
||||||
|
virtual void Serialize(RakNet::BitStream& bitStream) const {}
|
||||||
|
virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; }
|
||||||
|
virtual void Handle() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace BitStreamUtils {
|
namespace BitStreamUtils {
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void WriteHeader(RakNet::BitStream& bitStream, eConnectionType connectionType, T internalPacketID) {
|
void WriteHeader(RakNet::BitStream& bitStream, eConnectionType connectionType, T internalPacketID) {
|
||||||
@ -53,7 +76,6 @@ namespace BitStreamUtils {
|
|||||||
bitStream.Write(static_cast<uint32_t>(internalPacketID));
|
bitStream.Write(static_cast<uint32_t>(internalPacketID));
|
||||||
bitStream.Write<uint8_t>(0);
|
bitStream.Write<uint8_t>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace RakNet {
|
namespace RakNet {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
set(DNET_SOURCES "AuthPackets.cpp"
|
set(DNET_SOURCES "AuthPackets.cpp"
|
||||||
|
"BitStreamUtils.cpp"
|
||||||
"ChatPackets.cpp"
|
"ChatPackets.cpp"
|
||||||
"ClientPackets.cpp"
|
"ClientPackets.cpp"
|
||||||
"dServer.cpp"
|
"dServer.cpp"
|
||||||
|
"MailInfo.cpp"
|
||||||
"MasterPackets.cpp"
|
"MasterPackets.cpp"
|
||||||
"PacketUtils.cpp"
|
"PacketUtils.cpp"
|
||||||
"WorldPackets.cpp"
|
"WorldPackets.cpp"
|
||||||
|
@ -97,3 +97,54 @@ void ChatPackets::SendMessageFail(const SystemAddress& sysAddr) {
|
|||||||
//docs say there's a wstring here-- no idea what it's for, or if it's even needed so leaving it as is for now.
|
//docs say there's a wstring here-- no idea what it's for, or if it's even needed so leaving it as is for now.
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatPackets::Announcement::Send() {
|
||||||
|
CBITSTREAM;
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_ANNOUNCE);
|
||||||
|
bitStream.Write<uint32_t>(title.size());
|
||||||
|
bitStream.Write(title);
|
||||||
|
bitStream.Write<uint32_t>(message.size());
|
||||||
|
bitStream.Write(message);
|
||||||
|
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(earnerName);
|
||||||
|
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(targetPlayerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatPackets::AchievementNotify::Deserialize(RakNet::BitStream& bitstream) {
|
||||||
|
bitstream.IgnoreBytes(13);
|
||||||
|
VALIDATE_READ(bitstream.Read(earnerName));
|
||||||
|
bitstream.IgnoreBytes(15);
|
||||||
|
VALIDATE_READ(bitstream.Read(missionEmailID));
|
||||||
|
VALIDATE_READ(bitstream.Read(earningPlayerID));
|
||||||
|
VALIDATE_READ(bitstream.Read(targetPlayerName));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
@ -27,9 +29,36 @@ struct FindPlayerRequest{
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace ChatPackets {
|
namespace ChatPackets {
|
||||||
|
|
||||||
|
struct Announcement {
|
||||||
|
std::string title;
|
||||||
|
std::string message;
|
||||||
|
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
|
||||||
|
63
dNet/MailInfo.cpp
Normal file
63
dNet/MailInfo.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "MailInfo.h"
|
||||||
|
#include "BitStream.h"
|
||||||
|
#include "DluAssert.h"
|
||||||
|
|
||||||
|
void MailInfo::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
|
bitStream.Write(id);
|
||||||
|
const LUWString subject(this->subject, 50);
|
||||||
|
bitStream.Write(subject);
|
||||||
|
const LUWString body(this->body, 400);
|
||||||
|
bitStream.Write(body);
|
||||||
|
const LUWString sender(this->senderUsername, 32);
|
||||||
|
bitStream.Write(sender);
|
||||||
|
bitStream.Write<uint32_t>(0); // packing
|
||||||
|
|
||||||
|
bitStream.Write<uint64_t>(0); // attachedCurrency
|
||||||
|
bitStream.Write(itemID);
|
||||||
|
|
||||||
|
LOT lot = itemLOT;
|
||||||
|
if (lot <= 0) bitStream.Write<LOT>(LOT_NULL);
|
||||||
|
else bitStream.Write(lot);
|
||||||
|
bitStream.Write<uint32_t>(0); // packing
|
||||||
|
|
||||||
|
bitStream.Write(itemSubkey);
|
||||||
|
|
||||||
|
bitStream.Write(itemCount);
|
||||||
|
bitStream.Write<uint8_t>(0); // subject type (used for auction)
|
||||||
|
bitStream.Write<uint8_t>(0); // packing
|
||||||
|
bitStream.Write<uint32_t>(0); // packing
|
||||||
|
|
||||||
|
bitStream.Write<uint64_t>(timeSent); // expiration date
|
||||||
|
bitStream.Write<uint64_t>(timeSent);// send date
|
||||||
|
bitStream.Write<uint8_t>(wasRead); // was read
|
||||||
|
|
||||||
|
bitStream.Write<uint8_t>(0); // isLocalized
|
||||||
|
bitStream.Write<uint16_t>(1033); // language code
|
||||||
|
bitStream.Write<uint32_t>(0); // packing
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MailInfo::Deserialize(RakNet::BitStream& bitStream) {
|
||||||
|
LUWString subject(50);
|
||||||
|
VALIDATE_READ(bitStream.Read(subject));
|
||||||
|
this->subject = subject.GetAsString();
|
||||||
|
|
||||||
|
LUWString body(400);
|
||||||
|
VALIDATE_READ(bitStream.Read(body));
|
||||||
|
this->body = body.GetAsString();
|
||||||
|
|
||||||
|
LUWString recipientName(32);
|
||||||
|
VALIDATE_READ(bitStream.Read(recipientName));
|
||||||
|
this->recipient = recipientName.GetAsString();
|
||||||
|
|
||||||
|
uint64_t unknown;
|
||||||
|
VALIDATE_READ(bitStream.Read(unknown));
|
||||||
|
|
||||||
|
VALIDATE_READ(bitStream.Read(itemID));
|
||||||
|
VALIDATE_READ(bitStream.Read(itemCount));
|
||||||
|
VALIDATE_READ(bitStream.Read(languageCode));
|
||||||
|
bitStream.IgnoreBytes(4); // padding
|
||||||
|
|
||||||
|
DluAssert(bitStream.GetNumberOfUnreadBits() == 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
34
dNet/MailInfo.h
Normal file
34
dNet/MailInfo.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef __MAILINFO_H__
|
||||||
|
#define __MAILINFO_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
|
namespace RakNet {
|
||||||
|
class BitStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MailInfo {
|
||||||
|
std::string senderUsername;
|
||||||
|
std::string recipient;
|
||||||
|
std::string subject;
|
||||||
|
std::string body;
|
||||||
|
uint64_t id{};
|
||||||
|
uint32_t senderId{};
|
||||||
|
uint32_t receiverId{};
|
||||||
|
uint64_t timeSent{};
|
||||||
|
bool wasRead{};
|
||||||
|
uint16_t languageCode{};
|
||||||
|
struct {
|
||||||
|
LWOOBJID itemID{};
|
||||||
|
int16_t itemCount{};
|
||||||
|
LOT itemLOT{};
|
||||||
|
LWOOBJID itemSubkey{};
|
||||||
|
};
|
||||||
|
|
||||||
|
void Serialize(RakNet::BitStream& bitStream) const;
|
||||||
|
bool Deserialize(RakNet::BitStream& bitStream);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __MAILINFO_H__
|
@ -3,4 +3,3 @@
|
|||||||
#include "RakPeer.h"
|
#include "RakPeer.h"
|
||||||
|
|
||||||
#define NET_PASSWORD_EXTERNAL "3.25 ND1"
|
#define NET_PASSWORD_EXTERNAL "3.25 ND1"
|
||||||
#define NET_PASSWORD_INTERNAL "3.25 DARKFLAME1"
|
|
||||||
|
@ -40,7 +40,21 @@ public:
|
|||||||
}
|
}
|
||||||
} ReceiveDownloadCompleteCB;
|
} ReceiveDownloadCompleteCB;
|
||||||
|
|
||||||
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) {
|
dServer::dServer(
|
||||||
|
const std::string& ip,
|
||||||
|
int port,
|
||||||
|
int instanceID,
|
||||||
|
int maxConnections,
|
||||||
|
bool isInternal,
|
||||||
|
bool useEncryption,
|
||||||
|
Logger* logger,
|
||||||
|
const std::string masterIP,
|
||||||
|
int masterPort,
|
||||||
|
ServerType serverType,
|
||||||
|
dConfig* config,
|
||||||
|
Game::signal_t* lastSignal,
|
||||||
|
const std::string& masterPassword,
|
||||||
|
unsigned int zoneID) {
|
||||||
mIP = ip;
|
mIP = ip;
|
||||||
mPort = port;
|
mPort = port;
|
||||||
mZoneID = zoneID;
|
mZoneID = zoneID;
|
||||||
@ -56,6 +70,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
|
|||||||
mReplicaManager = nullptr;
|
mReplicaManager = nullptr;
|
||||||
mServerType = serverType;
|
mServerType = serverType;
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
|
mMasterPassword = masterPassword;
|
||||||
mShouldShutdown = lastSignal;
|
mShouldShutdown = lastSignal;
|
||||||
//Attempt to start our server here:
|
//Attempt to start our server here:
|
||||||
mIsOkay = Startup();
|
mIsOkay = Startup();
|
||||||
@ -203,11 +218,11 @@ bool dServer::Startup() {
|
|||||||
if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false;
|
if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false;
|
||||||
|
|
||||||
if (mIsInternal) {
|
if (mIsInternal) {
|
||||||
mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15);
|
mPeer->SetIncomingPassword(mMasterPassword.c_str(), mMasterPassword.size());
|
||||||
} else {
|
} else {
|
||||||
UpdateBandwidthLimit();
|
UpdateBandwidthLimit();
|
||||||
UpdateMaximumMtuSize();
|
UpdateMaximumMtuSize();
|
||||||
mPeer->SetIncomingPassword("3.25 ND1", 8);
|
mPeer->SetIncomingPassword(NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
mPeer->SetMaximumIncomingConnections(mMaxConnections);
|
mPeer->SetMaximumIncomingConnections(mMaxConnections);
|
||||||
@ -257,7 +272,7 @@ void dServer::SetupForMasterConnection() {
|
|||||||
|
|
||||||
bool dServer::ConnectToMaster() {
|
bool dServer::ConnectToMaster() {
|
||||||
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
|
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
|
||||||
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
|
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, mMasterPassword.c_str(), mMasterPassword.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void dServer::UpdateReplica() {
|
void dServer::UpdateReplica() {
|
||||||
|
@ -46,6 +46,7 @@ public:
|
|||||||
ServerType serverType,
|
ServerType serverType,
|
||||||
dConfig* config,
|
dConfig* config,
|
||||||
Game::signal_t* shouldShutdown,
|
Game::signal_t* shouldShutdown,
|
||||||
|
const std::string& masterPassword,
|
||||||
unsigned int zoneID = 0);
|
unsigned int zoneID = 0);
|
||||||
~dServer();
|
~dServer();
|
||||||
|
|
||||||
@ -121,4 +122,5 @@ protected:
|
|||||||
std::string mMasterIP;
|
std::string mMasterIP;
|
||||||
int mMasterPort;
|
int mMasterPort;
|
||||||
std::chrono::steady_clock::time_point mStartTime = std::chrono::steady_clock::now();
|
std::chrono::steady_clock::time_point mStartTime = std::chrono::steady_clock::now();
|
||||||
|
std::string mMasterPassword;
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,7 @@ void DLUVanityTeleportingObject::OnStartup(Entity* self) {
|
|||||||
|
|
||||||
void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName) {
|
void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName) {
|
||||||
if (timerName == "setupTeleport") {
|
if (timerName == "setupTeleport") {
|
||||||
|
RenderComponent::PlayAnimation(self, u"interact");
|
||||||
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam");
|
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam");
|
||||||
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings");
|
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings");
|
||||||
|
|
||||||
@ -27,7 +28,6 @@ void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName
|
|||||||
} else if (timerName == "teleport") {
|
} else if (timerName == "teleport") {
|
||||||
std::vector<VanityObjectLocation>& locations = m_Object->m_Locations[Game::server->GetZoneID()];
|
std::vector<VanityObjectLocation>& locations = m_Object->m_Locations[Game::server->GetZoneID()];
|
||||||
|
|
||||||
selectLocation:
|
|
||||||
VanityObjectLocation& newLocation = locations[GeneralUtils::GenerateRandomNumber<size_t>(0, locations.size() - 1)];
|
VanityObjectLocation& newLocation = locations[GeneralUtils::GenerateRandomNumber<size_t>(0, locations.size() - 1)];
|
||||||
|
|
||||||
// try to get not the same position, but if we get the same one twice, it's fine
|
// try to get not the same position, but if we get the same one twice, it's fine
|
||||||
|
@ -8,16 +8,11 @@ void AmTeapotServer::OnUse(Entity* self, Entity* user) {
|
|||||||
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
|
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
|
||||||
if (!inventoryComponent) return;
|
if (!inventoryComponent) return;
|
||||||
|
|
||||||
auto* blueFlowerItem = inventoryComponent->FindItemByLot(BLUE_FLOWER_LEAVES, eInventoryType::ITEMS);
|
|
||||||
if (!blueFlowerItem) {
|
|
||||||
blueFlowerItem = inventoryComponent->FindItemByLot(BLUE_FLOWER_LEAVES, eInventoryType::VAULT_ITEMS);
|
|
||||||
if (!blueFlowerItem) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The client allows you to use the teapot only if you have a stack of 10 leaves in some inventory somewhere.
|
// The client allows you to use the teapot only if you have a stack of 10 leaves in some inventory somewhere.
|
||||||
if (blueFlowerItem->GetCount() >= 10) {
|
if (inventoryComponent->GetLotCountNonTransfer(BLUE_FLOWER_LEAVES, false) >= 10) {
|
||||||
blueFlowerItem->SetCount(blueFlowerItem->GetCount() - 10);
|
inventoryComponent->RemoveItem(BLUE_FLOWER_LEAVES, 10, eInventoryType::ALL);
|
||||||
inventoryComponent->AddItem(WU_S_IMAGINATION_TEA, 1);
|
inventoryComponent->AddItem(WU_S_IMAGINATION_TEA, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID());
|
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID());
|
||||||
}
|
}
|
||||||
|
@ -10,49 +10,38 @@ void NsLegoClubDoor::OnStartup(Entity* self) {
|
|||||||
self->SetVar(u"teleportString", m_TeleportString);
|
self->SetVar(u"teleportString", m_TeleportString);
|
||||||
self->SetVar(u"spawnPoint", m_SpawnPoint);
|
self->SetVar(u"spawnPoint", m_SpawnPoint);
|
||||||
|
|
||||||
args = {};
|
teleportArgs.Reset();
|
||||||
|
|
||||||
args.Insert("callbackClient", std::to_string(self->GetObjectID()));
|
teleportArgs.Insert("callbackClient", std::to_string(self->GetObjectID()));
|
||||||
args.Insert("strIdentifier", "choiceDoor");
|
teleportArgs.Insert("strIdentifier", "choiceDoor");
|
||||||
args.Insert("title", "%[UI_CHOICE_DESTINATION]");
|
teleportArgs.Insert("title", "%[UI_CHOICE_DESTINATION]");
|
||||||
|
|
||||||
AMFArrayValue* choiceOptions = args.InsertArray("options");
|
auto& choiceOptions = *teleportArgs.InsertArray("options");
|
||||||
|
|
||||||
{
|
{
|
||||||
AMFArrayValue* nsArgs = choiceOptions->PushArray();
|
auto& nsArgs = *choiceOptions.PushArray();
|
||||||
|
|
||||||
nsArgs->Insert("image", "textures/ui/zone_thumnails/Nimbus_Station.dds");
|
nsArgs.Insert("image", "textures/ui/zone_thumnails/Nimbus_Station.dds");
|
||||||
nsArgs->Insert("caption", "%[UI_CHOICE_NS]");
|
nsArgs.Insert("caption", "%[UI_CHOICE_NS]");
|
||||||
nsArgs->Insert("identifier", "zoneID_1200");
|
nsArgs.Insert("identifier", "zoneID_1200");
|
||||||
nsArgs->Insert("tooltipText", "%[UI_CHOICE_NS_HOVER]");
|
nsArgs.Insert("tooltipText", "%[UI_CHOICE_NS_HOVER]");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
AMFArrayValue* ntArgs = choiceOptions->PushArray();
|
auto& ntArgs = *choiceOptions.PushArray();
|
||||||
|
|
||||||
ntArgs->Insert("image", "textures/ui/zone_thumnails/Nexus_Tower.dds");
|
ntArgs.Insert("image", "textures/ui/zone_thumnails/Nexus_Tower.dds");
|
||||||
ntArgs->Insert("caption", "%[UI_CHOICE_NT]");
|
ntArgs.Insert("caption", "%[UI_CHOICE_NT]");
|
||||||
ntArgs->Insert("identifier", "zoneID_1900");
|
ntArgs.Insert("identifier", "zoneID_1900");
|
||||||
ntArgs->Insert("tooltipText", "%[UI_CHOICE_NT_HOVER]");
|
ntArgs.Insert("tooltipText", "%[UI_CHOICE_NT_HOVER]");
|
||||||
}
|
}
|
||||||
|
|
||||||
options = choiceOptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NsLegoClubDoor::OnUse(Entity* self, Entity* user) {
|
void NsLegoClubDoor::OnUse(Entity* self, Entity* user) {
|
||||||
auto* player = user;
|
auto* player = user;
|
||||||
|
|
||||||
if (CheckChoice(self, player)) {
|
if (CheckChoice(self, player)) {
|
||||||
AMFArrayValue multiArgs;
|
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "QueueChoiceBox", teleportArgs);
|
||||||
|
|
||||||
multiArgs.Insert("callbackClient", std::to_string(self->GetObjectID()));
|
|
||||||
multiArgs.Insert("strIdentifier", "choiceDoor");
|
|
||||||
multiArgs.Insert("title", "%[UI_CHOICE_DESTINATION]");
|
|
||||||
multiArgs.Insert("options", static_cast<AMFBaseValue*>(options));
|
|
||||||
|
|
||||||
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "QueueChoiceBox", multiArgs);
|
|
||||||
|
|
||||||
multiArgs.Remove("options", false); // We do not want the local amf to delete the options!
|
|
||||||
} else if (self->GetVar<int32_t>(u"currentZone") != m_ChoiceZoneID) {
|
} else if (self->GetVar<int32_t>(u"currentZone") != m_ChoiceZoneID) {
|
||||||
AMFArrayValue multiArgs;
|
AMFArrayValue multiArgs;
|
||||||
multiArgs.Insert("state", "Lobby");
|
multiArgs.Insert("state", "Lobby");
|
||||||
|
@ -19,6 +19,5 @@ private:
|
|||||||
std::string m_SpawnPoint = "NS_LEGO_Club";
|
std::string m_SpawnPoint = "NS_LEGO_Club";
|
||||||
std::u16string m_TeleportAnim = u"lup-teleport";
|
std::u16string m_TeleportAnim = u"lup-teleport";
|
||||||
std::u16string m_TeleportString = u"ROCKET_TOOLTIP_USE_THE_GATEWAY_TO_TRAVEL_TO_LUP_WORLD";
|
std::u16string m_TeleportString = u"ROCKET_TOOLTIP_USE_THE_GATEWAY_TO_TRAVEL_TO_LUP_WORLD";
|
||||||
AMFArrayValue args = {};
|
AMFArrayValue teleportArgs{};
|
||||||
AMFArrayValue* options = {};
|
|
||||||
};
|
};
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,6 +334,8 @@
|
|||||||
#include "AgSpiderBossMessage.h"
|
#include "AgSpiderBossMessage.h"
|
||||||
#include "GfRaceInstancer.h"
|
#include "GfRaceInstancer.h"
|
||||||
#include "NsRaceServer.h"
|
#include "NsRaceServer.h"
|
||||||
|
#include "TrialFactionArmorServer.h"
|
||||||
|
#include "ImaginationBackPack.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -700,6 +702,8 @@ namespace {
|
|||||||
{"scripts\\ai\\RACING\\TRACK_GF\\GF_RACE_SERVER.lua", []() {return new GfRaceServer();}},
|
{"scripts\\ai\\RACING\\TRACK_GF\\GF_RACE_SERVER.lua", []() {return new GfRaceServer();}},
|
||||||
{"scripts\\ai\\RACING\\TRACK_FV\\FV_RACE_SERVER.lua", []() {return new FvRaceServer();}},
|
{"scripts\\ai\\RACING\\TRACK_FV\\FV_RACE_SERVER.lua", []() {return new FvRaceServer();}},
|
||||||
{"scripts\\ai\\RACING\\OBJECTS\\VEHICLE_DEATH_TRIGGER_WATER_SERVER.lua", []() {return new VehicleDeathTriggerWaterServer();}},
|
{"scripts\\ai\\RACING\\OBJECTS\\VEHICLE_DEATH_TRIGGER_WATER_SERVER.lua", []() {return new VehicleDeathTriggerWaterServer();}},
|
||||||
|
{"scripts\\equipmenttriggers\\L_TRIAL_FACTION_ARMOR_SERVER.lua", []() {return new TrialFactionArmorServer();}},
|
||||||
|
{"scripts\\equipmenttriggers\\ImaginationBackPack.lua", []() {return new ImaginationBackPack();}},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -186,6 +186,8 @@ namespace CppScripts {
|
|||||||
*/
|
*/
|
||||||
virtual void NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {};
|
virtual void NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {};
|
||||||
|
|
||||||
|
virtual void NotifyPlayerResurrectionFinished(Entity& self, GameMessages::PlayerResurrectionFinished& msg) {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when a player has responsed to a mission.
|
* Invoked when a player has responsed to a mission.
|
||||||
*
|
*
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user