mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-01-06 08:50:14 -06:00
Compare commits
1 Commits
1058-Other
...
csr_comman
| Author | SHA1 | Date | |
|---|---|---|---|
| d77a1a92bf |
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 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Add msbuild to PATH (Windows only)
|
||||
if: ${{ matrix.os == 'windows-2022' }}
|
||||
uses: microsoft/setup-msbuild@767f00a3f09872d96a0cb9fcd5e6a4ff33311330
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
vs-version: '[17,18)'
|
||||
msbuild-architecture: x64
|
||||
@@ -30,16 +30,12 @@ jobs:
|
||||
run: |
|
||||
brew install openssl@3
|
||||
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
|
||||
uses: lukka/run-cmake@67c73a83a46f86c4e0b96b741ac37ff495478c38
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
workflowPreset: "ci-${{matrix.os}}"
|
||||
- name: artifacts
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{matrix.os}}
|
||||
path: |
|
||||
|
||||
@@ -235,8 +235,6 @@ include_directories(
|
||||
|
||||
"dNet"
|
||||
|
||||
"dWeb"
|
||||
|
||||
"tests"
|
||||
"tests/dCommonTests"
|
||||
"tests/dGameTests"
|
||||
@@ -303,7 +301,6 @@ add_subdirectory(dZoneManager)
|
||||
add_subdirectory(dNavigation)
|
||||
add_subdirectory(dPhysics)
|
||||
add_subdirectory(dServer)
|
||||
add_subdirectory(dWeb)
|
||||
|
||||
# Create a list of common libraries shared between all binaries
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "magic_enum")
|
||||
|
||||
25
Dockerfile
25
Dockerfile
@@ -11,12 +11,7 @@ COPY --chmod=0500 ./build.sh /app/
|
||||
|
||||
RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt
|
||||
|
||||
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/
|
||||
RUN ./build.sh
|
||||
|
||||
FROM debian:12 as runtime
|
||||
|
||||
@@ -28,23 +23,23 @@ RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Grab libraries and load them
|
||||
COPY --from=build /tmp/persisted-build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
|
||||
COPY --from=build /app/build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
|
||||
RUN ldconfig
|
||||
|
||||
# Server bins
|
||||
COPY --from=build /tmp/persisted-build/*Server /app/
|
||||
COPY --from=build /app/build/*Server /app/
|
||||
|
||||
# Necessary suplimentary files
|
||||
COPY --from=build /tmp/persisted-build/*.ini /app/configs/
|
||||
COPY --from=build /tmp/persisted-build/vanity/*.* /app/vanity/
|
||||
COPY --from=build /tmp/persisted-build/navmeshes /app/navmeshes
|
||||
COPY --from=build /tmp/persisted-build/migrations /app/migrations
|
||||
COPY --from=build /tmp/persisted-build/*.dcf /app/
|
||||
COPY --from=build /app/build/*.ini /app/configs/
|
||||
COPY --from=build /app/build/vanity/*.* /app/vanity/
|
||||
COPY --from=build /app/build/navmeshes /app/navmeshes
|
||||
COPY --from=build /app/build/migrations /app/migrations
|
||||
COPY --from=build /app/build/*.dcf /app/
|
||||
|
||||
# backup of config and vanity files to copy to the host incase
|
||||
# of a mount clobbering the copy from above
|
||||
COPY --from=build /tmp/persisted-build/*.ini /app/default-configs/
|
||||
COPY --from=build /tmp/persisted-build/vanity/*.* /app/default-vanity/
|
||||
COPY --from=build /app/build/*.ini /app/default-configs/
|
||||
COPY --from=build /app/build/vanity/*.* /app/default-vanity/
|
||||
|
||||
# needed as the container runs with the root user
|
||||
# and therefore sudo doesn't exist
|
||||
|
||||
@@ -78,7 +78,7 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
|
||||
|
||||
### 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.
|
||||
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>!).
|
||||
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
|
||||
|
||||
### MacOS packages
|
||||
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
|
||||
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.
|
||||
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.
|
||||
You can check your CMake version by using the following command in a terminal.
|
||||
```bash
|
||||
cmake --version
|
||||
|
||||
@@ -70,15 +70,12 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1500;
|
||||
std::string masterPassword;
|
||||
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
masterPassword = masterInfo->password;
|
||||
}
|
||||
|
||||
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
@@ -92,7 +89,7 @@ int main(int argc, char** argv) {
|
||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||
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, masterPassword);
|
||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -105,7 +105,7 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowLis
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
|
||||
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
|
||||
if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
|
||||
if (message.empty()) return { };
|
||||
if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } };
|
||||
@@ -114,7 +114,7 @@ std::set<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::str
|
||||
std::string segment;
|
||||
std::regex reg("(!*|\\?*|\\;*|\\.*|\\,*)");
|
||||
|
||||
std::set<std::pair<uint8_t, uint8_t>> listOfBadSegments;
|
||||
std::vector<std::pair<uint8_t, uint8_t>> listOfBadSegments = std::vector<std::pair<uint8_t, uint8_t>>();
|
||||
|
||||
uint32_t position = 0;
|
||||
|
||||
@@ -127,17 +127,17 @@ std::set<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::str
|
||||
size_t hash = CalculateHash(segment);
|
||||
|
||||
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) {
|
||||
listOfBadSegments.emplace(position, originalSegment.length());
|
||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||
}
|
||||
|
||||
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) {
|
||||
m_UserUnapprovedWordCache.push_back(hash);
|
||||
listOfBadSegments.emplace(position, originalSegment.length());
|
||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||
}
|
||||
|
||||
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) {
|
||||
m_UserUnapprovedWordCache.push_back(hash);
|
||||
listOfBadSegments.emplace(position, originalSegment.length());
|
||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||
}
|
||||
|
||||
position += originalSegment.length() + 1;
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
void ReadWordlistPlaintext(const std::string& filepath, bool allowList);
|
||||
bool ReadWordlistDCF(const std::string& filepath, bool allowList);
|
||||
void ExportWordlistToDCF(const std::string& filepath, bool allowList);
|
||||
std::set<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
|
||||
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
|
||||
|
||||
private:
|
||||
bool m_DontGenerateDCF;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
set(DCHATSERVER_SOURCES
|
||||
"ChatIgnoreList.cpp"
|
||||
"ChatPacketHandler.cpp"
|
||||
"ChatJSONUtils.cpp"
|
||||
"ChatWeb.cpp"
|
||||
"PlayerContainer.cpp"
|
||||
"TeamContainer.cpp"
|
||||
"ChatWebAPI.cpp"
|
||||
"JSONUtils.cpp"
|
||||
"CSRCommandHandler.cpp"
|
||||
)
|
||||
|
||||
add_executable(ChatServer "ChatServer.cpp")
|
||||
target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter" "${PROJECT_SOURCE_DIR}/dWeb")
|
||||
target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter")
|
||||
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
|
||||
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
||||
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer" "${PROJECT_SOURCE_DIR}/dChatFilter")
|
||||
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
|
||||
|
||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose dWeb)
|
||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose)
|
||||
|
||||
|
||||
353
dChatServer/CSRCommandHandler.cpp
Normal file
353
dChatServer/CSRCommandHandler.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include "CSRCommandHandler.h"
|
||||
#include "eCSRCommand.h"
|
||||
#include "eHTTPStatusCode.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "magic_enum.hpp"
|
||||
#include "json.hpp"
|
||||
|
||||
void CSRCommandHandler::HandleCSRCommand(HTTPReply& reply, const json& data) {
|
||||
auto check = JSONUtils::CheckRequiredData(data, { "command" });
|
||||
if (!check.empty()) {
|
||||
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||
reply.message = check;
|
||||
} else {
|
||||
// get command from json
|
||||
std::string command_string = data["command"];
|
||||
// upper
|
||||
std::transform(command_string.begin(), command_string.end(), command_string.begin(), ::toupper);
|
||||
eCSRCommand command = magic_enum::enum_cast<eCSRCommand>(command_string).value_or(eCSRCommand::INVALID);
|
||||
|
||||
switch (command) {
|
||||
case eCSRCommand::QUERY_SERVER_STATUS:
|
||||
QueryServerStatus(reply, data);
|
||||
break;
|
||||
case eCSRCommand::QUERY_CHARACTER_LOCATION:
|
||||
QueryCharacterLocation(reply, data);
|
||||
break;
|
||||
case eCSRCommand::QUERY_CHARACTER_ONLINE_STATUS:
|
||||
QueryCharacterOnlineStatus(reply, data);
|
||||
break;
|
||||
case eCSRCommand::INVENTORY_ADD_ITEM:
|
||||
InventoryAddItem(reply, data);
|
||||
break;
|
||||
case eCSRCommand::INVENTORY_DELETE_ITEM:
|
||||
InventoryDeleteItem(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_MUTE_ACCOUNT:
|
||||
ModerateMuteAccount(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_BAN_ACCOUNT:
|
||||
ModerateBanAccount(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_EDUCATE_CHARACTER:
|
||||
ModerateEducateCharacter(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_KICK_CHARACTER:
|
||||
ModerateKickCharacter(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_WARN_CHARACTER:
|
||||
ModerateWarnCharacter(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_RENAME_CHARACTER:
|
||||
ModerateRenameCharacter(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_DELETE_CHARACTER_FRIEND:
|
||||
ModerateDeleteCharacterFriend(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_KILL_CHARACTER:
|
||||
ModerateKillCharacter(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_HEALTH:
|
||||
UpdateCharacterHealth(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_ARMOR:
|
||||
UpdateCharacterArmor(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_IMAGINATION:
|
||||
UpdateCharacterImagination(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_MAX_HEALTH:
|
||||
UpdateCharacterMaxHealth(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_MAX_ARMOR:
|
||||
UpdateCharacterMaxArmor(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_MAX_IMAGINATION:
|
||||
UpdateCharacterMaxImagination(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_CURRENCY:
|
||||
UpdateCharacterCurrency(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_REPUTATION:
|
||||
UpdateCharacterReputation(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_LEGO_SCORE:
|
||||
UpdateCharacterLegoScore(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_EMOTES:
|
||||
UpdateCharacterEmotes(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_ADD_ACHIEVEMENT:
|
||||
UpdateCharacterAddAchievement(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_COMPLETE_ACHIEVEMENT:
|
||||
UpdateCharacterCompleteAchievement(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_REMOVE_ACHIEVEMENT:
|
||||
UpdateCharacterRemoveAchievement(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_POSITION_OFFLINE:
|
||||
UpdateCharacterPositionOffline(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UPDATE_CHARACTER_INV_SLOT_AMOUNT:
|
||||
UpdateCharacterInvSlotAmount(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UTILITY_SAVE_CHARACTER:
|
||||
UtilitySaveCharacter(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UTILITY_SEND_MAIL:
|
||||
UtilitySendMail(reply, data);
|
||||
break;
|
||||
case eCSRCommand::UTILITY_GIVE_ITEM_TO_ALL_PLAYERS_ONLINE:
|
||||
UtilityGiveItemToAllPlayersOnline(reply, data);
|
||||
break;
|
||||
case eCSRCommand::METRICS_CONFIGURE:
|
||||
MetricsConfigure(reply, data);
|
||||
break;
|
||||
case eCSRCommand::DISABLE_ZONE:
|
||||
DisableZone(reply, data);
|
||||
break;
|
||||
case eCSRCommand::INIT_DONATION_AMOUNT:
|
||||
InitDonationAmount(reply, data);
|
||||
break;
|
||||
case eCSRCommand::KILL_SERVERS_COUNTDOWN:
|
||||
KillServersCountdown(reply, data);
|
||||
break;
|
||||
case eCSRCommand::DISABLE_FAQ:
|
||||
DisableFAQ(reply, data);
|
||||
break;
|
||||
case eCSRCommand::THROTTLEQUEUE:
|
||||
ThrottleQueue(reply, data);
|
||||
break;
|
||||
case eCSRCommand::GATEGM_ACCESS:
|
||||
GateGMAccess(reply, data);
|
||||
break;
|
||||
case eCSRCommand::RECONNECT_CRISP:
|
||||
ReconnectCrisp(reply, data);
|
||||
break;
|
||||
case eCSRCommand::MODERATE_KICK_ACCOUNT:
|
||||
ModerateKickAccount(reply, data);
|
||||
break;
|
||||
case eCSRCommand::TOGGLE_CRISP_SERVER:
|
||||
ToggleCrispServer(reply, data);
|
||||
break;
|
||||
case eCSRCommand::QUICK_DRAIN_SERVER:
|
||||
QuickDrainServer(reply, data);
|
||||
break;
|
||||
case eCSRCommand::QUICK_DRAIN_SERVER_RENEW:
|
||||
QuickDrainServerRenew(reply, data);
|
||||
break;
|
||||
case eCSRCommand::REPLICATE_CHARACTER:
|
||||
ReplicateCharacter(reply, data);
|
||||
break;
|
||||
case eCSRCommand::GET_SERVER_STATUS:
|
||||
GetServerStatus(reply);
|
||||
break;
|
||||
case eCSRCommand::RELOAD_SERVER_INIS:
|
||||
ReloadServerINIs(reply);
|
||||
break;
|
||||
case eCSRCommand::INVALID:
|
||||
default:
|
||||
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||
reply.message = "{\"error\":\"Invalid Command\"}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSRCommandHandler::QueryServerStatus(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::QueryCharacterLocation(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::QueryCharacterOnlineStatus(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::InventoryAddItem(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::InventoryDeleteItem(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateMuteAccount(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateBanAccount(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateEducateCharacter(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateKickCharacter(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateWarnCharacter(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateRenameCharacter(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateDeleteCharacterFriend(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateKillCharacter(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterHealth(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterArmor(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterImagination(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterMaxHealth(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterMaxArmor(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterMaxImagination(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterCurrency(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterReputation(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterLegoScore(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterEmotes(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterAddAchievement(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterCompleteAchievement(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterRemoveAchievement(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterPositionOffline(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UpdateCharacterInvSlotAmount(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UtilitySaveCharacter(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UtilitySendMail(HTTPReply& reply, const json& data) {
|
||||
|
||||
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::UtilityGiveItemToAllPlayersOnline(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::MetricsConfigure(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::DisableZone(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::InitDonationAmount(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::KillServersCountdown(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::DisableFAQ(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ThrottleQueue(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::GateGMAccess(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ReconnectCrisp(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ModerateKickAccount(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ToggleCrispServer(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::QuickDrainServer(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::QuickDrainServerRenew(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ReplicateCharacter(HTTPReply& reply, const json& data) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::GetServerStatus(HTTPReply& reply) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
void CSRCommandHandler::ReloadServerINIs(HTTPReply& reply) {
|
||||
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
|
||||
reply.message = "{\"error\":\"Not Implemented\"}";
|
||||
}
|
||||
62
dChatServer/CSRCommandHandler.h
Normal file
62
dChatServer/CSRCommandHandler.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef __CSR_COMMAND_HANDLER__H__
|
||||
#define __CSR_COMMAND_HANDLER__H__
|
||||
|
||||
#include <optional>
|
||||
#include "json_fwd.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
struct HTTPReply;
|
||||
|
||||
|
||||
namespace CSRCommandHandler {
|
||||
void HandleCSRCommand(HTTPReply& reply, const json& data);
|
||||
|
||||
void QueryServerStatus(HTTPReply& reply, const json& data);
|
||||
void QueryCharacterLocation(HTTPReply& reply, const json& data);
|
||||
void QueryCharacterOnlineStatus(HTTPReply& reply, const json& data);
|
||||
void InventoryAddItem(HTTPReply& reply, const json& data);
|
||||
void InventoryDeleteItem(HTTPReply& reply, const json& data);
|
||||
void ModerateMuteAccount(HTTPReply& reply, const json& data);
|
||||
void ModerateBanAccount(HTTPReply& reply, const json& data);
|
||||
void ModerateEducateCharacter(HTTPReply& reply, const json& data);
|
||||
void ModerateKickCharacter(HTTPReply& reply, const json& data);
|
||||
void ModerateWarnCharacter(HTTPReply& reply, const json& data);
|
||||
void ModerateRenameCharacter(HTTPReply& reply, const json& data);
|
||||
void ModerateDeleteCharacterFriend(HTTPReply& reply, const json& data);
|
||||
void ModerateKillCharacter(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterHealth(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterArmor(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterImagination(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterMaxHealth(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterMaxArmor(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterMaxImagination(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterCurrency(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterReputation(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterLegoScore(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterEmotes(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterAddAchievement(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterCompleteAchievement(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterRemoveAchievement(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterPositionOffline(HTTPReply& reply, const json& data);
|
||||
void UpdateCharacterInvSlotAmount(HTTPReply& reply, const json& data);
|
||||
void UtilitySaveCharacter(HTTPReply& reply, const json& data);
|
||||
void UtilitySendMail(HTTPReply& reply, const json& data);
|
||||
void UtilityGiveItemToAllPlayersOnline(HTTPReply& reply, const json& data);
|
||||
void MetricsConfigure(HTTPReply& reply, const json& data);
|
||||
void DisableZone(HTTPReply& reply, const json& data);
|
||||
void InitDonationAmount(HTTPReply& reply, const json& data);
|
||||
void KillServersCountdown(HTTPReply& reply, const json& data);
|
||||
void DisableFAQ(HTTPReply& reply, const json& data);
|
||||
void ThrottleQueue(HTTPReply& reply, const json& data);
|
||||
void GateGMAccess(HTTPReply& reply, const json& data);
|
||||
void ReconnectCrisp(HTTPReply& reply, const json& data);
|
||||
void ModerateKickAccount(HTTPReply& reply, const json& data);
|
||||
void ToggleCrispServer(HTTPReply& reply, const json& data);
|
||||
void QuickDrainServer(HTTPReply& reply, const json& data);
|
||||
void QuickDrainServerRenew(HTTPReply& reply, const json& data);
|
||||
void ReplicateCharacter(HTTPReply& reply, const json& data);
|
||||
void GetServerStatus(HTTPReply& reply);
|
||||
void ReloadServerINIs(HTTPReply& reply);
|
||||
};
|
||||
|
||||
#endif // !__CSR_COMMAND_HANDLER__H__
|
||||
@@ -12,8 +12,8 @@
|
||||
// not allowing teams, rejecting DMs, friends requets etc.
|
||||
// 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 MessageType::Client type) {
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receivingPlayer);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -48,9 +48,9 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) {
|
||||
}
|
||||
|
||||
CBITSTREAM;
|
||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::GET_IGNORE_LIST_RESPONSE);
|
||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::GET_IGNORE);
|
||||
|
||||
bitStream.Write<uint8_t>(false); // Is Free Trial, but we don't care about that
|
||||
bitStream.Write<uint8_t>(false); // Probably is 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>(receiver.ignoredPlayers.size());
|
||||
@@ -86,7 +86,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
|
||||
std::string toIgnoreStr = toIgnoreName.GetAsString();
|
||||
|
||||
CBITSTREAM;
|
||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::ADD_IGNORE_RESPONSE);
|
||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::ADD_IGNORE);
|
||||
|
||||
// Check if the player exists
|
||||
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
|
||||
@@ -161,7 +161,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) {
|
||||
receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end());
|
||||
|
||||
CBITSTREAM;
|
||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::REMOVE_IGNORE_RESPONSE);
|
||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::REMOVE_IGNORE);
|
||||
|
||||
bitStream.Write<int8_t>(0);
|
||||
LUWString playerNameSend(removedIgnoreStr, 33);
|
||||
|
||||
@@ -5,16 +5,17 @@ struct Packet;
|
||||
|
||||
#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 {
|
||||
void GetIgnoreList(Packet* packet);
|
||||
void AddIgnore(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 {
|
||||
SUCCESS,
|
||||
ALREADY_IGNORED,
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef __CHATJSONUTILS_H__
|
||||
#define __CHATJSONUTILS_H__
|
||||
|
||||
#include "json_fwd.hpp"
|
||||
#include "PlayerContainer.h"
|
||||
#include "TeamContainer.h"
|
||||
|
||||
/* Remember, to_json needs to be in the same namespace as the class its located in */
|
||||
|
||||
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 TeamData& teamData);
|
||||
|
||||
namespace TeamContainer {
|
||||
void to_json(nlohmann::json& data, const TeamContainer::Data& teamData);
|
||||
};
|
||||
|
||||
#endif // !__CHATJSONUTILS_H__
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "StringifiedEnum.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "TeamContainer.h"
|
||||
|
||||
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
//Get from the packet which player we want to do something with:
|
||||
@@ -30,33 +29,35 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
auto& player = Game::playerContainer.GetPlayerDataMutable(playerID);
|
||||
if (!player) return;
|
||||
|
||||
auto friendsList = Database::Get()->GetFriendsList(playerID);
|
||||
for (const auto& friendData : friendsList) {
|
||||
FriendData fd;
|
||||
fd.isFTP = false; // not a thing in DLU
|
||||
fd.friendID = friendData.friendID;
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
|
||||
if (player.friends.empty()) {
|
||||
auto friendsList = Database::Get()->GetFriendsList(playerID);
|
||||
for (const auto& friendData : friendsList) {
|
||||
FriendData fd;
|
||||
fd.isFTP = false; // not a thing in DLU
|
||||
fd.friendID = friendData.friendID;
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
|
||||
|
||||
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||
if (fd.isBestFriend) player.countOfBestFriends += 1;
|
||||
fd.friendName = friendData.friendName;
|
||||
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||
if (fd.isBestFriend) player.countOfBestFriends += 1;
|
||||
fd.friendName = friendData.friendName;
|
||||
|
||||
//Now check if they're online:
|
||||
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
|
||||
//Now check if they're online:
|
||||
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
|
||||
|
||||
if (fr) {
|
||||
fd.isOnline = true;
|
||||
fd.zoneID = fr.zoneID;
|
||||
if (fr) {
|
||||
fd.isOnline = true;
|
||||
fd.zoneID = fr.zoneID;
|
||||
|
||||
//Since this friend is online, we need to update them on the fact that we've just logged in:
|
||||
if (player.isLogin) SendFriendUpdate(fr, player, 1, fd.isBestFriend);
|
||||
} else {
|
||||
fd.isOnline = false;
|
||||
fd.zoneID = LWOZONEID();
|
||||
//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);
|
||||
} else {
|
||||
fd.isOnline = false;
|
||||
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:
|
||||
@@ -74,7 +75,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
data.Serialize(bitStream);
|
||||
}
|
||||
|
||||
SystemAddress sysAddr = player.worldServerSysAddr;
|
||||
SystemAddress sysAddr = player.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -104,8 +105,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
return;
|
||||
};
|
||||
|
||||
// Intentional copy
|
||||
PlayerData requestee = Game::playerContainer.GetPlayerData(playerName);
|
||||
auto& requestee = Game::playerContainer.GetPlayerDataMutable(playerName);
|
||||
|
||||
// Check if player is online first
|
||||
if (isBestFriendRequest && !requestee) {
|
||||
@@ -123,7 +123,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
requesteeFriendData.isOnline = false;
|
||||
requesteeFriendData.zoneID = requestor.zoneID;
|
||||
requestee.friends.push_back(requesteeFriendData);
|
||||
requestee.worldServerSysAddr = UNASSIGNED_SYSTEM_ADDRESS;
|
||||
requestee.sysAddr = UNASSIGNED_SYSTEM_ADDRESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -190,29 +190,24 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus);
|
||||
// Sent the best friend update here if the value is 3
|
||||
if (bestFriendStatus == 3U) {
|
||||
if (requestee.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true);
|
||||
if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true);
|
||||
|
||||
requestee.countOfBestFriends += 1;
|
||||
requestor.countOfBestFriends += 1;
|
||||
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) {
|
||||
if (friendData.friendID == requestee.playerID) {
|
||||
friendData.isBestFriend = true;
|
||||
}
|
||||
}
|
||||
requestor.countOfBestFriends += 1;
|
||||
|
||||
auto& toModify = Game::playerContainer.GetPlayerDataMutable(playerName);
|
||||
if (toModify) {
|
||||
for (auto& friendData : toModify.friends) {
|
||||
if (friendData.friendID == requestor.playerID) {
|
||||
friendData.isBestFriend = true;
|
||||
}
|
||||
for (auto& friendData : requestee.friends) {
|
||||
if (friendData.friendID == requestor.playerID) {
|
||||
friendData.isBestFriend = true;
|
||||
}
|
||||
toModify.countOfBestFriends += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true);
|
||||
if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true);
|
||||
}
|
||||
} else {
|
||||
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
|
||||
@@ -385,7 +380,7 @@ void ChatPacketHandler::HandleWho(Packet* packet) {
|
||||
bitStream.Write(player.zoneID.GetCloneID());
|
||||
bitStream.Write(request.playerName);
|
||||
|
||||
SystemAddress sysAddr = sender.worldServerSysAddr;
|
||||
SystemAddress sysAddr = sender.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -419,7 +414,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
||||
}
|
||||
}
|
||||
}
|
||||
SystemAddress sysAddr = sender.worldServerSysAddr;
|
||||
SystemAddress sysAddr = sender.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -448,7 +443,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
|
||||
switch (channel) {
|
||||
case eChatChannel::TEAM: {
|
||||
auto* team = TeamContainer::GetTeam(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
if (team == nullptr) return;
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
@@ -520,28 +515,6 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
||||
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) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
@@ -560,7 +533,387 @@ void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const P
|
||||
bitStream.Write(responseCode);
|
||||
bitStream.Write(message);
|
||||
|
||||
SystemAddress sysAddr = routeTo.worldServerSysAddr;
|
||||
SystemAddress sysAddr = routeTo.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
|
||||
void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOOBJID playerID;
|
||||
LUWString invitedPlayer;
|
||||
|
||||
inStream.Read(playerID);
|
||||
inStream.IgnoreBytes(4);
|
||||
inStream.Read(invitedPlayer);
|
||||
|
||||
const auto& player = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (!player) return;
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team == nullptr) {
|
||||
team = Game::playerContainer.CreateTeam(playerID);
|
||||
}
|
||||
|
||||
const auto& other = Game::playerContainer.GetPlayerData(invitedPlayer.GetAsString());
|
||||
|
||||
if (!other) return;
|
||||
|
||||
if (Game::playerContainer.GetTeam(other.playerID) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (team->memberIDs.size() > 3) {
|
||||
// no more teams greater than 4
|
||||
|
||||
LOG("Someone tried to invite a 5th player to a team");
|
||||
return;
|
||||
}
|
||||
|
||||
SendTeamInvite(other, player);
|
||||
|
||||
LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.GetAsString().c_str());
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
char declined = 0;
|
||||
inStream.Read(declined);
|
||||
LWOOBJID leaderID = LWOOBJID_EMPTY;
|
||||
inStream.Read(leaderID);
|
||||
|
||||
LOG("Accepted invite: %llu -> %llu (%d)", playerID, leaderID, declined);
|
||||
|
||||
if (declined) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(leaderID);
|
||||
|
||||
if (team == nullptr) {
|
||||
LOG("Failed to find team for leader (%llu)", leaderID);
|
||||
|
||||
team = Game::playerContainer.GetTeam(playerID);
|
||||
}
|
||||
|
||||
if (team == nullptr) {
|
||||
LOG("Failed to find team for player (%llu)", playerID);
|
||||
return;
|
||||
}
|
||||
|
||||
Game::playerContainer.AddMember(team, playerID);
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
LOG("(%llu) leaving team", playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
Game::playerContainer.RemoveMember(team, playerID, false, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamKick(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
LUWString kickedPlayer;
|
||||
|
||||
inStream.Read(playerID);
|
||||
inStream.IgnoreBytes(4);
|
||||
inStream.Read(kickedPlayer);
|
||||
|
||||
|
||||
LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.GetAsString().c_str());
|
||||
|
||||
const auto& kicked = Game::playerContainer.GetPlayerData(kickedPlayer.GetAsString());
|
||||
|
||||
LWOOBJID kickedId = LWOOBJID_EMPTY;
|
||||
|
||||
if (kicked) {
|
||||
kickedId = kicked.playerID;
|
||||
} else {
|
||||
kickedId = Game::playerContainer.GetId(kickedPlayer.string);
|
||||
}
|
||||
|
||||
if (kickedId == LWOOBJID_EMPTY) return;
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID || team->leaderID == kickedId) return;
|
||||
|
||||
Game::playerContainer.RemoveMember(team, kickedId, false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamPromote(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
LUWString promotedPlayer;
|
||||
|
||||
inStream.Read(playerID);
|
||||
inStream.IgnoreBytes(4);
|
||||
inStream.Read(promotedPlayer);
|
||||
|
||||
LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.GetAsString().c_str());
|
||||
|
||||
const auto& promoted = Game::playerContainer.GetPlayerData(promotedPlayer.GetAsString());
|
||||
|
||||
if (!promoted) return;
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
Game::playerContainer.PromoteMember(team, promoted.playerID);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamLootOption(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
|
||||
char option;
|
||||
inStream.Read(option);
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
team->lootFlag = option;
|
||||
|
||||
Game::playerContainer.TeamStatusUpdate(team);
|
||||
|
||||
Game::playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
const auto& data = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (team != nullptr && data) {
|
||||
if (team->local && data.zoneID.GetMapID() != team->zoneId.GetMapID() && data.zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
|
||||
Game::playerContainer.RemoveMember(team, playerID, false, false, true, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (team->memberIDs.size() <= 1 && !team->local) {
|
||||
Game::playerContainer.DisbandTeam(team);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!team->local) {
|
||||
ChatPacketHandler::SendTeamSetLeader(data, team->leaderID);
|
||||
} else {
|
||||
ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY);
|
||||
}
|
||||
|
||||
Game::playerContainer.TeamStatusUpdate(team);
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (memberId == playerID) continue;
|
||||
|
||||
const auto memberName = Game::playerContainer.GetName(memberId);
|
||||
|
||||
if (otherMember) {
|
||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data.playerID, data.zoneID);
|
||||
}
|
||||
ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
|
||||
}
|
||||
|
||||
Game::playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::TEAM_INVITE);
|
||||
|
||||
bitStream.Write(LUWString(sender.playerName.c_str()));
|
||||
bitStream.Write(sender.playerID);
|
||||
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
|
||||
|
||||
bitStream.Write(bLeaderIsFreeTrial);
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64LeaderZoneID);
|
||||
bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
|
||||
bitStream.Write(ucLootFlag);
|
||||
bitStream.Write(ucNumOfOtherPlayers);
|
||||
bitStream.Write(ucResponseCode);
|
||||
bitStream.Write<uint32_t>(wsLeaderName.size());
|
||||
for (const auto character : wsLeaderName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
|
||||
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64LeaderZoneID);
|
||||
bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
|
||||
bitStream.Write(ucLootFlag);
|
||||
bitStream.Write(ucNumOfOtherPlayers);
|
||||
bitStream.Write<uint32_t>(wsLeaderName.size());
|
||||
for (const auto character : wsLeaderName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
|
||||
|
||||
bitStream.Write(i64PlayerID);
|
||||
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
|
||||
|
||||
bitStream.Write(bIsFreeTrial);
|
||||
bitStream.Write(bLocal);
|
||||
bitStream.Write(bNoLootOnDeath);
|
||||
bitStream.Write(i64PlayerID);
|
||||
bitStream.Write<uint32_t>(wsPlayerName.size());
|
||||
for (const auto character : wsPlayerName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
bitStream.Write1();
|
||||
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
|
||||
zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
|
||||
}
|
||||
bitStream.Write(zoneID);
|
||||
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
|
||||
|
||||
bitStream.Write(bDisband);
|
||||
bitStream.Write(bIsKicked);
|
||||
bitStream.Write(bIsLeaving);
|
||||
bitStream.Write(bLocal);
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64PlayerID);
|
||||
bitStream.Write<uint32_t>(wsPlayerName.size());
|
||||
for (const auto character : wsPlayerName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
|
||||
|
||||
bitStream.Write(i64PlayerID);
|
||||
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
|
||||
zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
|
||||
}
|
||||
bitStream.Write(zoneID);
|
||||
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -602,7 +955,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
|
||||
bitStream.Write<uint8_t>(isBestFriend); //isBFF
|
||||
bitStream.Write<uint8_t>(0); //isFTP
|
||||
|
||||
SystemAddress sysAddr = friendData.worldServerSysAddr;
|
||||
SystemAddress sysAddr = friendData.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -624,7 +977,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
|
||||
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.
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -637,7 +990,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE);
|
||||
bitStream.Write(responseCode);
|
||||
// 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.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS);
|
||||
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
|
||||
// Then write the player name
|
||||
bitStream.Write(LUWString(sender.playerName));
|
||||
// Then if this is an acceptance code, write the following extra info.
|
||||
@@ -647,7 +1000,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
|
||||
bitStream.Write(isBestFriendRequest); //isBFF
|
||||
bitStream.Write<uint8_t>(0); //isFTP
|
||||
}
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -661,6 +1014,6 @@ void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string
|
||||
bitStream.Write<uint8_t>(isSuccessful); //isOnline
|
||||
bitStream.Write(LUWString(personToRemove));
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SystemAddress sysAddr = receiver.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -35,13 +35,13 @@ enum class eChatChannel : uint8_t {
|
||||
|
||||
|
||||
enum class eChatMessageResponseCode : uint8_t {
|
||||
SENT = 0,
|
||||
NOTONLINE,
|
||||
GENERALERROR,
|
||||
RECEIVEDNEWWHISPER,
|
||||
NOTFRIENDS,
|
||||
SENDERFREETRIAL,
|
||||
RECEIVERFREETRIAL,
|
||||
SENT = 0,
|
||||
NOTONLINE,
|
||||
GENERALERROR,
|
||||
RECEIVEDNEWWHISPER,
|
||||
NOTFRIENDS,
|
||||
SENDERFREETRIAL,
|
||||
RECEIVERFREETRIAL,
|
||||
};
|
||||
|
||||
namespace ChatPacketHandler {
|
||||
@@ -52,14 +52,30 @@ namespace ChatPacketHandler {
|
||||
void HandleGMLevelUpdate(Packet* packet);
|
||||
void HandleWho(Packet* packet);
|
||||
void HandleShowAll(Packet* packet);
|
||||
|
||||
void HandleChatMessage(Packet* packet);
|
||||
void HandlePrivateChatMessage(Packet* packet);
|
||||
void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode);
|
||||
|
||||
void OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr);
|
||||
void HandleTeamInvite(Packet* packet);
|
||||
void HandleTeamInviteResponse(Packet* packet);
|
||||
void HandleTeamLeave(Packet* packet);
|
||||
void HandleTeamKick(Packet* packet);
|
||||
void HandleTeamPromote(Packet* packet);
|
||||
void HandleTeamLootOption(Packet* packet);
|
||||
void HandleTeamStatusRequest(Packet* packet);
|
||||
|
||||
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 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 SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
|
||||
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);
|
||||
|
||||
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
|
||||
void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend);
|
||||
void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode);
|
||||
|
||||
void SendFriendRequest(const PlayerData& receiver, const PlayerData& sender);
|
||||
void SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
|
||||
void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "MessageType/World.h"
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "TeamContainer.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Server.h"
|
||||
@@ -29,7 +28,7 @@
|
||||
#include "RakNetDefines.h"
|
||||
#include "MessageIdentifiers.h"
|
||||
|
||||
#include "ChatWeb.h"
|
||||
#include "ChatWebAPI.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
@@ -77,8 +76,7 @@ int main(int argc, char** argv) {
|
||||
Game::assetManager = new AssetManager(clientPath);
|
||||
} catch (std::runtime_error& ex) {
|
||||
LOG("Got an error while setting up assets: %s", ex.what());
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -88,33 +86,18 @@ int main(int argc, char** argv) {
|
||||
} catch (std::exception& ex) {
|
||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("ChatServer");
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// setup the chat api web server
|
||||
const uint32_t web_server_port = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("web_server_port")).value_or(2005);
|
||||
if (Game::config->GetValue("web_server_enabled") == "1" && !Game::web.Startup("localhost", web_server_port)) {
|
||||
// if we want the web server 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;
|
||||
}
|
||||
|
||||
if (Game::web.IsEnabled()) ChatWeb::RegisterRoutes();
|
||||
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1000;
|
||||
std::string masterPassword;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
masterPassword = masterInfo->password;
|
||||
}
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
std::string ourIP = "localhost";
|
||||
@@ -123,7 +106,7 @@ int main(int argc, char** argv) {
|
||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||
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, masterPassword);
|
||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
|
||||
|
||||
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);
|
||||
@@ -141,6 +124,11 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceMasterDisconnect = 0;
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
|
||||
bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
|
||||
ChatWebAPI chatwebapi;
|
||||
if (web_server_enabled) chatwebapi.Listen();
|
||||
|
||||
auto lastTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
Game::logger->Flush(); // once immediately before main loop
|
||||
@@ -168,8 +156,10 @@ int main(int argc, char** argv) {
|
||||
packet = nullptr;
|
||||
}
|
||||
|
||||
// Check and handle web requests:
|
||||
if (Game::web.IsEnabled()) Game::web.ReceiveRequests();
|
||||
//Check and handle web requests:
|
||||
if (web_server_enabled) {
|
||||
chatwebapi.ReceiveRequests();
|
||||
}
|
||||
|
||||
//Push our log every 30s:
|
||||
if (framesSinceLastFlush >= logFlushTime) {
|
||||
@@ -197,7 +187,6 @@ int main(int argc, char** argv) {
|
||||
std::this_thread::sleep_until(t);
|
||||
}
|
||||
Game::playerContainer.Shutdown();
|
||||
TeamContainer::Shutdown();
|
||||
//Delete our objects here:
|
||||
Database::Destroy("ChatServer");
|
||||
delete Game::server;
|
||||
@@ -225,17 +214,13 @@ void HandlePacket(Packet* packet) {
|
||||
if (connection != eConnectionType::CHAT) return;
|
||||
inStream.Read(chatMessageID);
|
||||
|
||||
// Our packing byte wasnt there? Probably a false packet
|
||||
if (inStream.GetNumberOfUnreadBits() < 8) return;
|
||||
inStream.IgnoreBytes(1);
|
||||
|
||||
switch (chatMessageID) {
|
||||
case MessageType::Chat::GM_MUTE:
|
||||
Game::playerContainer.MuteUpdate(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::CREATE_TEAM:
|
||||
TeamContainer::CreateTeamServer(packet);
|
||||
Game::playerContainer.CreateTeamServer(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::GET_FRIENDS_LIST:
|
||||
@@ -255,7 +240,7 @@ void HandlePacket(Packet* packet) {
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_GET_STATUS:
|
||||
TeamContainer::HandleTeamStatusRequest(packet);
|
||||
ChatPacketHandler::HandleTeamStatusRequest(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::ADD_FRIEND_REQUEST:
|
||||
@@ -285,27 +270,27 @@ void HandlePacket(Packet* packet) {
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_INVITE:
|
||||
TeamContainer::HandleTeamInvite(packet);
|
||||
ChatPacketHandler::HandleTeamInvite(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_INVITE_RESPONSE:
|
||||
TeamContainer::HandleTeamInviteResponse(packet);
|
||||
ChatPacketHandler::HandleTeamInviteResponse(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_LEAVE:
|
||||
TeamContainer::HandleTeamLeave(packet);
|
||||
ChatPacketHandler::HandleTeamLeave(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_SET_LEADER:
|
||||
TeamContainer::HandleTeamPromote(packet);
|
||||
ChatPacketHandler::HandleTeamPromote(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_KICK:
|
||||
TeamContainer::HandleTeamKick(packet);
|
||||
ChatPacketHandler::HandleTeamKick(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_SET_LOOT:
|
||||
TeamContainer::HandleTeamLootOption(packet);
|
||||
ChatPacketHandler::HandleTeamLootOption(packet);
|
||||
break;
|
||||
case MessageType::Chat::GMLEVEL_UPDATE:
|
||||
ChatPacketHandler::HandleGMLevelUpdate(packet);
|
||||
@@ -327,9 +312,6 @@ void HandlePacket(Packet* packet) {
|
||||
case MessageType::Chat::SHOW_ALL:
|
||||
ChatPacketHandler::HandleShowAll(packet);
|
||||
break;
|
||||
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
|
||||
ChatPacketHandler::OnAchievementNotify(inStream, packet->systemAddress);
|
||||
break;
|
||||
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
|
||||
case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
|
||||
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
|
||||
@@ -365,6 +347,7 @@ void HandlePacket(Packet* packet) {
|
||||
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
||||
case MessageType::Chat::UGCC_REQUEST:
|
||||
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
||||
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
|
||||
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
||||
case MessageType::Chat::PLAYER_READY:
|
||||
case MessageType::Chat::GET_DONATION_TOTAL:
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
#include "ChatWeb.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 "GeneralUtils.h"
|
||||
#include "eHTTPMethod.h"
|
||||
#include "magic_enum.hpp"
|
||||
#include "ChatPackets.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "Database.h"
|
||||
#include "ChatJSONUtils.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "dChatFilter.h"
|
||||
#include "TeamContainer.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
void HandleHTTPPlayersRequest(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 HandleHTTPTeamsRequest(HTTPReply& reply, std::string body) {
|
||||
const json data = TeamContainer::GetTeamContainer();
|
||||
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
|
||||
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
|
||||
}
|
||||
|
||||
void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) {
|
||||
auto data = GeneralUtils::TryParse<json>(body);
|
||||
if (!data) {
|
||||
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||
reply.message = "{\"error\":\"Invalid JSON\"}";
|
||||
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.Broadcast();
|
||||
|
||||
reply.status = eHTTPStatusCode::OK;
|
||||
reply.message = "{\"status\":\"Announcement Sent\"}";
|
||||
}
|
||||
}
|
||||
|
||||
void HandleWSChat(mg_connection* connection, json data) {
|
||||
auto check = JSONUtils::CheckRequiredData(data, { "user", "message", "gmlevel", "zone" });
|
||||
if (!check.empty()) {
|
||||
LOG_DEBUG("Received invalid websocket message: %s", check.c_str());
|
||||
} else {
|
||||
const auto user = data["user"].get<std::string>();
|
||||
const auto message = data["message"].get<std::string>();
|
||||
const auto gmlevel = GeneralUtils::TryParse<eGameMasterLevel>(data["gmlevel"].get<std::string>()).value_or(eGameMasterLevel::CIVILIAN);
|
||||
const auto zone = data["zone"].get<uint32_t>();
|
||||
|
||||
const auto filter_check = Game::chatFilter->IsSentenceOkay(message, gmlevel);
|
||||
if (!filter_check.empty()) {
|
||||
LOG_DEBUG("Chat message \"%s\" from %s was not allowed", message.c_str(), user.c_str());
|
||||
data["error"] = "Chat message blocked by filter";
|
||||
data["filtered"] = json::array();
|
||||
for (const auto& [start, len] : filter_check) {
|
||||
data["filtered"].push_back(message.substr(start, len));
|
||||
}
|
||||
mg_ws_send(connection, data.dump().c_str(), data.dump().size(), WEBSOCKET_OP_TEXT);
|
||||
return;
|
||||
}
|
||||
LOG("%s: %s", user.c_str(), message.c_str());
|
||||
|
||||
// TODO: Implement chat message handling from websocket message
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace ChatWeb {
|
||||
void RegisterRoutes() {
|
||||
|
||||
// REST API v1 routes
|
||||
|
||||
std::string v1_route = "/api/v1/";
|
||||
Game::web.RegisterHTTPRoute({
|
||||
.path = v1_route + "players",
|
||||
.method = eHTTPMethod::GET,
|
||||
.handle = HandleHTTPPlayersRequest
|
||||
});
|
||||
|
||||
Game::web.RegisterHTTPRoute({
|
||||
.path = v1_route + "teams",
|
||||
.method = eHTTPMethod::GET,
|
||||
.handle = HandleHTTPTeamsRequest
|
||||
});
|
||||
|
||||
Game::web.RegisterHTTPRoute({
|
||||
.path = v1_route + "announce",
|
||||
.method = eHTTPMethod::POST,
|
||||
.handle = HandleHTTPAnnounceRequest
|
||||
});
|
||||
|
||||
// WebSocket Events Handlers
|
||||
|
||||
// Game::web.RegisterWSEvent({
|
||||
// .name = "chat",
|
||||
// .handle = HandleWSChat
|
||||
// });
|
||||
|
||||
// WebSocket subscriptions
|
||||
|
||||
Game::web.RegisterWSSubscription("player");
|
||||
}
|
||||
|
||||
void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType) {
|
||||
json data;
|
||||
data["player_data"] = player;
|
||||
data["update_type"] = magic_enum::enum_name(activityType);
|
||||
Game::web.SendWSMessage("player", data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef __CHATWEB_H__
|
||||
#define __CHATWEB_H__
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "Web.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "IActivityLog.h"
|
||||
#include "ChatPacketHandler.h"
|
||||
|
||||
namespace ChatWeb {
|
||||
void RegisterRoutes();
|
||||
void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType);
|
||||
};
|
||||
|
||||
|
||||
#endif // __CHATWEB_H__
|
||||
|
||||
209
dChatServer/ChatWebAPI.cpp
Normal file
209
dChatServer/ChatWebAPI.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#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 "GeneralUtils.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "eHTTPMethod.h"
|
||||
#include "eHTTPStatusCode.h"
|
||||
#include "magic_enum.hpp"
|
||||
#include "ChatPackets.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "eCSRCommand.h"
|
||||
#include "CSRCommandHandler.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 std::string root_path = "/api/v1/";
|
||||
const char* json_content_type = "Content-Type: application/json\r\n";
|
||||
}
|
||||
|
||||
void HandlePlayersRequest(HTTPReply& reply) {
|
||||
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) {
|
||||
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 HandleInvalidRoute(HTTPReply& reply) {
|
||||
reply.status = eHTTPStatusCode::NOT_FOUND;
|
||||
reply.message = "{\"error\":\"Invalid Route\"}";
|
||||
}
|
||||
|
||||
void HandleGET(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
|
||||
switch (route) {
|
||||
case ChatWebAPI::eRoute::PLAYERS:
|
||||
HandlePlayersRequest(reply);
|
||||
break;
|
||||
case ChatWebAPI::eRoute::TEAMS:
|
||||
HandleTeamsRequest(reply);
|
||||
break;
|
||||
case ChatWebAPI::eRoute::INVALID:
|
||||
default:
|
||||
HandleInvalidRoute(reply);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleAnnounceRequest(HTTPReply& reply, const std::optional<json>& data) {
|
||||
if (!JSONUtils::Validate(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 HandleCSRCommandRequest(HTTPReply& reply, const std::optional<json>& data) {
|
||||
if (!JSONUtils::Validate(data, reply)) return;
|
||||
|
||||
const auto& good_data = data.value();
|
||||
auto check = JSONUtils::CheckRequiredData(good_data, { "command" });
|
||||
if (!check.empty()) {
|
||||
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||
reply.message = check;
|
||||
} else {
|
||||
CSRCommandHandler::HandleCSRCommand(reply, good_data);
|
||||
}
|
||||
}
|
||||
|
||||
void HandlePOST(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
|
||||
auto data = GeneralUtils::TryParse<json>(body);
|
||||
switch (route) {
|
||||
case ChatWebAPI::eRoute::ANNOUNCE:
|
||||
HandleAnnounceRequest(reply, data.value());
|
||||
break;
|
||||
case ChatWebAPI::eRoute::CSR:
|
||||
HandleCSRCommandRequest(reply, data.value());
|
||||
break;
|
||||
case ChatWebAPI::eRoute::INVALID:
|
||||
default:
|
||||
HandleInvalidRoute(reply);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
// 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);
|
||||
// check for root path
|
||||
if (uri.find(root_path) == 0) {
|
||||
// remove root path from uri
|
||||
uri.erase(0, root_path.length());
|
||||
// convert uri to uppercase
|
||||
std::transform(uri.begin(), uri.end(), uri.begin(), ::toupper);
|
||||
// convert uri string to route enum
|
||||
ChatWebAPI::eRoute route = magic_enum::enum_cast<ChatWebAPI::eRoute>(uri).value_or(ChatWebAPI::eRoute::INVALID);
|
||||
// convert body from cstring to std string
|
||||
std::string body(http_msg->body.buf, http_msg->body.len);
|
||||
|
||||
switch (method) {
|
||||
case eHTTPMethod::GET:
|
||||
HandleGET(reply, route, body);
|
||||
break;
|
||||
case eHTTPMethod::POST:
|
||||
HandlePOST(reply, route, body);
|
||||
break;
|
||||
case eHTTPMethod::PUT:
|
||||
case eHTTPMethod::DELETE:
|
||||
case eHTTPMethod::HEAD:
|
||||
case eHTTPMethod::CONNECT:
|
||||
case eHTTPMethod::OPTIONS:
|
||||
case eHTTPMethod::TRACE:
|
||||
case eHTTPMethod::PATCH:
|
||||
case eHTTPMethod::INVALID:
|
||||
default:
|
||||
reply.status = eHTTPStatusCode::METHOD_NOT_ALLOWED;
|
||||
reply.message = "{\"error\":\"Invalid Method\"}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||
#pragma pop_macro("DELETE")
|
||||
#endif
|
||||
|
||||
ChatWebAPI::ChatWebAPI() {
|
||||
mg_log_set(MG_LL_NONE);
|
||||
mg_mgr_init(&mgr); // Initialize event manager
|
||||
}
|
||||
|
||||
ChatWebAPI::~ChatWebAPI() {
|
||||
mg_mgr_free(&mgr);
|
||||
}
|
||||
|
||||
void ChatWebAPI::Listen() {
|
||||
// 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;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void ChatWebAPI::ReceiveRequests() {
|
||||
mg_mgr_poll(&mgr, 15);
|
||||
}
|
||||
30
dChatServer/ChatWebAPI.h
Normal file
30
dChatServer/ChatWebAPI.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef CHATWEBAPI_H
|
||||
#define CHATWEBAPI_H
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
typedef struct mg_mgr mg_mgr;
|
||||
|
||||
class ChatWebAPI {
|
||||
public:
|
||||
ChatWebAPI();
|
||||
~ChatWebAPI();
|
||||
void ReceiveRequests();
|
||||
void Listen();
|
||||
enum class eRoute {
|
||||
// GET
|
||||
PLAYERS,
|
||||
TEAMS,
|
||||
// POST
|
||||
ANNOUNCE,
|
||||
CSR,
|
||||
// INVALID
|
||||
INVALID
|
||||
};
|
||||
private:
|
||||
mg_mgr mgr;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ChatJSONUtils.h"
|
||||
#include "JSONUtils.h"
|
||||
|
||||
#include "json.hpp"
|
||||
#include "eHTTPStatusCode.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -17,10 +18,13 @@ void to_json(json& data, const PlayerData& playerData) {
|
||||
}
|
||||
|
||||
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);
|
||||
data = playerContainer.GetAllPlayers();
|
||||
}
|
||||
|
||||
void to_json(json& data, const TeamContainer& teamContainer) {
|
||||
for (auto& teamData : Game::playerContainer.GetTeams()) {
|
||||
if (!teamData) continue;
|
||||
data.push_back(*teamData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +45,24 @@ void to_json(json& data, const TeamData& teamData) {
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::to_json(json& data, const TeamContainer::Data& teamContainer) {
|
||||
for (auto& teamData : TeamContainer::GetTeams()) {
|
||||
if (!teamData) continue;
|
||||
data.push_back(*teamData);
|
||||
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();
|
||||
}
|
||||
|
||||
bool JSONUtils::Validate(const std::optional<json>& data, HTTPReply& reply) {
|
||||
if (!data) {
|
||||
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||
reply.message = "{\"error\":\"Invalid JSON\"}";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
24
dChatServer/JSONUtils.h
Normal file
24
dChatServer/JSONUtils.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef __JSONUTILS_H__
|
||||
#define __JSONUTILS_H__
|
||||
|
||||
#include "json_fwd.hpp"
|
||||
#include "PlayerContainer.h"
|
||||
#include "eHTTPStatusCode.h"
|
||||
|
||||
struct HTTPReply {
|
||||
eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
|
||||
std::string message = "{\"error\":\"Not Found\"}";
|
||||
};
|
||||
|
||||
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);
|
||||
bool Validate(const std::optional<nlohmann::json>& data, HTTPReply& reply);
|
||||
}
|
||||
|
||||
#endif // __JSONUTILS_H__
|
||||
@@ -12,8 +12,6 @@
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
#include "MessageType/Chat.h"
|
||||
#include "ChatWeb.h"
|
||||
#include "TeamContainer.h"
|
||||
|
||||
void PlayerContainer::Initialize() {
|
||||
m_MaxNumberOfBestFriends =
|
||||
@@ -34,10 +32,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isLogin = !m_Players.contains(playerId);
|
||||
auto& data = m_Players[playerId];
|
||||
data = PlayerData();
|
||||
data.isLogin = isLogin;
|
||||
data.playerID = playerId;
|
||||
|
||||
uint32_t len;
|
||||
@@ -54,15 +49,14 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
if (!inStream.Read(data.zoneID)) return;
|
||||
if (!inStream.Read(data.muteExpire)) return;
|
||||
if (!inStream.Read(data.gmLevel)) return;
|
||||
data.worldServerSysAddr = packet->systemAddress;
|
||||
data.sysAddr = packet->systemAddress;
|
||||
|
||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||
m_PlayerCount++;
|
||||
|
||||
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
||||
ChatWeb::SendWSPlayerUpdate(data, isLogin ? eActivityType::PlayerLoggedIn : eActivityType::PlayerChangedZone);
|
||||
|
||||
Database::Get()->UpdateActivityLog(data.playerID, isLogin ? eActivityType::PlayerLoggedIn : eActivityType::PlayerChangedZone, data.zoneID.GetMapID());
|
||||
Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID());
|
||||
m_PlayersToRemove.erase(playerId);
|
||||
}
|
||||
|
||||
@@ -102,7 +96,7 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
|
||||
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
|
||||
}
|
||||
|
||||
auto* team = TeamContainer::GetTeam(playerID);
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
|
||||
@@ -112,12 +106,10 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
TeamContainer::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
|
||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
|
||||
}
|
||||
}
|
||||
|
||||
ChatWeb::SendWSPlayerUpdate(player, eActivityType::PlayerLoggedOut);
|
||||
|
||||
m_PlayerCount--;
|
||||
LOG("Removed user: %llu", playerID);
|
||||
m_Players.erase(playerID);
|
||||
@@ -145,6 +137,40 @@ void PlayerContainer::MuteUpdate(Packet* packet) {
|
||||
BroadcastMuteUpdate(playerID, expire);
|
||||
}
|
||||
|
||||
void PlayerContainer::CreateTeamServer(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID;
|
||||
inStream.Read(playerID);
|
||||
size_t membersSize = 0;
|
||||
inStream.Read(membersSize);
|
||||
|
||||
if (membersSize >= 4) {
|
||||
LOG("Tried to create a team with more than 4 players");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> members;
|
||||
|
||||
members.reserve(membersSize);
|
||||
|
||||
for (size_t i = 0; i < membersSize; i++) {
|
||||
LWOOBJID member;
|
||||
inStream.Read(member);
|
||||
members.push_back(member);
|
||||
}
|
||||
|
||||
LWOZONEID zoneId;
|
||||
|
||||
inStream.Read(zoneId);
|
||||
|
||||
auto* team = CreateLocalTeam(members);
|
||||
|
||||
if (team != nullptr) {
|
||||
team->zoneId = zoneId;
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_MUTE);
|
||||
@@ -155,6 +181,221 @@ void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
|
||||
TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
||||
if (members.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TeamData* newTeam = nullptr;
|
||||
|
||||
for (const auto member : members) {
|
||||
auto* team = GetTeam(member);
|
||||
|
||||
if (team != nullptr) {
|
||||
RemoveMember(team, member, false, false, true);
|
||||
}
|
||||
|
||||
if (newTeam == nullptr) {
|
||||
newTeam = CreateTeam(member, true);
|
||||
} else {
|
||||
AddMember(newTeam, member);
|
||||
}
|
||||
}
|
||||
|
||||
newTeam->lootFlag = 1;
|
||||
|
||||
TeamStatusUpdate(newTeam);
|
||||
|
||||
return newTeam;
|
||||
}
|
||||
|
||||
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
||||
auto* team = new TeamData();
|
||||
|
||||
team->teamID = ++m_TeamIDCounter;
|
||||
team->leaderID = leader;
|
||||
team->local = local;
|
||||
|
||||
GetTeamsMut().push_back(team);
|
||||
|
||||
AddMember(team, leader);
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
|
||||
for (auto* team : GetTeams()) {
|
||||
if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
|
||||
if (team->memberIDs.size() >= 4) {
|
||||
LOG("Tried to add player to team that already had 4 players");
|
||||
const auto& player = GetPlayerData(playerID);
|
||||
if (!player) return;
|
||||
ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
|
||||
|
||||
if (index != team->memberIDs.end()) return;
|
||||
|
||||
team->memberIDs.push_back(playerID);
|
||||
|
||||
const auto& leader = GetPlayerData(team->leaderID);
|
||||
const auto& member = GetPlayerData(playerID);
|
||||
|
||||
if (!leader || !member) return;
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
|
||||
const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName);
|
||||
|
||||
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName);
|
||||
|
||||
if (!team->local) {
|
||||
ChatPacketHandler::SendTeamSetLeader(member, leader.playerID);
|
||||
} else {
|
||||
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = GetPlayerData(memberId);
|
||||
|
||||
if (otherMember == member) continue;
|
||||
|
||||
const auto otherMemberName = GetName(memberId);
|
||||
|
||||
ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
|
||||
|
||||
if (otherMember) {
|
||||
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent) {
|
||||
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
|
||||
|
||||
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);
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
|
||||
if (team->memberIDs.size() <= 1) {
|
||||
DisbandTeam(team);
|
||||
} else {
|
||||
if (playerID == team->leaderID) {
|
||||
PromoteMember(team, team->memberIDs[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
|
||||
team->leaderID = newLeader;
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerContainer::DisbandTeam(TeamData* team) {
|
||||
const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
|
||||
|
||||
if (index == GetTeams().end()) return;
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember.playerName);
|
||||
|
||||
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
|
||||
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember.playerID, memberName);
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, true);
|
||||
|
||||
GetTeamsMut().erase(index);
|
||||
|
||||
delete team;
|
||||
}
|
||||
|
||||
void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
||||
const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
|
||||
|
||||
if (index == GetTeams().end()) return;
|
||||
|
||||
const auto& leader = GetPlayerData(team->leaderID);
|
||||
|
||||
if (!leader) return;
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
if (!team->local) {
|
||||
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
|
||||
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::TEAM_GET_STATUS);
|
||||
|
||||
bitStream.Write(team->teamID);
|
||||
bitStream.Write(deleteTeam);
|
||||
|
||||
if (!deleteTeam) {
|
||||
bitStream.Write(team->lootFlag);
|
||||
bitStream.Write<char>(team->memberIDs.size());
|
||||
for (const auto memberID : team->memberIDs) {
|
||||
bitStream.Write(memberID);
|
||||
}
|
||||
}
|
||||
|
||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
|
||||
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
|
||||
const auto iter = m_Names.find(playerID);
|
||||
|
||||
@@ -203,4 +444,5 @@ void PlayerContainer::Shutdown() {
|
||||
Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
|
||||
m_Players.erase(m_Players.begin());
|
||||
}
|
||||
for (auto* team : GetTeams()) if (team) delete team;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ enum class eGameMasterLevel : uint8_t;
|
||||
|
||||
struct TeamData;
|
||||
|
||||
struct TeamContainer {
|
||||
std::vector<TeamData*> mTeams;
|
||||
};
|
||||
|
||||
struct IgnoreData {
|
||||
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
|
||||
inline bool operator==(const std::string& other) const noexcept {
|
||||
@@ -38,7 +42,7 @@ struct PlayerData {
|
||||
return muteExpire == 1 || muteExpire > time(NULL);
|
||||
}
|
||||
|
||||
SystemAddress worldServerSysAddr{};
|
||||
SystemAddress sysAddr{};
|
||||
LWOZONEID zoneID{};
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
time_t muteExpire = 0;
|
||||
@@ -48,7 +52,6 @@ struct PlayerData {
|
||||
std::vector<IgnoreData> ignoredPlayers;
|
||||
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
|
||||
bool isFTP = false;
|
||||
bool isLogin = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -69,6 +72,7 @@ public:
|
||||
void ScheduleRemovePlayer(Packet* packet);
|
||||
void RemovePlayer(const LWOOBJID playerID);
|
||||
void MuteUpdate(Packet* packet);
|
||||
void CreateTeamServer(Packet* packet);
|
||||
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
||||
void Shutdown();
|
||||
|
||||
@@ -76,19 +80,34 @@ public:
|
||||
const PlayerData& GetPlayerData(const std::string& playerName);
|
||||
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
||||
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
||||
std::u16string GetName(LWOOBJID playerID);
|
||||
LWOOBJID GetId(const std::u16string& playerName);
|
||||
void Update(const float deltaTime);
|
||||
|
||||
uint32_t GetPlayerCount() { return m_PlayerCount; };
|
||||
uint32_t GetSimCount() { return m_SimCount; };
|
||||
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() const { return m_Players; };
|
||||
|
||||
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
||||
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
||||
TeamData* GetTeam(LWOOBJID playerID);
|
||||
void AddMember(TeamData* team, LWOOBJID playerID);
|
||||
void RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent = false);
|
||||
void PromoteMember(TeamData* team, LWOOBJID newLeader);
|
||||
void DisbandTeam(TeamData* team);
|
||||
void TeamStatusUpdate(TeamData* team);
|
||||
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
|
||||
std::u16string GetName(LWOOBJID playerID);
|
||||
LWOOBJID GetId(const std::u16string& playerName);
|
||||
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
||||
const TeamContainer& GetTeamContainer() { return teamContainer; }
|
||||
std::vector<TeamData*>& GetTeamsMut() { return teamContainer.mTeams; };
|
||||
const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); };
|
||||
|
||||
void Update(const float deltaTime);
|
||||
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
|
||||
|
||||
private:
|
||||
LWOOBJID m_TeamIDCounter = 0;
|
||||
std::map<LWOOBJID, PlayerData> m_Players;
|
||||
TeamContainer teamContainer{};
|
||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||
std::map<LWOOBJID, float> m_PlayersToRemove;
|
||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||
|
||||
@@ -1,669 +0,0 @@
|
||||
#include "TeamContainer.h"
|
||||
|
||||
#include "ChatPackets.h"
|
||||
|
||||
#include "MessageType/Chat.h"
|
||||
#include "MessageType/Game.h"
|
||||
|
||||
#include "ChatPacketHandler.h"
|
||||
#include "PlayerContainer.h"
|
||||
|
||||
namespace {
|
||||
TeamContainer::Data g_TeamContainer{};
|
||||
LWOOBJID g_TeamIDCounter = 0;
|
||||
}
|
||||
|
||||
const TeamContainer::Data& TeamContainer::GetTeamContainer() {
|
||||
return g_TeamContainer;
|
||||
}
|
||||
|
||||
std::vector<TeamData*>& TeamContainer::GetTeamsMut() {
|
||||
return g_TeamContainer.mTeams;
|
||||
}
|
||||
|
||||
const std::vector<TeamData*>& TeamContainer::GetTeams() {
|
||||
return GetTeamsMut();
|
||||
}
|
||||
|
||||
void TeamContainer::Shutdown() {
|
||||
for (auto* team : g_TeamContainer.mTeams) if (team) delete team;
|
||||
}
|
||||
|
||||
void TeamContainer::HandleTeamInvite(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOOBJID playerID;
|
||||
LUWString invitedPlayer;
|
||||
|
||||
inStream.Read(playerID);
|
||||
inStream.IgnoreBytes(4);
|
||||
inStream.Read(invitedPlayer);
|
||||
|
||||
const auto& player = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (!player) return;
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
if (team == nullptr) {
|
||||
team = CreateTeam(playerID);
|
||||
}
|
||||
|
||||
const auto& other = Game::playerContainer.GetPlayerData(invitedPlayer.GetAsString());
|
||||
|
||||
if (!other) return;
|
||||
|
||||
if (GetTeam(other.playerID) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (team->memberIDs.size() > 3) {
|
||||
// no more teams greater than 4
|
||||
|
||||
LOG("Someone tried to invite a 5th player to a team");
|
||||
return;
|
||||
}
|
||||
|
||||
SendTeamInvite(other, player);
|
||||
|
||||
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 TeamContainer::HandleTeamInviteResponse(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
char declined = 0;
|
||||
inStream.Read(declined);
|
||||
LWOOBJID leaderID = LWOOBJID_EMPTY;
|
||||
inStream.Read(leaderID);
|
||||
|
||||
LOG("Invite reponse received: %llu -> %llu (%d)", playerID, leaderID, declined);
|
||||
|
||||
if (declined) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* team = GetTeam(leaderID);
|
||||
|
||||
if (team == nullptr) {
|
||||
LOG("Failed to find team for leader (%llu)", leaderID);
|
||||
|
||||
team = GetTeam(playerID);
|
||||
}
|
||||
|
||||
if (team == nullptr) {
|
||||
LOG("Failed to find team for player (%llu)", playerID);
|
||||
return;
|
||||
}
|
||||
|
||||
AddMember(team, playerID);
|
||||
}
|
||||
|
||||
void TeamContainer::HandleTeamLeave(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
LOG("(%llu) leaving team", playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
RemoveMember(team, playerID, false, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::HandleTeamKick(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
LUWString kickedPlayer;
|
||||
|
||||
inStream.Read(playerID);
|
||||
inStream.IgnoreBytes(4);
|
||||
inStream.Read(kickedPlayer);
|
||||
|
||||
|
||||
LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.GetAsString().c_str());
|
||||
|
||||
const auto& kicked = Game::playerContainer.GetPlayerData(kickedPlayer.GetAsString());
|
||||
|
||||
LWOOBJID kickedId = LWOOBJID_EMPTY;
|
||||
|
||||
if (kicked) {
|
||||
kickedId = kicked.playerID;
|
||||
} else {
|
||||
kickedId = Game::playerContainer.GetId(kickedPlayer.string);
|
||||
}
|
||||
|
||||
if (kickedId == LWOOBJID_EMPTY) return;
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID || team->leaderID == kickedId) return;
|
||||
|
||||
RemoveMember(team, kickedId, false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::HandleTeamPromote(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
LUWString promotedPlayer;
|
||||
|
||||
inStream.Read(playerID);
|
||||
inStream.IgnoreBytes(4);
|
||||
inStream.Read(promotedPlayer);
|
||||
|
||||
LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.GetAsString().c_str());
|
||||
|
||||
const auto& promoted = Game::playerContainer.GetPlayerData(promotedPlayer.GetAsString());
|
||||
|
||||
if (!promoted) return;
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
PromoteMember(team, promoted.playerID);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::HandleTeamLootOption(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
|
||||
char option;
|
||||
inStream.Read(option);
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
team->lootFlag = option;
|
||||
|
||||
TeamStatusUpdate(team);
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::HandleTeamStatusRequest(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
const auto& data = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
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()) {
|
||||
RemoveMember(team, playerID, false, false, false, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (team->memberIDs.size() <= 1 && !team->local) {
|
||||
DisbandTeam(team, LWOOBJID_EMPTY, u"");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!team->local) {
|
||||
SendTeamSetLeader(data, team->leaderID);
|
||||
} else {
|
||||
SendTeamSetLeader(data, LWOOBJID_EMPTY);
|
||||
}
|
||||
|
||||
TeamStatusUpdate(team);
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (memberId == playerID) continue;
|
||||
|
||||
const auto memberName = Game::playerContainer.GetName(memberId);
|
||||
|
||||
if (otherMember) {
|
||||
SendTeamSetOffWorldFlag(otherMember, data.playerID, data.zoneID);
|
||||
}
|
||||
SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::TEAM_INVITE);
|
||||
|
||||
bitStream.Write(LUWString(sender.playerName.c_str()));
|
||||
bitStream.Write(sender.playerID);
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void TeamContainer::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
|
||||
|
||||
bitStream.Write(bLeaderIsFreeTrial);
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64LeaderZoneID);
|
||||
bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
|
||||
bitStream.Write(ucLootFlag);
|
||||
bitStream.Write(ucNumOfOtherPlayers);
|
||||
bitStream.Write(ucResponseCode);
|
||||
bitStream.Write<uint32_t>(wsLeaderName.size());
|
||||
for (const auto character : wsLeaderName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void TeamContainer::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
|
||||
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64LeaderZoneID);
|
||||
bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
|
||||
bitStream.Write(ucLootFlag);
|
||||
bitStream.Write(ucNumOfOtherPlayers);
|
||||
bitStream.Write<uint32_t>(wsLeaderName.size());
|
||||
for (const auto character : wsLeaderName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void TeamContainer::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
|
||||
|
||||
bitStream.Write(i64PlayerID);
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void TeamContainer::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
|
||||
|
||||
bitStream.Write(bIsFreeTrial);
|
||||
bitStream.Write(bLocal);
|
||||
bitStream.Write(bNoLootOnDeath);
|
||||
bitStream.Write(i64PlayerID);
|
||||
bitStream.Write<uint32_t>(wsPlayerName.size());
|
||||
for (const auto character : wsPlayerName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
bitStream.Write1();
|
||||
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
|
||||
zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
|
||||
}
|
||||
bitStream.Write(zoneID);
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void TeamContainer::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
|
||||
|
||||
bitStream.Write(bDisband);
|
||||
bitStream.Write(bIsKicked);
|
||||
bitStream.Write(bIsLeaving);
|
||||
bitStream.Write(bLocal);
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64PlayerID);
|
||||
bitStream.Write<uint32_t>(wsPlayerName.size());
|
||||
for (const auto character : wsPlayerName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void TeamContainer::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
|
||||
|
||||
bitStream.Write(i64PlayerID);
|
||||
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
|
||||
zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
|
||||
}
|
||||
bitStream.Write(zoneID);
|
||||
|
||||
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void TeamContainer::CreateTeamServer(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID;
|
||||
inStream.Read(playerID);
|
||||
size_t membersSize = 0;
|
||||
inStream.Read(membersSize);
|
||||
|
||||
if (membersSize >= 4) {
|
||||
LOG("Tried to create a team with more than 4 players");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> members;
|
||||
|
||||
members.reserve(membersSize);
|
||||
|
||||
for (size_t i = 0; i < membersSize; i++) {
|
||||
LWOOBJID member;
|
||||
inStream.Read(member);
|
||||
members.push_back(member);
|
||||
}
|
||||
|
||||
LWOZONEID zoneId;
|
||||
|
||||
inStream.Read(zoneId);
|
||||
|
||||
auto* team = CreateLocalTeam(members);
|
||||
|
||||
if (team != nullptr) {
|
||||
team->zoneId = zoneId;
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
TeamData* TeamContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
||||
if (members.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TeamData* newTeam = nullptr;
|
||||
|
||||
for (const auto member : members) {
|
||||
auto* team = GetTeam(member);
|
||||
|
||||
if (team != nullptr) {
|
||||
RemoveMember(team, member, false, false, true);
|
||||
}
|
||||
|
||||
if (newTeam == nullptr) {
|
||||
newTeam = CreateTeam(member, true);
|
||||
} else {
|
||||
AddMember(newTeam, member);
|
||||
}
|
||||
}
|
||||
|
||||
newTeam->lootFlag = 1;
|
||||
|
||||
TeamStatusUpdate(newTeam);
|
||||
|
||||
return newTeam;
|
||||
}
|
||||
|
||||
TeamData* TeamContainer::CreateTeam(LWOOBJID leader, bool local) {
|
||||
auto* team = new TeamData();
|
||||
|
||||
team->teamID = ++g_TeamIDCounter;
|
||||
team->leaderID = leader;
|
||||
team->local = local;
|
||||
|
||||
GetTeamsMut().push_back(team);
|
||||
|
||||
AddMember(team, leader);
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
TeamData* TeamContainer::GetTeam(LWOOBJID playerID) {
|
||||
for (auto* team : GetTeams()) {
|
||||
if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TeamContainer::AddMember(TeamData* team, LWOOBJID playerID) {
|
||||
if (team->memberIDs.size() >= 4) {
|
||||
LOG("Tried to add player to team that already had 4 players");
|
||||
const auto& player = Game::playerContainer.GetPlayerData(playerID);
|
||||
if (!player) return;
|
||||
ChatPackets::SendSystemMessage(player.worldServerSysAddr, u"The teams is full! You have not been added to a team!");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
|
||||
|
||||
if (index != team->memberIDs.end()) return;
|
||||
|
||||
team->memberIDs.push_back(playerID);
|
||||
|
||||
const auto& leader = Game::playerContainer.GetPlayerData(team->leaderID);
|
||||
const auto& member = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (!leader || !member) return;
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
|
||||
const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName);
|
||||
|
||||
SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName);
|
||||
|
||||
if (!team->local) {
|
||||
SendTeamSetLeader(member, leader.playerID);
|
||||
} else {
|
||||
SendTeamSetLeader(member, LWOOBJID_EMPTY);
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (otherMember == member) continue;
|
||||
|
||||
const auto otherMemberName = Game::playerContainer.GetName(memberId);
|
||||
|
||||
SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
|
||||
|
||||
if (otherMember) {
|
||||
SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::RemoveMember(TeamData* team, LWOOBJID causingPlayerID, bool disband, bool kicked, bool leaving, bool silent) {
|
||||
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;
|
||||
|
||||
team->memberIDs.erase(index);
|
||||
|
||||
const auto& member = Game::playerContainer.GetPlayerData(causingPlayerID);
|
||||
|
||||
const auto causingMemberName = Game::playerContainer.GetName(causingPlayerID);
|
||||
|
||||
if (member && !silent) {
|
||||
SendTeamRemovePlayer(member, disband, kicked, leaving, team->local, LWOOBJID_EMPTY, causingPlayerID, causingMemberName);
|
||||
}
|
||||
|
||||
if (team->memberIDs.size() <= 1) {
|
||||
DisbandTeam(team, causingPlayerID, causingMemberName);
|
||||
} else /* team has enough members to be a team still */ {
|
||||
team->leaderID = (causingPlayerID == team->leaderID) ? team->memberIDs[0] : team->leaderID;
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
if (silent && memberId == causingPlayerID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
SendTeamRemovePlayer(otherMember, disband, kicked, leaving, team->local, team->leaderID, causingPlayerID, causingMemberName);
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
|
||||
team->leaderID = newLeader;
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
SendTeamSetLeader(otherMember, newLeader);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamContainer::DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName) {
|
||||
const auto index = std::ranges::find(GetTeams(), team);
|
||||
|
||||
if (index == GetTeams().end()) return;
|
||||
LOG_DEBUG("Disbanding team %i", (*index)->teamID);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
|
||||
SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, causingPlayerID, causingPlayerName);
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, true);
|
||||
|
||||
GetTeamsMut().erase(index);
|
||||
|
||||
delete team;
|
||||
}
|
||||
|
||||
void TeamContainer::TeamStatusUpdate(TeamData* team) {
|
||||
const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
|
||||
|
||||
if (index == GetTeams().end()) return;
|
||||
|
||||
const auto& leader = Game::playerContainer.GetPlayerData(team->leaderID);
|
||||
|
||||
if (!leader) return;
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
|
||||
if (!team->local) {
|
||||
SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
|
||||
void TeamContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::TEAM_GET_STATUS);
|
||||
|
||||
bitStream.Write(team->teamID);
|
||||
bitStream.Write(deleteTeam);
|
||||
|
||||
if (!deleteTeam) {
|
||||
bitStream.Write(team->lootFlag);
|
||||
bitStream.Write<char>(team->memberIDs.size());
|
||||
for (const auto memberID : team->memberIDs) {
|
||||
bitStream.Write(memberID);
|
||||
}
|
||||
}
|
||||
|
||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Darkflame Universe
|
||||
// Copyright 2025
|
||||
|
||||
#ifndef TEAMCONTAINER_H
|
||||
#define TEAMCONTAINER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
struct Packet;
|
||||
struct PlayerData;
|
||||
struct TeamData;
|
||||
|
||||
namespace TeamContainer {
|
||||
struct Data {
|
||||
std::vector<TeamData*> mTeams;
|
||||
};
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void HandleTeamInvite(Packet* packet);
|
||||
void HandleTeamInviteResponse(Packet* packet);
|
||||
void HandleTeamLeave(Packet* packet);
|
||||
void HandleTeamKick(Packet* packet);
|
||||
void HandleTeamPromote(Packet* packet);
|
||||
void HandleTeamLootOption(Packet* packet);
|
||||
void HandleTeamStatusRequest(Packet* packet);
|
||||
|
||||
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 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 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 SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
|
||||
|
||||
void CreateTeamServer(Packet* packet);
|
||||
|
||||
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
||||
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
||||
TeamData* GetTeam(LWOOBJID playerID);
|
||||
void AddMember(TeamData* team, LWOOBJID playerID);
|
||||
void RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent = false);
|
||||
void PromoteMember(TeamData* team, LWOOBJID newLeader);
|
||||
void DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName);
|
||||
void TeamStatusUpdate(TeamData* team);
|
||||
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
|
||||
|
||||
const TeamContainer::Data& GetTeamContainer();
|
||||
std::vector<TeamData*>& GetTeamsMut();
|
||||
const std::vector<TeamData*>& GetTeams();
|
||||
};
|
||||
|
||||
#endif //!TEAMCONTAINER_H
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "Logger.h"
|
||||
#include "Game.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -40,7 +39,6 @@ public:
|
||||
// AMFValue template class instantiations
|
||||
template <typename ValueType>
|
||||
class AMFValue : public AMFBaseValue {
|
||||
static_assert(!std::is_same_v<ValueType, std::string_view>, "AMFValue cannot be instantiated with std::string_view");
|
||||
public:
|
||||
AMFValue() = default;
|
||||
AMFValue(const ValueType value) : m_Data{ value } {}
|
||||
@@ -53,15 +51,6 @@ public:
|
||||
|
||||
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:
|
||||
ValueType m_Data;
|
||||
};
|
||||
@@ -221,17 +210,13 @@ public:
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
template<typename AmfType>
|
||||
AmfType& Insert(const std::string_view key, std::unique_ptr<AmfType> value) {
|
||||
void Insert(const std::string_view key, std::unique_ptr<AMFBaseValue> value) {
|
||||
const auto element = m_Associative.find(key);
|
||||
auto& toReturn = *value;
|
||||
if (element != m_Associative.cend() && element->second) {
|
||||
element->second = std::move(value);
|
||||
} else {
|
||||
m_Associative.emplace(key, std::move(value));
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,15 +228,11 @@ public:
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
template<typename AmfType>
|
||||
AmfType& Insert(const size_t index, std::unique_ptr<AmfType> value) {
|
||||
auto& toReturn = *value;
|
||||
void Insert(const size_t index, std::unique_ptr<AMFBaseValue> value) {
|
||||
if (index >= m_Dense.size()) {
|
||||
m_Dense.resize(index + 1);
|
||||
}
|
||||
|
||||
m_Dense.at(index) = std::move(value);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,10 +257,10 @@ public:
|
||||
*
|
||||
* @param key The key to remove from the associative portion
|
||||
*/
|
||||
void Remove(const std::string& key) {
|
||||
void Remove(const std::string& key, const bool deleteValue = true) {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
if (it != m_Associative.cend()) {
|
||||
m_Associative.erase(it);
|
||||
if (deleteValue) m_Associative.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,18 +343,6 @@ public:
|
||||
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:
|
||||
/**
|
||||
* The associative portion. These values are key'd with strings to an AMFValue.
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
#include "Sd0.h"
|
||||
#include "ZCompression.h"
|
||||
#include "Logger.h"
|
||||
|
||||
@@ -45,10 +44,10 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
}
|
||||
|
||||
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
|
||||
std::unique_ptr<uint8_t[]> uncompressedChunk(new uint8_t[Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE]);
|
||||
std::unique_ptr<uint8_t[]> uncompressedChunk(new uint8_t[ZCompression::MAX_SD0_CHUNK_SIZE]);
|
||||
int32_t err{};
|
||||
int32_t actualUncompressedSize = ZCompression::Decompress(
|
||||
compressedChunk.get(), chunkSize, uncompressedChunk.get(), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
|
||||
compressedChunk.get(), chunkSize, uncompressedChunk.get(), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
||||
|
||||
if (actualUncompressedSize != -1) {
|
||||
uint32_t previousSize = completeUncompressedModel.size();
|
||||
@@ -118,7 +117,7 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
}
|
||||
|
||||
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
|
||||
std::stringstream outputStringStream(outputString);
|
||||
std::istringstream outputStringStream(outputString);
|
||||
|
||||
try {
|
||||
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
|
||||
|
||||
@@ -16,10 +16,6 @@ set(DCOMMON_SOURCES
|
||||
"BrickByBrickFix.cpp"
|
||||
"BinaryPathFinder.cpp"
|
||||
"FdbToSqlite.cpp"
|
||||
"JSONUtils.cpp"
|
||||
"TinyXmlUtils.cpp"
|
||||
"Sd0.cpp"
|
||||
"Lxfml.cpp"
|
||||
)
|
||||
|
||||
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
# define DluAssert(expression) do { assert(expression) } while(0)
|
||||
# define DluAssert(expression) assert(expression)
|
||||
#else
|
||||
# define DluAssert(expression)
|
||||
#endif
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#include "JSONUtils.h"
|
||||
#include "json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef _JSONUTILS_H_
|
||||
#define _JSONUTILS_H_
|
||||
|
||||
#include "json_fwd.hpp"
|
||||
|
||||
namespace JSONUtils {
|
||||
// check required fields in json data
|
||||
std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
|
||||
}
|
||||
|
||||
#endif // _JSONUTILS_H_
|
||||
@@ -29,8 +29,8 @@ constexpr const char* GetFileNameFromAbsolutePath(const char* path) {
|
||||
// they will not be valid constexpr and will be evaluated at runtime instead of compile time!
|
||||
// The full string is still stored in the binary, however the offset of the filename in the absolute paths
|
||||
// is used in the instruction instead of the start of the absolute path.
|
||||
#define LOG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->Log(str_, message, ##__VA_ARGS__); } while(0)
|
||||
#define LOG_DEBUG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->LogDebug(str_, message, ##__VA_ARGS__); } while(0)
|
||||
#define LOG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->Log(str, message, ##__VA_ARGS__); } while(0)
|
||||
#define LOG_DEBUG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); } while(0)
|
||||
|
||||
// Writer class for writing data to files.
|
||||
class Writer {
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
#include "Lxfml.h"
|
||||
|
||||
#include "GeneralUtils.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "TinyXmlUtils.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
|
||||
Result toReturn;
|
||||
tinyxml2::XMLDocument doc;
|
||||
const auto err = doc.Parse(data.data());
|
||||
if (err != tinyxml2::XML_SUCCESS) {
|
||||
LOG("Failed to parse xml %s.", StringifiedEnum::ToString(err).data());
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
TinyXmlUtils::DocumentReader reader(doc);
|
||||
std::map<std::string/* refID */, std::string> transformations;
|
||||
|
||||
auto lxfml = reader["LXFML"];
|
||||
if (!lxfml) {
|
||||
LOG("Failed to find LXFML element.");
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
// First get all the positions of bricks
|
||||
for (const auto& brick : lxfml["Bricks"]) {
|
||||
const auto* part = brick.FirstChildElement("Part");
|
||||
if (part) {
|
||||
const auto* bone = part->FirstChildElement("Bone");
|
||||
if (bone) {
|
||||
auto* transformation = bone->Attribute("transformation");
|
||||
if (transformation) {
|
||||
auto* refID = bone->Attribute("refID");
|
||||
if (refID) transformations[refID] = transformation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These points are well out of bounds for an actual player
|
||||
NiPoint3 lowest{ 10'000.0f, 10'000.0f, 10'000.0f };
|
||||
NiPoint3 highest{ -10'000.0f, -10'000.0f, -10'000.0f };
|
||||
|
||||
// Calculate the lowest and highest points on the entire model
|
||||
for (const auto& transformation : transformations | std::views::values) {
|
||||
auto split = GeneralUtils::SplitString(transformation, ',');
|
||||
if (split.size() < 12) {
|
||||
LOG("Not enough in the split?");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
||||
if (x < lowest.x) lowest.x = x;
|
||||
if (y < lowest.y) lowest.y = y;
|
||||
if (z < lowest.z) lowest.z = z;
|
||||
|
||||
if (highest.x < x) highest.x = x;
|
||||
if (highest.y < y) highest.y = y;
|
||||
if (highest.z < z) highest.z = z;
|
||||
}
|
||||
|
||||
auto delta = (highest - lowest) / 2.0f;
|
||||
auto newRootPos = lowest + delta;
|
||||
|
||||
// Clamp the Y to the lowest point on the model
|
||||
newRootPos.y = lowest.y;
|
||||
|
||||
// Adjust all positions to account for the new origin
|
||||
for (auto& transformation : transformations | std::views::values) {
|
||||
auto split = GeneralUtils::SplitString(transformation, ',');
|
||||
if (split.size() < 12) {
|
||||
LOG("Not enough in the split?");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x;
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y;
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z;
|
||||
std::stringstream stream;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
stream << split[i];
|
||||
stream << ',';
|
||||
}
|
||||
stream << x << ',' << y << ',' << z;
|
||||
transformation = stream.str();
|
||||
}
|
||||
|
||||
// Finally write the new transformation back into the lxfml
|
||||
for (auto& brick : lxfml["Bricks"]) {
|
||||
auto* part = brick.FirstChildElement("Part");
|
||||
if (part) {
|
||||
auto* bone = part->FirstChildElement("Bone");
|
||||
if (bone) {
|
||||
auto* transformation = bone->Attribute("transformation");
|
||||
if (transformation) {
|
||||
auto* refID = bone->Attribute("refID");
|
||||
if (refID) {
|
||||
bone->SetAttribute("transformation", transformations[refID].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tinyxml2::XMLPrinter printer;
|
||||
doc.Print(&printer);
|
||||
|
||||
toReturn.lxfml = printer.CStr();
|
||||
toReturn.center = newRootPos;
|
||||
return toReturn;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Darkflame Universe
|
||||
// Copyright 2025
|
||||
|
||||
#ifndef LXFML_H
|
||||
#define LXFML_H
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "NiPoint3.h"
|
||||
|
||||
namespace Lxfml {
|
||||
struct Result {
|
||||
std::string lxfml;
|
||||
NiPoint3 center;
|
||||
};
|
||||
|
||||
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
|
||||
// Returns a struct of its new center and the updated LXFML containing these edits.
|
||||
[[nodiscard]] Result NormalizePosition(const std::string_view data);
|
||||
};
|
||||
|
||||
#endif //!LXFML_H
|
||||
150
dCommon/Sd0.cpp
150
dCommon/Sd0.cpp
@@ -1,150 +0,0 @@
|
||||
#include "Sd0.h"
|
||||
|
||||
#include <array>
|
||||
#include <ranges>
|
||||
|
||||
#include "BinaryIO.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include "ZCompression.h"
|
||||
|
||||
// Insert header if on first buffer
|
||||
void WriteHeader(Sd0::BinaryBuffer& chunk) {
|
||||
chunk.push_back(Sd0::SD0_HEADER[0]);
|
||||
chunk.push_back(Sd0::SD0_HEADER[1]);
|
||||
chunk.push_back(Sd0::SD0_HEADER[2]);
|
||||
chunk.push_back(Sd0::SD0_HEADER[3]);
|
||||
chunk.push_back(Sd0::SD0_HEADER[4]);
|
||||
}
|
||||
|
||||
// Write the size of the buffer to a chunk
|
||||
void WriteSize(Sd0::BinaryBuffer& chunk, uint32_t chunkSize) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char toPush = chunkSize & 0xff;
|
||||
chunkSize = chunkSize >> 8;
|
||||
chunk.push_back(toPush);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t GetDataOffset(bool firstBuffer) {
|
||||
return firstBuffer ? 9 : 4;
|
||||
}
|
||||
|
||||
Sd0::Sd0(std::istream& buffer) {
|
||||
char header[5]{};
|
||||
|
||||
// Check if this is an sd0 buffer. It's possible we may be handed a zlib buffer directly due to old code so check for that too.
|
||||
if (!BinaryIO::BinaryRead(buffer, header) || memcmp(header, SD0_HEADER, sizeof(header)) != 0) {
|
||||
LOG("Failed to read SD0 header %i %i %i %i %i %i %i", buffer.good(), buffer.tellg(), header[0], header[1], header[2], header[3], header[4]);
|
||||
LOG_DEBUG("This may be a zlib buffer directly? Trying again assuming its a zlib buffer.");
|
||||
auto& firstChunk = m_Chunks.emplace_back();
|
||||
WriteHeader(firstChunk);
|
||||
buffer.seekg(0, std::ios::end);
|
||||
uint32_t bufferSize = buffer.tellg();
|
||||
buffer.seekg(0, std::ios::beg);
|
||||
WriteSize(firstChunk, bufferSize);
|
||||
firstChunk.resize(firstChunk.size() + bufferSize);
|
||||
auto* dataStart = reinterpret_cast<char*>(firstChunk.data() + GetDataOffset(true));
|
||||
if (!buffer.read(dataStart, bufferSize)) {
|
||||
m_Chunks.pop_back();
|
||||
LOG("Failed to read %u bytes from chunk %i", bufferSize, m_Chunks.size() - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (buffer && buffer.peek() != std::istream::traits_type::eof()) {
|
||||
uint32_t chunkSize{};
|
||||
if (!BinaryIO::BinaryRead(buffer, chunkSize)) {
|
||||
LOG("Failed to read chunk size from stream %lld %zu", buffer.tellg(), m_Chunks.size());
|
||||
break;
|
||||
}
|
||||
auto& chunk = m_Chunks.emplace_back();
|
||||
bool firstBuffer = m_Chunks.size() == 1;
|
||||
auto dataOffset = GetDataOffset(firstBuffer);
|
||||
|
||||
// Insert header if on first buffer
|
||||
if (firstBuffer) {
|
||||
WriteHeader(chunk);
|
||||
}
|
||||
|
||||
WriteSize(chunk, chunkSize);
|
||||
|
||||
chunk.resize(chunkSize + dataOffset);
|
||||
auto* dataStart = reinterpret_cast<char*>(chunk.data() + dataOffset);
|
||||
if (!buffer.read(dataStart, chunkSize)) {
|
||||
m_Chunks.pop_back();
|
||||
LOG("Failed to read %u bytes from chunk %i", chunkSize, m_Chunks.size() - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sd0::FromData(const uint8_t* data, size_t bufferSize) {
|
||||
const auto originalBufferSize = bufferSize;
|
||||
if (bufferSize == 0) return;
|
||||
|
||||
m_Chunks.clear();
|
||||
while (bufferSize > 0) {
|
||||
const auto numToCopy = std::min(MAX_UNCOMPRESSED_CHUNK_SIZE, bufferSize);
|
||||
const auto* startOffset = data + originalBufferSize - bufferSize;
|
||||
bufferSize -= numToCopy;
|
||||
std::array<uint8_t, MAX_UNCOMPRESSED_CHUNK_SIZE> compressedChunk;
|
||||
const auto compressedSize = ZCompression::Compress(
|
||||
startOffset, numToCopy,
|
||||
compressedChunk.data(), compressedChunk.size());
|
||||
|
||||
auto& chunk = m_Chunks.emplace_back();
|
||||
bool firstBuffer = m_Chunks.size() == 1;
|
||||
auto dataOffset = GetDataOffset(firstBuffer);
|
||||
|
||||
if (firstBuffer) {
|
||||
WriteHeader(chunk);
|
||||
}
|
||||
|
||||
WriteSize(chunk, compressedSize);
|
||||
|
||||
chunk.resize(compressedSize + dataOffset);
|
||||
memcpy(chunk.data() + dataOffset, compressedChunk.data(), compressedSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string Sd0::GetAsStringUncompressed() const {
|
||||
std::string toReturn;
|
||||
bool first = true;
|
||||
uint32_t totalSize{};
|
||||
for (const auto& chunk : m_Chunks) {
|
||||
auto dataOffset = GetDataOffset(first);
|
||||
first = false;
|
||||
const auto chunkSize = chunk.size();
|
||||
|
||||
auto oldSize = toReturn.size();
|
||||
toReturn.resize(oldSize + MAX_UNCOMPRESSED_CHUNK_SIZE);
|
||||
int32_t error{};
|
||||
const auto uncompressedSize = ZCompression::Decompress(
|
||||
chunk.data() + dataOffset, chunkSize - dataOffset,
|
||||
reinterpret_cast<uint8_t*>(toReturn.data()) + oldSize, MAX_UNCOMPRESSED_CHUNK_SIZE,
|
||||
error);
|
||||
|
||||
totalSize += uncompressedSize;
|
||||
}
|
||||
|
||||
toReturn.resize(totalSize);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::stringstream Sd0::GetAsStream() const {
|
||||
std::stringstream toReturn;
|
||||
|
||||
for (const auto& chunk : m_Chunks) {
|
||||
toReturn.write(reinterpret_cast<const char*>(chunk.data()), chunk.size());
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
const std::vector<Sd0::BinaryBuffer>& Sd0::GetAsVector() const {
|
||||
return m_Chunks;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// Darkflame Universe
|
||||
// Copyright 2025
|
||||
|
||||
#ifndef SD0_H
|
||||
#define SD0_H
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
// Sd0 is comprised of multiple zlib compressed buffers stored in a row.
|
||||
// The format starts with a SD0 header (see SD0_HEADER) followed by the size of a zlib buffer, and then the zlib buffer itself.
|
||||
// This repeats until end of file
|
||||
class Sd0 {
|
||||
public:
|
||||
using BinaryBuffer = std::vector<uint8_t>;
|
||||
|
||||
static inline const char* SD0_HEADER = "sd0\x01\xff";
|
||||
|
||||
/**
|
||||
* @brief Max size of an inflated sd0 zlib chunk
|
||||
*/
|
||||
static constexpr inline size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 1024 * 256;
|
||||
|
||||
// Read the input buffer into an internal chunk stream to be used later
|
||||
Sd0(std::istream& buffer);
|
||||
|
||||
// Uncompresses the entire Sd0 buffer and returns it as a string
|
||||
[[nodiscard]] std::string GetAsStringUncompressed() const;
|
||||
|
||||
// Gets the Sd0 buffer as a stream in its raw compressed form
|
||||
[[nodiscard]] std::stringstream GetAsStream() const;
|
||||
|
||||
// Gets the Sd0 buffer as a vector in its raw compressed form
|
||||
[[nodiscard]] const std::vector<BinaryBuffer>& GetAsVector() const;
|
||||
|
||||
// Compress data into a Sd0 buffer
|
||||
void FromData(const uint8_t* data, size_t bufferSize);
|
||||
private:
|
||||
std::vector<BinaryBuffer> m_Chunks{};
|
||||
};
|
||||
|
||||
#endif //!SD0_H
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "TinyXmlUtils.h"
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
using namespace TinyXmlUtils;
|
||||
|
||||
Element DocumentReader::operator[](const std::string_view elem) const {
|
||||
return Element(m_Doc.FirstChildElement(elem.empty() ? nullptr : elem.data()), elem);
|
||||
}
|
||||
|
||||
Element::Element(tinyxml2::XMLElement* xmlElem, const std::string_view elem) :
|
||||
m_IteratedName{ elem },
|
||||
m_Elem{ xmlElem } {
|
||||
}
|
||||
|
||||
Element Element::operator[](const std::string_view elem) const {
|
||||
const auto* usedElem = elem.empty() ? nullptr : elem.data();
|
||||
auto* toReturn = m_Elem ? m_Elem->FirstChildElement(usedElem) : nullptr;
|
||||
return Element(toReturn, m_IteratedName);
|
||||
}
|
||||
|
||||
ElementIterator Element::begin() {
|
||||
return ElementIterator(m_Elem ? m_Elem->FirstChildElement() : nullptr);
|
||||
}
|
||||
|
||||
ElementIterator Element::end() {
|
||||
return ElementIterator(nullptr);
|
||||
}
|
||||
|
||||
ElementIterator::ElementIterator(tinyxml2::XMLElement* elem) :
|
||||
m_CurElem{ elem } {
|
||||
}
|
||||
|
||||
ElementIterator& ElementIterator::operator++() {
|
||||
if (m_CurElem) m_CurElem = m_CurElem->NextSiblingElement();
|
||||
return *this;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Darkflame Universe
|
||||
// Copyright 2025
|
||||
|
||||
#ifndef TINYXMLUTILS_H
|
||||
#define TINYXMLUTILS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "DluAssert.h"
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
namespace TinyXmlUtils {
|
||||
// See cstdlib for iterator technicalities
|
||||
struct ElementIterator {
|
||||
ElementIterator(tinyxml2::XMLElement* elem);
|
||||
|
||||
ElementIterator& operator++();
|
||||
[[nodiscard]] tinyxml2::XMLElement* operator->() { DluAssert(m_CurElem); return m_CurElem; }
|
||||
[[nodiscard]] tinyxml2::XMLElement& operator*() { DluAssert(m_CurElem); return *m_CurElem; }
|
||||
|
||||
bool operator==(const ElementIterator& other) const { return other.m_CurElem == m_CurElem; }
|
||||
|
||||
private:
|
||||
tinyxml2::XMLElement* m_CurElem{ nullptr };
|
||||
};
|
||||
|
||||
// Wrapper class to act as an iterator over xml elements.
|
||||
// All the normal rules that apply to Iterators in the std library apply here.
|
||||
class Element {
|
||||
public:
|
||||
Element(tinyxml2::XMLElement* xmlElem, const std::string_view elem);
|
||||
|
||||
// The first child element of this element.
|
||||
[[nodiscard]] ElementIterator begin();
|
||||
|
||||
// Always returns an ElementIterator which points to nullptr.
|
||||
// TinyXml2 return NULL when you've reached the last child element so
|
||||
// you can't do any funny one past end logic here.
|
||||
[[nodiscard]] ElementIterator end();
|
||||
|
||||
// Get a child element
|
||||
[[nodiscard]] Element operator[](const std::string_view elem) const;
|
||||
[[nodiscard]] Element operator[](const char* elem) const { return operator[](std::string_view(elem)); };
|
||||
|
||||
// Whether or not data exists for this element
|
||||
operator bool() const { return m_Elem != nullptr; }
|
||||
|
||||
[[nodiscard]] const tinyxml2::XMLElement* operator->() const { return m_Elem; }
|
||||
private:
|
||||
const char* GetElementName() const { return m_IteratedName.empty() ? nullptr : m_IteratedName.c_str(); }
|
||||
const std::string m_IteratedName;
|
||||
tinyxml2::XMLElement* m_Elem;
|
||||
};
|
||||
|
||||
class DocumentReader {
|
||||
public:
|
||||
DocumentReader(tinyxml2::XMLDocument& doc) : m_Doc{ doc } {}
|
||||
|
||||
[[nodiscard]] Element operator[](const std::string_view elem) const;
|
||||
private:
|
||||
tinyxml2::XMLDocument& m_Doc;
|
||||
};
|
||||
};
|
||||
|
||||
#endif //!TINYXMLUTILS_H
|
||||
@@ -8,5 +8,11 @@ namespace ZCompression {
|
||||
int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst);
|
||||
|
||||
int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr);
|
||||
|
||||
/**
|
||||
* @brief Max size of an inflated sd0 zlib chunk
|
||||
*
|
||||
*/
|
||||
constexpr uint32_t MAX_SD0_CHUNK_SIZE = 1024 * 256;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
|
||||
#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) {
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory.");
|
||||
@@ -21,20 +18,12 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
||||
|
||||
m_RootPath = m_Path;
|
||||
m_ResPath = (m_Path / "client" / "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.");
|
||||
}
|
||||
|
||||
} else if (std::filesystem::exists(m_Path / ".." / "versions") && std::filesystem::exists(m_Path / "res")) {
|
||||
m_AssetBundleType = eAssetBundleType::Packed;
|
||||
|
||||
m_RootPath = (m_Path / "..");
|
||||
m_ResPath = (m_Path / "res");
|
||||
} 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.");
|
||||
}
|
||||
|
||||
} else if (std::filesystem::exists(m_Path / "pack") && std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
|
||||
m_AssetBundleType = eAssetBundleType::Packed;
|
||||
|
||||
m_RootPath = (m_Path / ".." / "..");
|
||||
@@ -59,7 +48,6 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
||||
break;
|
||||
}
|
||||
case eAssetBundleType::None:
|
||||
[[fallthrough]];
|
||||
case eAssetBundleType::Unpacked: {
|
||||
break;
|
||||
}
|
||||
@@ -67,10 +55,19 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
||||
}
|
||||
|
||||
void AssetManager::LoadPackIndex() {
|
||||
m_PackIndex = PackIndex(m_RootPath);
|
||||
m_PackIndex = new PackIndex(m_RootPath);
|
||||
}
|
||||
|
||||
bool AssetManager::HasFile(std::string fixedName) const {
|
||||
std::filesystem::path AssetManager::GetResPath() {
|
||||
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); });
|
||||
|
||||
// Special case for unpacked client have BrickModels in upper case
|
||||
@@ -84,7 +81,8 @@ bool AssetManager::HasFile(std::string fixedName) const {
|
||||
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
||||
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
|
||||
|
||||
const auto crc = crc32b(crc32b(CRC32_INIT, fixedName), NULL_TERMINATOR);
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
||||
|
||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||
if (item.m_Crc == crc) {
|
||||
@@ -95,7 +93,8 @@ bool AssetManager::HasFile(std::string fixedName) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetManager::GetFile(std::string fixedName, char** data, uint32_t* len) const {
|
||||
bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
||||
auto fixedName = std::string(name);
|
||||
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
|
||||
|
||||
@@ -130,7 +129,8 @@ bool AssetManager::GetFile(std::string fixedName, char** data, uint32_t* len) co
|
||||
fixedName = "client\\res\\" + fixedName;
|
||||
}
|
||||
int32_t packIndex = -1;
|
||||
auto crc = crc32b(crc32b(CRC32_INIT, fixedName), NULL_TERMINATOR);
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
||||
|
||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||
if (item.m_Crc == crc) {
|
||||
@@ -144,13 +144,15 @@ bool AssetManager::GetFile(std::string fixedName, char** data, uint32_t* len) co
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& pack = this->m_PackIndex->GetPacks().at(packIndex);
|
||||
const bool success = pack.ReadFileFromPack(crc, data, len);
|
||||
auto packs = this->m_PackIndex->GetPacks();
|
||||
auto* pack = packs.at(packIndex);
|
||||
|
||||
bool success = pack->ReadFileFromPack(crc, data, len);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
AssetStream AssetManager::GetFile(const char* name) const {
|
||||
AssetStream AssetManager::GetFile(const char* name) {
|
||||
char* buf; uint32_t len;
|
||||
|
||||
bool success = this->GetFile(name, &buf, &len);
|
||||
@@ -158,15 +160,23 @@ AssetStream AssetManager::GetFile(const char* name) const {
|
||||
return AssetStream(buf, len, success);
|
||||
}
|
||||
|
||||
uint32_t AssetManager::crc32b(uint32_t crc, const std::string_view message) {
|
||||
for (const auto byte : message) {
|
||||
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
||||
size_t i, j;
|
||||
uint32_t crc, msb;
|
||||
|
||||
crc = base;
|
||||
for (i = 0; i < l; i++) {
|
||||
// xor next byte to upper bits of crc
|
||||
crc ^= (static_cast<uint32_t>(std::bit_cast<uint8_t>(byte)) << 24);
|
||||
for (size_t _ = 0; _ < 8; _++) { // Do eight times.
|
||||
const uint32_t msb = crc >> 31;
|
||||
crc ^= (static_cast<unsigned int>(message[i]) << 24);
|
||||
for (j = 0; j < 8; j++) { // Do eight times.
|
||||
msb = crc >> 31;
|
||||
crc <<= 1;
|
||||
crc ^= (0 - msb) & 0x04C11DB7;
|
||||
}
|
||||
}
|
||||
return crc; // don't complement crc on output
|
||||
}
|
||||
|
||||
AssetManager::~AssetManager() {
|
||||
delete m_PackIndex;
|
||||
}
|
||||
|
||||
@@ -61,32 +61,23 @@ struct AssetStream : std::istream {
|
||||
class AssetManager {
|
||||
public:
|
||||
AssetManager(const std::filesystem::path& path);
|
||||
~AssetManager();
|
||||
|
||||
[[nodiscard]]
|
||||
const std::filesystem::path& GetResPath() const {
|
||||
return m_ResPath;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
eAssetBundleType GetAssetBundleType() const {
|
||||
return m_AssetBundleType;
|
||||
}
|
||||
std::filesystem::path GetResPath();
|
||||
eAssetBundleType GetAssetBundleType();
|
||||
|
||||
[[nodiscard]]
|
||||
bool HasFile(std::string name) const;
|
||||
|
||||
[[nodiscard]]
|
||||
bool GetFile(std::string name, char** data, uint32_t* len) const;
|
||||
|
||||
[[nodiscard]]
|
||||
AssetStream GetFile(const char* name) const;
|
||||
bool HasFile(const char* name);
|
||||
bool GetFile(const char* name, char** data, uint32_t* len);
|
||||
AssetStream GetFile(const char* name);
|
||||
|
||||
private:
|
||||
void LoadPackIndex();
|
||||
|
||||
// Modified crc algorithm (mpeg2)
|
||||
// Reference: https://stackoverflow.com/questions/54339800/how-to-modify-crc-32-to-crc-32-mpeg-2
|
||||
static inline uint32_t crc32b(uint32_t crc, std::string_view message);
|
||||
inline uint32_t crc32b(uint32_t base, uint8_t* message, size_t l);
|
||||
|
||||
bool m_SuccessfullyLoaded;
|
||||
|
||||
std::filesystem::path m_Path;
|
||||
std::filesystem::path m_RootPath;
|
||||
@@ -94,5 +85,5 @@ private:
|
||||
|
||||
eAssetBundleType m_AssetBundleType = eAssetBundleType::None;
|
||||
|
||||
std::optional<PackIndex> m_PackIndex;
|
||||
PackIndex* m_PackIndex;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "Pack.h"
|
||||
|
||||
#include "BinaryIO.h"
|
||||
#include "Sd0.h"
|
||||
#include "ZCompression.h"
|
||||
|
||||
Pack::Pack(const std::filesystem::path& filePath) {
|
||||
@@ -22,20 +21,19 @@ Pack::Pack(const std::filesystem::path& filePath) {
|
||||
|
||||
m_FileStream.seekg(recordCountPos, std::ios::beg);
|
||||
|
||||
uint32_t recordCount = 0;
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, recordCount);
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_RecordCount);
|
||||
|
||||
m_Records.reserve(recordCount);
|
||||
std::generate_n(std::back_inserter(m_Records), recordCount, [&] {
|
||||
for (int i = 0; i < m_RecordCount; i++) {
|
||||
PackRecord record;
|
||||
BinaryIO::BinaryRead<PackRecord>(m_FileStream, record);
|
||||
return record;
|
||||
});
|
||||
|
||||
m_Records.push_back(record);
|
||||
}
|
||||
|
||||
m_FileStream.close();
|
||||
}
|
||||
|
||||
bool Pack::HasFile(const uint32_t crc) const {
|
||||
bool Pack::HasFile(uint32_t crc) {
|
||||
for (const auto& record : m_Records) {
|
||||
if (record.m_Crc == crc) {
|
||||
return true;
|
||||
@@ -45,7 +43,7 @@ bool Pack::HasFile(const uint32_t crc) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) const {
|
||||
bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
||||
// Time for some wacky C file reading for speed reasons
|
||||
|
||||
PackRecord pkRecord{};
|
||||
@@ -107,7 +105,7 @@ bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) cons
|
||||
pos += size; // Move pointer position the amount of bytes read to the right
|
||||
|
||||
int32_t err;
|
||||
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
|
||||
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
||||
|
||||
free(chunk);
|
||||
}
|
||||
|
||||
@@ -24,17 +24,16 @@ struct PackRecord {
|
||||
class Pack {
|
||||
public:
|
||||
Pack(const std::filesystem::path& filePath);
|
||||
~Pack() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
bool HasFile(uint32_t crc) const;
|
||||
|
||||
[[nodiscard]]
|
||||
bool ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) const;
|
||||
bool HasFile(uint32_t crc);
|
||||
bool ReadFileFromPack(uint32_t crc, char** data, uint32_t* len);
|
||||
private:
|
||||
std::ifstream m_FileStream;
|
||||
std::filesystem::path m_FilePath;
|
||||
|
||||
char m_Version[7];
|
||||
|
||||
uint32_t m_RecordCount;
|
||||
std::vector<PackRecord> m_Records;
|
||||
};
|
||||
|
||||
@@ -6,32 +6,38 @@
|
||||
PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
||||
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, packPathCount);
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
|
||||
|
||||
m_PackPaths.resize(packPathCount);
|
||||
m_PackPaths.resize(m_PackPathCount);
|
||||
for (auto& item : m_PackPaths) {
|
||||
BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
|
||||
}
|
||||
|
||||
uint32_t packFileIndexCount = 0;
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, packFileIndexCount);
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
|
||||
|
||||
m_PackFileIndices.reserve(packFileIndexCount);
|
||||
std::generate_n(std::back_inserter(m_PackFileIndices), packFileIndexCount, [&] {
|
||||
for (int i = 0; i < m_PackFileIndexCount; i++) {
|
||||
PackFileIndex 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());
|
||||
|
||||
m_Packs.reserve(m_PackPaths.size());
|
||||
for (auto& item : m_PackPaths) {
|
||||
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();
|
||||
}
|
||||
|
||||
PackIndex::~PackIndex() {
|
||||
for (const auto* item : m_Packs) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,23 +21,20 @@ struct PackFileIndex {
|
||||
class PackIndex {
|
||||
public:
|
||||
PackIndex(const std::filesystem::path& filePath);
|
||||
~PackIndex();
|
||||
|
||||
[[nodiscard]]
|
||||
const std::vector<std::string>& GetPackPaths() const { return m_PackPaths; }
|
||||
|
||||
[[nodiscard]]
|
||||
const std::vector<PackFileIndex>& GetPackFileIndices() const { return m_PackFileIndices; }
|
||||
|
||||
[[nodiscard]]
|
||||
const std::vector<Pack>& GetPacks() const { return m_Packs; }
|
||||
const std::vector<std::string>& GetPackPaths() { return m_PackPaths; }
|
||||
const std::vector<PackFileIndex>& GetPackFileIndices() { return m_PackFileIndices; }
|
||||
const std::vector<Pack*>& GetPacks() { return m_Packs; }
|
||||
private:
|
||||
std::ifstream m_FileStream;
|
||||
|
||||
uint32_t m_Version;
|
||||
|
||||
uint32_t m_PackPathCount;
|
||||
std::vector<std::string> m_PackPaths;
|
||||
|
||||
uint32_t m_PackFileIndexCount;
|
||||
std::vector<PackFileIndex> m_PackFileIndices;
|
||||
|
||||
std::vector<Pack> m_Packs;
|
||||
std::vector<Pack*> m_Packs;
|
||||
};
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
#ifndef __DCOMMONVARS__H__
|
||||
#define __DCOMMONVARS__H__
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include "BitStream.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "MessageType/Client.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "MessageType/Client.h"
|
||||
#include "BitStreamUtils.h"
|
||||
|
||||
#pragma warning (disable:4251) //Disables SQL warnings
|
||||
|
||||
@@ -99,8 +98,6 @@ public:
|
||||
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 LWOZONEID& replacement) noexcept { *this = replacement; }
|
||||
constexpr bool operator==(const LWOZONEID&) const = default;
|
||||
constexpr auto operator<=>(const LWOZONEID&) const = default;
|
||||
|
||||
private:
|
||||
LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc...
|
||||
|
||||
54
dCommon/dEnums/eCSRCommand.h
Normal file
54
dCommon/dEnums/eCSRCommand.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef __ECSRCOMMAND__H__
|
||||
#define __ECSRCOMMAND__H__
|
||||
|
||||
enum class eCSRCommand {
|
||||
QUERY_SERVER_STATUS,
|
||||
QUERY_CHARACTER_LOCATION,
|
||||
QUERY_CHARACTER_ONLINE_STATUS,
|
||||
INVENTORY_ADD_ITEM,
|
||||
INVENTORY_DELETE_ITEM,
|
||||
MODERATE_MUTE_ACCOUNT,
|
||||
MODERATE_BAN_ACCOUNT,
|
||||
MODERATE_EDUCATE_CHARACTER,
|
||||
MODERATE_KICK_CHARACTER,
|
||||
MODERATE_WARN_CHARACTER,
|
||||
MODERATE_RENAME_CHARACTER,
|
||||
MODERATE_DELETE_CHARACTER_FRIEND,
|
||||
MODERATE_KILL_CHARACTER,
|
||||
UPDATE_CHARACTER_HEALTH,
|
||||
UPDATE_CHARACTER_ARMOR,
|
||||
UPDATE_CHARACTER_IMAGINATION,
|
||||
UPDATE_CHARACTER_MAX_HEALTH,
|
||||
UPDATE_CHARACTER_MAX_ARMOR,
|
||||
UPDATE_CHARACTER_MAX_IMAGINATION,
|
||||
UPDATE_CHARACTER_CURRENCY,
|
||||
UPDATE_CHARACTER_REPUTATION,
|
||||
UPDATE_CHARACTER_LEGO_SCORE,
|
||||
UPDATE_CHARACTER_EMOTES,
|
||||
UPDATE_CHARACTER_ADD_ACHIEVEMENT,
|
||||
UPDATE_CHARACTER_COMPLETE_ACHIEVEMENT,
|
||||
UPDATE_CHARACTER_REMOVE_ACHIEVEMENT,
|
||||
UPDATE_CHARACTER_POSITION_OFFLINE,
|
||||
UPDATE_CHARACTER_INV_SLOT_AMOUNT,
|
||||
UTILITY_SAVE_CHARACTER,
|
||||
UTILITY_SEND_MAIL,
|
||||
UTILITY_GIVE_ITEM_TO_ALL_PLAYERS_ONLINE,
|
||||
METRICS_CONFIGURE,
|
||||
DISABLE_ZONE,
|
||||
INIT_DONATION_AMOUNT,
|
||||
KILL_SERVERS_COUNTDOWN,
|
||||
DISABLE_FAQ,
|
||||
THROTTLEQUEUE,
|
||||
GATEGM_ACCESS,
|
||||
RECONNECT_CRISP,
|
||||
MODERATE_KICK_ACCOUNT,
|
||||
TOGGLE_CRISP_SERVER,
|
||||
QUICK_DRAIN_SERVER,
|
||||
QUICK_DRAIN_SERVER_RENEW,
|
||||
REPLICATE_CHARACTER,
|
||||
GET_SERVER_STATUS,
|
||||
RELOAD_SERVER_INIS,
|
||||
INVALID
|
||||
};
|
||||
|
||||
#endif // !__ECSRCOMMAND__H__
|
||||
@@ -16,9 +16,7 @@ enum class eCharacterVersion : uint32_t {
|
||||
VAULT_SIZE,
|
||||
// Fixes speed base value in level component
|
||||
SPEED_BASE,
|
||||
// Fixes nexus force explorer missions
|
||||
NJ_JAYMISSIONS,
|
||||
UP_TO_DATE, // will become NEXUS_FORCE_EXPLORER
|
||||
UP_TO_DATE, // will become NJ_JAYMISSIONS
|
||||
};
|
||||
|
||||
#endif //!__ECHARACTERVERSION__H__
|
||||
|
||||
@@ -7,8 +7,7 @@ enum class eConnectionType : uint16_t {
|
||||
CHAT,
|
||||
WORLD = 4,
|
||||
CLIENT,
|
||||
MASTER,
|
||||
UNKNOWN
|
||||
MASTER
|
||||
};
|
||||
|
||||
#endif //!__ECONNECTIONTYPE__H__
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef __EHTTPMETHODS__H__
|
||||
#define __EHTTPMETHODS__H__
|
||||
|
||||
#include "dPlatforms.h"
|
||||
|
||||
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||
#pragma push_macro("DELETE")
|
||||
#undef DELETE
|
||||
|
||||
@@ -28,8 +28,7 @@ enum eInventoryType : uint32_t {
|
||||
DONATION,
|
||||
VAULT_MODELS,
|
||||
ITEM_SETS, //internal, technically this is BankBehaviors.
|
||||
INVALID, // made up, for internal use!!!, Technically this called the ALL inventory.
|
||||
ALL, // Use this to search all inventories instead of a specific one.
|
||||
INVALID // made up, for internal use!!!, Technically this called the ALL inventory.
|
||||
};
|
||||
|
||||
class InventoryType {
|
||||
|
||||
@@ -102,6 +102,7 @@ DEFINE_TABLE_STORAGE(CDScriptComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
||||
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
|
||||
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDZoneTableTable);
|
||||
|
||||
void CDClientManager::LoadValuesFromDatabase() {
|
||||
if (!CDClientDatabase::isConnected) {
|
||||
@@ -148,7 +149,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
|
||||
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDZoneTableTable::LoadValuesFromDatabase();
|
||||
CDZoneTableTable::Instance().LoadValuesFromDatabase();
|
||||
}
|
||||
|
||||
void CDClientManager::LoadValuesFromDefaults() {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#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];
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#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
|
||||
@@ -1,53 +1,67 @@
|
||||
#include "CDZoneTableTable.h"
|
||||
|
||||
namespace CDZoneTableTable {
|
||||
Table entries;
|
||||
void CDZoneTableTable::LoadValuesFromDatabase() {
|
||||
|
||||
void LoadValuesFromDatabase() {
|
||||
// Get the data from the database
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ZoneTable");
|
||||
while (!tableData.eof()) {
|
||||
CDZoneTable entry;
|
||||
entry.zoneID = tableData.getIntField("zoneID", -1);
|
||||
entry.locStatus = tableData.getIntField("locStatus", -1);
|
||||
entry.zoneName = tableData.getStringField("zoneName", "");
|
||||
entry.scriptID = tableData.getIntField("scriptID", -1);
|
||||
entry.ghostdistance_min = tableData.getFloatField("ghostdistance_min", -1.0f);
|
||||
entry.ghostdistance = tableData.getFloatField("ghostdistance", -1.0f);
|
||||
entry.population_soft_cap = tableData.getIntField("population_soft_cap", -1);
|
||||
entry.population_hard_cap = tableData.getIntField("population_hard_cap", -1);
|
||||
UNUSED(entry.DisplayDescription = tableData.getStringField("DisplayDescription", ""));
|
||||
UNUSED(entry.mapFolder = tableData.getStringField("mapFolder", ""));
|
||||
entry.smashableMinDistance = tableData.getFloatField("smashableMinDistance", -1.0f);
|
||||
entry.smashableMaxDistance = tableData.getFloatField("smashableMaxDistance", -1.0f);
|
||||
UNUSED(entry.mixerProgram = tableData.getStringField("mixerProgram", ""));
|
||||
UNUSED(entry.clientPhysicsFramerate = tableData.getStringField("clientPhysicsFramerate", ""));
|
||||
entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", "");
|
||||
entry.zoneControlTemplate = tableData.getIntField("zoneControlTemplate", -1);
|
||||
entry.widthInChunks = tableData.getIntField("widthInChunks", -1);
|
||||
entry.heightInChunks = tableData.getIntField("heightInChunks", -1);
|
||||
entry.petsAllowed = tableData.getIntField("petsAllowed", -1) == 1 ? true : false;
|
||||
entry.localize = tableData.getIntField("localize", -1) == 1 ? true : false;
|
||||
entry.fZoneWeight = tableData.getFloatField("fZoneWeight", -1.0f);
|
||||
UNUSED(entry.thumbnail = tableData.getStringField("thumbnail", ""));
|
||||
entry.PlayerLoseCoinsOnDeath = tableData.getIntField("PlayerLoseCoinsOnDeath", -1) == 1 ? true : false;
|
||||
entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false;
|
||||
entry.teamRadius = tableData.getFloatField("teamRadius", -1.0f);
|
||||
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||
entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false;
|
||||
// First, get the size of the table
|
||||
uint32_t size = 0;
|
||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ZoneTable");
|
||||
while (!tableSize.eof()) {
|
||||
size = tableSize.getIntField(0, 0);
|
||||
|
||||
entries[entry.zoneID] = entry;
|
||||
tableData.nextRow();
|
||||
}
|
||||
tableSize.nextRow();
|
||||
}
|
||||
|
||||
//! Queries the table with a zoneID to find.
|
||||
const CDZoneTable* Query(uint32_t zoneID) {
|
||||
const auto& iter = entries.find(zoneID);
|
||||
if (iter != entries.end()) {
|
||||
return &iter->second;
|
||||
}
|
||||
tableSize.finalize();
|
||||
|
||||
return nullptr;
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ZoneTable");
|
||||
auto& entries = GetEntriesMutable();
|
||||
while (!tableData.eof()) {
|
||||
CDZoneTable entry;
|
||||
entry.zoneID = tableData.getIntField("zoneID", -1);
|
||||
entry.locStatus = tableData.getIntField("locStatus", -1);
|
||||
entry.zoneName = tableData.getStringField("zoneName", "");
|
||||
entry.scriptID = tableData.getIntField("scriptID", -1);
|
||||
entry.ghostdistance_min = tableData.getFloatField("ghostdistance_min", -1.0f);
|
||||
entry.ghostdistance = tableData.getFloatField("ghostdistance", -1.0f);
|
||||
entry.population_soft_cap = tableData.getIntField("population_soft_cap", -1);
|
||||
entry.population_hard_cap = tableData.getIntField("population_hard_cap", -1);
|
||||
UNUSED(entry.DisplayDescription = tableData.getStringField("DisplayDescription", ""));
|
||||
UNUSED(entry.mapFolder = tableData.getStringField("mapFolder", ""));
|
||||
entry.smashableMinDistance = tableData.getFloatField("smashableMinDistance", -1.0f);
|
||||
entry.smashableMaxDistance = tableData.getFloatField("smashableMaxDistance", -1.0f);
|
||||
UNUSED(entry.mixerProgram = tableData.getStringField("mixerProgram", ""));
|
||||
UNUSED(entry.clientPhysicsFramerate = tableData.getStringField("clientPhysicsFramerate", ""));
|
||||
entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", "");
|
||||
entry.zoneControlTemplate = tableData.getIntField("zoneControlTemplate", -1);
|
||||
entry.widthInChunks = tableData.getIntField("widthInChunks", -1);
|
||||
entry.heightInChunks = tableData.getIntField("heightInChunks", -1);
|
||||
entry.petsAllowed = tableData.getIntField("petsAllowed", -1) == 1 ? true : false;
|
||||
entry.localize = tableData.getIntField("localize", -1) == 1 ? true : false;
|
||||
entry.fZoneWeight = tableData.getFloatField("fZoneWeight", -1.0f);
|
||||
UNUSED(entry.thumbnail = tableData.getStringField("thumbnail", ""));
|
||||
entry.PlayerLoseCoinsOnDeath = tableData.getIntField("PlayerLoseCoinsOnDeath", -1) == 1 ? true : false;
|
||||
entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false;
|
||||
entry.teamRadius = tableData.getFloatField("teamRadius", -1.0f);
|
||||
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||
entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false;
|
||||
|
||||
entries.insert(std::make_pair(entry.zoneID, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
//! Queries the table with a zoneID to find.
|
||||
const CDZoneTable* CDZoneTableTable::Query(uint32_t zoneID) {
|
||||
auto& m_Entries = GetEntries();
|
||||
const auto& iter = m_Entries.find(zoneID);
|
||||
|
||||
if (iter != m_Entries.end()) {
|
||||
return &iter->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ struct CDZoneTable {
|
||||
bool mountsAllowed; //!< Whether or not mounts are allowed
|
||||
};
|
||||
|
||||
namespace CDZoneTableTable {
|
||||
using Table = std::map<uint32_t, CDZoneTable>;
|
||||
class CDZoneTableTable : public CDTable<CDZoneTableTable, std::map<uint32_t, CDZoneTable>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
// Queries the table with a zoneID to find.
|
||||
|
||||
@@ -25,7 +25,6 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
|
||||
"CDObjectsTable.cpp"
|
||||
"CDPetComponentTable.cpp"
|
||||
"CDPackageComponentTable.cpp"
|
||||
"CDPlayerFlagsTable.cpp"
|
||||
"CDPhysicsComponentTable.cpp"
|
||||
"CDPropertyEntranceComponentTable.cpp"
|
||||
"CDPropertyTemplateTable.cpp"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(CDClientDatabase)
|
||||
add_subdirectory(GameDatabase)
|
||||
|
||||
add_library(dDatabase STATIC "MigrationRunner.cpp" "ModelNormalizeMigration.cpp")
|
||||
add_library(dDatabase STATIC "MigrationRunner.cpp")
|
||||
|
||||
add_custom_target(conncpp_dylib
|
||||
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
enum class eActivityType : uint32_t {
|
||||
PlayerLoggedIn,
|
||||
PlayerLoggedOut,
|
||||
PlayerChangedZone
|
||||
};
|
||||
|
||||
class IActivityLog {
|
||||
|
||||
@@ -44,8 +44,6 @@ public:
|
||||
|
||||
// Updates the given character ids last login to be right now.
|
||||
virtual void UpdateLastLoggedInCharacter(const uint32_t characterId) = 0;
|
||||
|
||||
virtual bool IsNameInUse(const std::string_view name) = 0;
|
||||
};
|
||||
|
||||
#endif //!__ICHARINFO__H__
|
||||
|
||||
@@ -8,10 +8,27 @@
|
||||
#include "dCommonVars.h"
|
||||
#include "NiQuaternion.h"
|
||||
#include "NiPoint3.h"
|
||||
#include "MailInfo.h"
|
||||
|
||||
|
||||
class IMail {
|
||||
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.
|
||||
virtual void InsertNewMail(const MailInfo& mail) = 0;
|
||||
|
||||
|
||||
@@ -53,9 +53,6 @@ public:
|
||||
// Update the property details for the given property id.
|
||||
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.
|
||||
virtual void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) = 0;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
|
||||
// Inserts a new UGC model into the database.
|
||||
virtual void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
std::istringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const uint32_t characterId) = 0;
|
||||
@@ -34,17 +34,9 @@ public:
|
||||
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
|
||||
|
||||
// Update the model position and rotation for the given property id.
|
||||
virtual void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
|
||||
virtual void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<int32_t, 5> behaviorIDs) {
|
||||
std::array<std::pair<int32_t, std::string>, 5> behaviors;
|
||||
for (int32_t i = 0; i < behaviors.size(); i++) behaviors[i].first = behaviorIDs[i];
|
||||
UpdateModel(modelID, position, rotation, behaviors);
|
||||
}
|
||||
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
|
||||
|
||||
// Remove the model for the given property id.
|
||||
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
||||
|
||||
// Gets a model by ID
|
||||
virtual Model GetModel(const LWOOBJID modelID) = 0;
|
||||
};
|
||||
#endif //!__IPROPERTIESCONTENTS__H__
|
||||
|
||||
@@ -9,11 +9,10 @@ public:
|
||||
struct MasterInfo {
|
||||
std::string ip;
|
||||
uint32_t port{};
|
||||
std::string password{};
|
||||
};
|
||||
|
||||
// Set the master server ip and port.
|
||||
virtual void SetMasterInfo(const MasterInfo& info) = 0;
|
||||
virtual void SetMasterIp(const std::string_view ip, const uint32_t port) = 0;
|
||||
|
||||
// Get the master server info.
|
||||
virtual std::optional<MasterInfo> GetMasterInfo() = 0;
|
||||
|
||||
@@ -12,7 +12,6 @@ public:
|
||||
struct Model {
|
||||
std::stringstream lxfmlData;
|
||||
LWOOBJID id{};
|
||||
LWOOBJID modelID{};
|
||||
};
|
||||
|
||||
// Gets all UGC models for the given property id.
|
||||
@@ -28,6 +27,6 @@ public:
|
||||
virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0;
|
||||
|
||||
// Inserts a new UGC model into the database.
|
||||
virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) = 0;
|
||||
virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) = 0;
|
||||
};
|
||||
#endif //!__IUGC__H__
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
||||
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
||||
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
|
||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
|
||||
std::vector<IUgc::Model> GetAllUgcModels() override;
|
||||
void CreateMigrationHistoryTable() override;
|
||||
bool IsMigrationRun(const std::string_view str) override;
|
||||
@@ -70,24 +70,23 @@ public:
|
||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||
void UpdatePropertyModerationInfo(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;
|
||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||
void RemoveUnreferencedUgcModels() override;
|
||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||
void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void RemoveModel(const LWOOBJID& modelId) override;
|
||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||
void InsertNewMail(const MailInfo& mail) override;
|
||||
void InsertNewMail(const IMail::MailInfo& mail) override;
|
||||
void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
std::istringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const uint32_t characterId) override;
|
||||
std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
|
||||
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
||||
void MarkMailRead(const uint64_t mailId) override;
|
||||
void DeleteMail(const uint64_t mailId) override;
|
||||
@@ -97,7 +96,7 @@ public:
|
||||
void UpdateAccountBan(const uint32_t accountId, const bool banned) 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 SetMasterInfo(const IServers::MasterInfo& info) override;
|
||||
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
|
||||
std::optional<uint32_t> GetCurrentPersistentId() override;
|
||||
void InsertDefaultPersistentId() override;
|
||||
void UpdatePersistentId(const uint32_t id) override;
|
||||
@@ -125,10 +124,8 @@ public:
|
||||
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 DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||
uint32_t GetAccountCount() override;
|
||||
bool IsNameInUse(const std::string_view name) override;
|
||||
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
|
||||
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||
uint32_t GetAccountCount() override;
|
||||
private:
|
||||
|
||||
// Generic query functions that can be used for any query.
|
||||
|
||||
@@ -76,9 +76,3 @@ void MySQLDatabase::SetPendingCharacterName(const uint32_t characterId, const st
|
||||
void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t 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,7 +1,6 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
|
||||
void MySQLDatabase::InsertNewMail(const MailInfo& mail) {
|
||||
void MySQLDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
||||
ExecuteInsert(
|
||||
"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`)"
|
||||
@@ -19,17 +18,17 @@ void MySQLDatabase::InsertNewMail(const MailInfo& mail) {
|
||||
mail.itemCount);
|
||||
}
|
||||
|
||||
std::vector<MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||
std::vector<IMail::MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||
auto res = ExecuteSelect(
|
||||
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
|
||||
" FROM mail WHERE receiver_id=? limit ?;",
|
||||
characterId, numberOfMail);
|
||||
|
||||
std::vector<MailInfo> toReturn;
|
||||
std::vector<IMail::MailInfo> toReturn;
|
||||
toReturn.reserve(res->rowsCount());
|
||||
|
||||
while (res->next()) {
|
||||
MailInfo mail;
|
||||
IMail::MailInfo mail;
|
||||
mail.id = res->getUInt64("id");
|
||||
mail.subject = res->getString("subject").c_str();
|
||||
mail.body = res->getString("body").c_str();
|
||||
@@ -47,14 +46,14 @@ std::vector<MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t characterId
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
|
||||
std::optional<IMail::MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
|
||||
auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
||||
|
||||
if (!res->next()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MailInfo toReturn;
|
||||
IMail::MailInfo toReturn;
|
||||
toReturn.itemLOT = res->getInt("attachment_lot");
|
||||
toReturn.itemCount = res->getInt("attachment_count");
|
||||
|
||||
|
||||
@@ -173,10 +173,6 @@ void MySQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
||||
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) {
|
||||
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ? LIMIT 1;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||
}
|
||||
|
||||
@@ -52,39 +52,14 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
|
||||
}
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
ExecuteUpdate(
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
|
||||
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, modelID);
|
||||
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
|
||||
}
|
||||
|
||||
IPropertyContents::Model MySQLDatabase::GetModel(const LWOOBJID modelID) {
|
||||
auto result = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
|
||||
|
||||
IPropertyContents::Model model{};
|
||||
while (result->next()) {
|
||||
model.id = result->getUInt64("id");
|
||||
model.lot = static_cast<LOT>(result->getUInt("lot"));
|
||||
model.position.x = result->getFloat("x");
|
||||
model.position.y = result->getFloat("y");
|
||||
model.position.z = result->getFloat("z");
|
||||
model.rotation.w = result->getFloat("rw");
|
||||
model.rotation.x = result->getFloat("rx");
|
||||
model.rotation.y = result->getFloat("ry");
|
||||
model.rotation.z = result->getFloat("rz");
|
||||
model.ugcId = result->getUInt64("ugc_id");
|
||||
model.behaviors[0] = result->getInt("behavior_1");
|
||||
model.behaviors[1] = result->getInt("behavior_2");
|
||||
model.behaviors[2] = result->getInt("behavior_3");
|
||||
model.behaviors[3] = result->getInt("behavior_4");
|
||||
model.behaviors[4] = result->getInt("behavior_5");
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
void MySQLDatabase::SetMasterInfo(const MasterInfo& info) {
|
||||
void MySQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
|
||||
// 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.
|
||||
ExecuteDelete("TRUNCATE TABLE servers;");
|
||||
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022, ?)", info.ip, info.port, info.password);
|
||||
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
|
||||
}
|
||||
|
||||
std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
|
||||
auto result = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
|
||||
auto result = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
|
||||
|
||||
if (!result->next()) {
|
||||
return std::nullopt;
|
||||
@@ -18,7 +18,6 @@ std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
|
||||
|
||||
toReturn.ip = result->getString("ip").c_str();
|
||||
toReturn.port = result->getInt("port");
|
||||
toReturn.password = result->getString("master_password").c_str();
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
|
||||
auto result = ExecuteSelect(
|
||||
"SELECT lxfml, u.id as ugcID, pc.id as modelID FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;",
|
||||
"SELECT lxfml, u.id FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;",
|
||||
propertyId);
|
||||
|
||||
std::vector<IUgc::Model> toReturn;
|
||||
@@ -13,8 +13,7 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
|
||||
// blob is owned by the query, so we need to do a deep copy :/
|
||||
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
|
||||
model.lxfmlData << blob->rdbuf();
|
||||
model.id = result->getUInt64("ugcID");
|
||||
model.modelID = result->getUInt64("modelID");
|
||||
model.id = result->getUInt64("id");
|
||||
toReturn.push_back(std::move(model));
|
||||
}
|
||||
|
||||
@@ -22,14 +21,13 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
|
||||
}
|
||||
|
||||
std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
|
||||
auto result = ExecuteSelect("SELECT u.id AS ugcID, lxfml, pc.id AS modelID FROM ugc AS u JOIN properties_contents AS pc ON pc.ugc_id = u.id WHERE pc.lot = 14 AND pc.ugc_id IS NOT NULL;");
|
||||
auto result = ExecuteSelect("SELECT id, lxfml FROM ugc;");
|
||||
|
||||
std::vector<IUgc::Model> models;
|
||||
models.reserve(result->rowsCount());
|
||||
while (result->next()) {
|
||||
IUgc::Model model;
|
||||
model.id = result->getInt64("ugcID");
|
||||
model.modelID = result->getUInt64("modelID");
|
||||
model.id = result->getInt64("id");
|
||||
|
||||
// blob is owned by the query, so we need to do a deep copy :/
|
||||
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
|
||||
@@ -45,7 +43,7 @@ void MySQLDatabase::RemoveUnreferencedUgcModels() {
|
||||
}
|
||||
|
||||
void MySQLDatabase::InsertNewUgcModel(
|
||||
std:: stringstream& sd0Data, // cant be const sad
|
||||
std::istringstream& sd0Data, // cant be const sad
|
||||
const uint32_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const uint32_t characterId) {
|
||||
@@ -67,7 +65,7 @@ void MySQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
|
||||
void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) {
|
||||
const std::istream stream(lxfml.rdbuf());
|
||||
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
||||
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
||||
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
|
||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
|
||||
std::vector<IUgc::Model> GetAllUgcModels() override;
|
||||
void CreateMigrationHistoryTable() override;
|
||||
bool IsMigrationRun(const std::string_view str) override;
|
||||
@@ -68,24 +68,23 @@ public:
|
||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||
void UpdatePropertyModerationInfo(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;
|
||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||
void RemoveUnreferencedUgcModels() override;
|
||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||
void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void RemoveModel(const LWOOBJID& modelId) override;
|
||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||
void InsertNewMail(const MailInfo& mail) override;
|
||||
void InsertNewMail(const IMail::MailInfo& mail) override;
|
||||
void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
std::istringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const uint32_t characterId) override;
|
||||
std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
|
||||
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
||||
void MarkMailRead(const uint64_t mailId) override;
|
||||
void DeleteMail(const uint64_t mailId) override;
|
||||
@@ -95,7 +94,7 @@ public:
|
||||
void UpdateAccountBan(const uint32_t accountId, const bool banned) 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 SetMasterInfo(const IServers::MasterInfo& info) override;
|
||||
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
|
||||
std::optional<uint32_t> GetCurrentPersistentId() override;
|
||||
void InsertDefaultPersistentId() override;
|
||||
void UpdatePersistentId(const uint32_t id) override;
|
||||
@@ -124,8 +123,6 @@ public:
|
||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
|
||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||
uint32_t GetAccountCount() override;
|
||||
bool IsNameInUse(const std::string_view name) override;
|
||||
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
|
||||
private:
|
||||
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
|
||||
|
||||
|
||||
@@ -77,9 +77,3 @@ void SQLiteDatabase::SetPendingCharacterName(const uint32_t characterId, const s
|
||||
void SQLiteDatabase::UpdateLastLoggedInCharacter(const uint32_t 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"
|
||||
|
||||
void SQLiteDatabase::InsertNewMail(const MailInfo& mail) {
|
||||
void SQLiteDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
||||
ExecuteInsert(
|
||||
"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`)"
|
||||
@@ -18,16 +18,16 @@ void SQLiteDatabase::InsertNewMail(const MailInfo& mail) {
|
||||
mail.itemCount);
|
||||
}
|
||||
|
||||
std::vector<MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||
std::vector<IMail::MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||
auto [_, res] = ExecuteSelect(
|
||||
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
|
||||
" FROM mail WHERE receiver_id=? limit ?;",
|
||||
characterId, numberOfMail);
|
||||
|
||||
std::vector<MailInfo> toReturn;
|
||||
std::vector<IMail::MailInfo> toReturn;
|
||||
|
||||
while (!res.eof()) {
|
||||
MailInfo mail;
|
||||
IMail::MailInfo mail;
|
||||
mail.id = res.getInt64Field("id");
|
||||
mail.subject = res.getStringField("subject");
|
||||
mail.body = res.getStringField("body");
|
||||
@@ -46,14 +46,14 @@ std::vector<MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t characterI
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::optional<MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
|
||||
std::optional<IMail::MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
|
||||
auto [_, res] = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
||||
|
||||
if (res.eof()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MailInfo toReturn;
|
||||
IMail::MailInfo toReturn;
|
||||
toReturn.itemLOT = res.getIntField("attachment_lot");
|
||||
toReturn.itemCount = res.getIntField("attachment_count");
|
||||
|
||||
|
||||
@@ -175,10 +175,6 @@ void SQLiteDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
||||
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) {
|
||||
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ?;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||
}
|
||||
|
||||
@@ -52,41 +52,14 @@ void SQLiteDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IP
|
||||
}
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
void SQLiteDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
ExecuteUpdate(
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
|
||||
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, modelID);
|
||||
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
|
||||
}
|
||||
|
||||
IPropertyContents::Model SQLiteDatabase::GetModel(const LWOOBJID modelID) {
|
||||
auto [_, result] = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
|
||||
|
||||
IPropertyContents::Model model{};
|
||||
if (!result.eof()) {
|
||||
do {
|
||||
model.id = result.getInt64Field("id");
|
||||
model.lot = static_cast<LOT>(result.getIntField("lot"));
|
||||
model.position.x = result.getFloatField("x");
|
||||
model.position.y = result.getFloatField("y");
|
||||
model.position.z = result.getFloatField("z");
|
||||
model.rotation.w = result.getFloatField("rw");
|
||||
model.rotation.x = result.getFloatField("rx");
|
||||
model.rotation.y = result.getFloatField("ry");
|
||||
model.rotation.z = result.getFloatField("rz");
|
||||
model.ugcId = result.getInt64Field("ugc_id");
|
||||
model.behaviors[0] = result.getIntField("behavior_1");
|
||||
model.behaviors[1] = result.getIntField("behavior_2");
|
||||
model.behaviors[2] = result.getIntField("behavior_3");
|
||||
model.behaviors[3] = result.getIntField("behavior_4");
|
||||
model.behaviors[4] = result.getIntField("behavior_5");
|
||||
} while (result.nextRow());
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
void SQLiteDatabase::SetMasterInfo(const MasterInfo& info) {
|
||||
void SQLiteDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
|
||||
// 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.
|
||||
ExecuteDelete("DELETE FROM servers;");
|
||||
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022, ?)", info.ip, info.port, info.password);
|
||||
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
|
||||
}
|
||||
|
||||
std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
|
||||
auto [_, result] = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
|
||||
auto [_, result] = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
|
||||
|
||||
if (result.eof()) {
|
||||
return std::nullopt;
|
||||
@@ -18,7 +18,6 @@ std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
|
||||
|
||||
toReturn.ip = result.getStringField("ip");
|
||||
toReturn.port = result.getIntField("port");
|
||||
toReturn.password = result.getStringField("master_password");
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) {
|
||||
auto [_, result] = ExecuteSelect(
|
||||
"SELECT lxfml, u.id AS ugcID, pc.id AS modelID FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;",
|
||||
"SELECT lxfml, u.id FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;",
|
||||
propertyId);
|
||||
|
||||
std::vector<IUgc::Model> toReturn;
|
||||
@@ -13,8 +13,7 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
|
||||
int blobSize{};
|
||||
const auto* blob = result.getBlobField("lxfml", blobSize);
|
||||
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
|
||||
model.id = result.getInt64Field("ugcID");
|
||||
model.modelID = result.getInt64Field("modelID");
|
||||
model.id = result.getInt64Field("id");
|
||||
toReturn.push_back(std::move(model));
|
||||
result.nextRow();
|
||||
}
|
||||
@@ -23,13 +22,12 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
|
||||
}
|
||||
|
||||
std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() {
|
||||
auto [_, result] = ExecuteSelect("SELECT u.id AS ugcID, pc.id AS modelID, lxfml FROM ugc AS u JOIN properties_contents AS pc ON pc.id = u.id;");
|
||||
auto [_, result] = ExecuteSelect("SELECT id, lxfml FROM ugc;");
|
||||
|
||||
std::vector<IUgc::Model> models;
|
||||
while (!result.eof()) {
|
||||
IUgc::Model model;
|
||||
model.id = result.getInt64Field("ugcID");
|
||||
model.modelID = result.getInt64Field("modelID");
|
||||
model.id = result.getInt64Field("id");
|
||||
|
||||
int blobSize{};
|
||||
const auto* blob = result.getBlobField("lxfml", blobSize);
|
||||
@@ -46,7 +44,7 @@ void SQLiteDatabase::RemoveUnreferencedUgcModels() {
|
||||
}
|
||||
|
||||
void SQLiteDatabase::InsertNewUgcModel(
|
||||
std::stringstream& sd0Data, // cant be const sad
|
||||
std::istringstream& sd0Data, // cant be const sad
|
||||
const uint32_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const uint32_t characterId) {
|
||||
@@ -68,7 +66,7 @@ void SQLiteDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
|
||||
void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) {
|
||||
const std::istream stream(lxfml.rdbuf());
|
||||
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ void TestSQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
|
||||
void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) {
|
||||
|
||||
}
|
||||
|
||||
@@ -148,10 +148,6 @@ 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) {
|
||||
|
||||
}
|
||||
@@ -168,7 +164,7 @@ void TestSQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const I
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
void TestSQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
|
||||
}
|
||||
|
||||
@@ -188,19 +184,19 @@ void TestSQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& i
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewMail(const MailInfo& mail) {
|
||||
void TestSQLDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) {
|
||||
void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||
std::vector<IMail::MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
|
||||
std::optional<IMail::MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -240,7 +236,7 @@ void TestSQLDatabase::InsertNewAccount(const std::string_view username, const st
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::SetMasterInfo(const IServers::MasterInfo& info) {
|
||||
void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class TestSQLDatabase : public GameDatabase {
|
||||
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
||||
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
||||
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
|
||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
|
||||
std::vector<IUgc::Model> GetAllUgcModels() override;
|
||||
void CreateMigrationHistoryTable() override;
|
||||
bool IsMigrationRun(const std::string_view str) override;
|
||||
@@ -47,24 +47,23 @@ class TestSQLDatabase : public GameDatabase {
|
||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||
void UpdatePropertyModerationInfo(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;
|
||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||
void RemoveUnreferencedUgcModels() override;
|
||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||
void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void RemoveModel(const LWOOBJID& modelId) override;
|
||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||
void InsertNewMail(const MailInfo& mail) override;
|
||||
void InsertNewMail(const IMail::MailInfo& mail) override;
|
||||
void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
std::istringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const uint32_t characterId) override;
|
||||
std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
|
||||
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
||||
void MarkMailRead(const uint64_t mailId) override;
|
||||
void DeleteMail(const uint64_t mailId) override;
|
||||
@@ -74,7 +73,7 @@ class TestSQLDatabase : public GameDatabase {
|
||||
void UpdateAccountBan(const uint32_t accountId, const bool banned) 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 SetMasterInfo(const IServers::MasterInfo& info) override;
|
||||
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
|
||||
std::optional<uint32_t> GetCurrentPersistentId() override;
|
||||
void InsertDefaultPersistentId() override;
|
||||
void UpdatePersistentId(const uint32_t id) override;
|
||||
@@ -103,9 +102,6 @@ class TestSQLDatabase : public GameDatabase {
|
||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override {};
|
||||
void DeleteUgcBuild(const LWOOBJID bigId) override {};
|
||||
uint32_t GetAccountCount() override { return 0; };
|
||||
|
||||
bool IsNameInUse(const std::string_view name) override { return false; };
|
||||
IPropertyContents::Model GetModel(const LWOOBJID modelID) override { return {}; }
|
||||
};
|
||||
|
||||
#endif //!TESTSQLDATABASE_H
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "GeneralUtils.h"
|
||||
#include "Logger.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "ModelNormalizeMigration.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
@@ -36,7 +35,7 @@ void MigrationRunner::RunMigrations() {
|
||||
Database::Get()->CreateMigrationHistoryTable();
|
||||
|
||||
// has to be here because when moving the files to the new folder, the migration_history table is not updated so it will run them all again.
|
||||
|
||||
|
||||
const auto migrationFolder = Database::GetMigrationFolder();
|
||||
if (!Database::Get()->IsMigrationRun("17_migration_for_migrations.sql") && migrationFolder == "mysql") {
|
||||
LOG("Running migration: 17_migration_for_migrations.sql");
|
||||
@@ -46,7 +45,6 @@ void MigrationRunner::RunMigrations() {
|
||||
|
||||
std::string finalSQL = "";
|
||||
bool runSd0Migrations = false;
|
||||
bool runNormalizeMigrations = false;
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
|
||||
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
|
||||
|
||||
@@ -59,8 +57,6 @@ void MigrationRunner::RunMigrations() {
|
||||
LOG("Running migration: %s", migration.name.c_str());
|
||||
if (migration.name == "5_brick_model_sd0.sql") {
|
||||
runSd0Migrations = true;
|
||||
} else if (migration.name.ends_with("_normalize_model_positions.sql")) {
|
||||
runNormalizeMigrations = true;
|
||||
} else {
|
||||
finalSQL.append(migration.data.c_str());
|
||||
}
|
||||
@@ -68,7 +64,7 @@ void MigrationRunner::RunMigrations() {
|
||||
Database::Get()->InsertMigration(migration.name);
|
||||
}
|
||||
|
||||
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations) {
|
||||
if (finalSQL.empty() && !runSd0Migrations) {
|
||||
LOG("Server database is up to date.");
|
||||
return;
|
||||
}
|
||||
@@ -92,10 +88,6 @@ void MigrationRunner::RunMigrations() {
|
||||
uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml();
|
||||
LOG("%i models were truncated from the database.", numberOfTruncatedModels);
|
||||
}
|
||||
|
||||
if (runNormalizeMigrations) {
|
||||
ModelNormalizeMigration::Run();
|
||||
}
|
||||
}
|
||||
|
||||
void MigrationRunner::RunSQLiteMigrations() {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "ModelNormalizeMigration.h"
|
||||
|
||||
#include "Database.h"
|
||||
#include "Lxfml.h"
|
||||
#include "Sd0.h"
|
||||
|
||||
void ModelNormalizeMigration::Run() {
|
||||
const auto oldCommit = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
for (auto& [lxfmlData, id, modelID] : Database::Get()->GetAllUgcModels()) {
|
||||
const auto model = Database::Get()->GetModel(modelID);
|
||||
// only BBB models (lot 14) and models with a position of NiPoint3::ZERO need to have their position fixed.
|
||||
if (model.position != NiPoint3Constant::ZERO || model.lot != 14) continue;
|
||||
|
||||
Sd0 sd0(lxfmlData);
|
||||
const auto asStr = sd0.GetAsStringUncompressed();
|
||||
const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr);
|
||||
if (newCenter == NiPoint3Constant::ZERO) {
|
||||
LOG("Failed to update model %llu due to failure reading xml.");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG("Updated model %llu to have a center of %f %f %f", modelID, newCenter.x, newCenter.y, newCenter.z);
|
||||
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
|
||||
auto asStream = sd0.GetAsStream();
|
||||
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
|
||||
Database::Get()->UpdateUgcModelData(id, asStream);
|
||||
}
|
||||
Database::Get()->SetAutoCommit(oldCommit);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Darkflame Universe
|
||||
// Copyright 2025
|
||||
|
||||
#ifndef MODELNORMALIZEMIGRATION_H
|
||||
#define MODELNORMALIZEMIGRATION_H
|
||||
|
||||
namespace ModelNormalizeMigration {
|
||||
void Run();
|
||||
};
|
||||
|
||||
#endif //!MODELNORMALIZEMIGRATION_H
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "eObjectBits.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "ePlayerFlag.h"
|
||||
#include "CDPlayerFlagsTable.h"
|
||||
|
||||
Character::Character(uint32_t id, User* parentUser) {
|
||||
//First load the name, etc:
|
||||
@@ -204,7 +203,6 @@ void Character::DoQuickXMLDataParse() {
|
||||
while (currentChild) {
|
||||
const auto* temp = currentChild->Attribute("v");
|
||||
const auto* id = currentChild->Attribute("id");
|
||||
const auto* si = currentChild->Attribute("si");
|
||||
if (temp && id) {
|
||||
uint32_t index = 0;
|
||||
uint64_t value = 0;
|
||||
@@ -213,9 +211,6 @@ void Character::DoQuickXMLDataParse() {
|
||||
value = std::stoull(temp);
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -236,12 +231,6 @@ void Character::SetBuildMode(bool buildMode) {
|
||||
}
|
||||
|
||||
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:
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
@@ -288,19 +277,39 @@ void Character::SaveXMLToDatabase() {
|
||||
}
|
||||
|
||||
flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
|
||||
for (const auto& [index, flagBucket] : m_PlayerFlags) {
|
||||
auto* f = flags->InsertNewChildElement("f");
|
||||
f->SetAttribute("id", index);
|
||||
f->SetAttribute("v", flagBucket);
|
||||
for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) {
|
||||
auto* f = m_Doc.NewElement("f");
|
||||
f->SetAttribute("id", flag.first);
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
for (const auto& sessionFlag : m_SessionFlags) {
|
||||
auto* s = flags->InsertNewChildElement("s");
|
||||
s->SetAttribute("si", sessionFlag);
|
||||
// Prevents the news feed from showing up on world transfers
|
||||
if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) {
|
||||
auto* s = m_Doc.NewElement("s");
|
||||
s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE);
|
||||
flags->LinkEndChild(s);
|
||||
}
|
||||
|
||||
if (GetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR)) {
|
||||
auto* s = m_Doc.NewElement("s");
|
||||
s->SetAttribute("si", ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR);
|
||||
flags->LinkEndChild(s);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
WriteToDatabase();
|
||||
@@ -320,8 +329,8 @@ void Character::SetIsNewLogin() {
|
||||
while (currentChild) {
|
||||
auto* nextChild = currentChild->NextSiblingElement();
|
||||
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);
|
||||
LOG("Removed isLoggedIn flag from character %i:%s, saving character to database", GetID(), GetName().c_str());
|
||||
WriteToDatabase();
|
||||
}
|
||||
currentChild = nextChild;
|
||||
@@ -354,9 +363,7 @@ void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
}
|
||||
}
|
||||
|
||||
const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);
|
||||
|
||||
if (flagEntry && flagEntry->sessionOnly) {
|
||||
if (flagId == EQUPPED_TRIAL_FACTION_GEAR || flagId == IS_NEWS_SCREEN_VISIBLE) {
|
||||
if (value) m_SessionFlags.insert(flagId);
|
||||
else m_SessionFlags.erase(flagId);
|
||||
} else {
|
||||
@@ -395,8 +402,8 @@ bool Character::GetPlayerFlag(const uint32_t flagId) const {
|
||||
|
||||
bool toReturn = false; //by def, return false.
|
||||
|
||||
const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);
|
||||
if (flagEntry && flagEntry->sessionOnly) {
|
||||
// TODO make actual session flag checker using flags table in database.
|
||||
if (flagId == EQUPPED_TRIAL_FACTION_GEAR || flagId == IS_NEWS_SCREEN_VISIBLE) {
|
||||
toReturn = m_SessionFlags.contains(flagId);
|
||||
} else {
|
||||
// Calculate the index first
|
||||
|
||||
@@ -386,9 +386,6 @@ void Entity::Initialize() {
|
||||
if (m_Character) {
|
||||
comp->LoadFromXml(m_Character->GetXMLDoc());
|
||||
} else {
|
||||
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
|
||||
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
|
||||
|
||||
if (componentID > 0) {
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
@@ -423,6 +420,9 @@ void Entity::Initialize() {
|
||||
comp->SetMinCoins(currencyValues[0].minvalue);
|
||||
comp->SetMaxCoins(currencyValues[0].maxvalue);
|
||||
}
|
||||
|
||||
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
|
||||
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
|
||||
}
|
||||
} else {
|
||||
comp->SetHealth(1);
|
||||
@@ -545,8 +545,9 @@ void Entity::Initialize() {
|
||||
|
||||
// ZoneControl script
|
||||
if (m_TemplateID == 2365) {
|
||||
CDZoneTableTable* zoneTable = CDClientManager::GetTable<CDZoneTableTable>();
|
||||
const auto zoneID = Game::zoneManager->GetZoneID();
|
||||
const CDZoneTable* zoneData = CDZoneTableTable::Query(zoneID.GetMapID());
|
||||
const CDZoneTable* zoneData = zoneTable->Query(zoneID.GetMapID());
|
||||
|
||||
if (zoneData != nullptr) {
|
||||
int zoneScriptID = zoneData->scriptID;
|
||||
@@ -1564,7 +1565,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
|
||||
|
||||
m_DieCallbacks.clear();
|
||||
|
||||
//お前はもう死んでいる
|
||||
//OMAI WA MOU, SHINDERIU
|
||||
|
||||
GetScript()->OnDie(this, murderer);
|
||||
|
||||
@@ -2207,7 +2208,7 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) {
|
||||
|
||||
int32_t Entity::GetCollisionGroup() const {
|
||||
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) {
|
||||
return compToCheck->GetCollisionGroup();
|
||||
}
|
||||
@@ -2215,17 +2216,3 @@ int32_t Entity::GetCollisionGroup() const {
|
||||
|
||||
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,17 +14,12 @@
|
||||
#include "Observable.h"
|
||||
|
||||
namespace GameMessages {
|
||||
struct GameMsg;
|
||||
struct ActivityNotify;
|
||||
struct ShootingGalleryFire;
|
||||
struct ChildLoaded;
|
||||
struct PlayerResurrectionFinished;
|
||||
};
|
||||
|
||||
namespace MessageType {
|
||||
enum class Game : uint16_t;
|
||||
}
|
||||
|
||||
namespace Loot {
|
||||
class Info;
|
||||
};
|
||||
@@ -321,10 +316,6 @@ public:
|
||||
// Scale will only be communicated to the client when the construction packet is sent
|
||||
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.
|
||||
*/
|
||||
@@ -386,8 +377,6 @@ protected:
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -99,7 +99,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
|
||||
}
|
||||
|
||||
// Exclude the zone control object from any flags
|
||||
if (!controller) {
|
||||
if (!controller && info.lot != 14) {
|
||||
|
||||
// The client flags means the client should render the entity
|
||||
GeneralUtils::SetBit(id, eObjectBits::CLIENT);
|
||||
@@ -320,7 +320,7 @@ const std::unordered_map<std::string, LWOOBJID>& EntityManager::GetSpawnPointEnt
|
||||
return m_SpawnPoints;
|
||||
}
|
||||
|
||||
void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr) {
|
||||
void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr, const bool skipChecks) {
|
||||
if (!entity) {
|
||||
LOG("Attempted to construct null entity");
|
||||
return;
|
||||
@@ -363,12 +363,16 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
entity->WriteComponents(stream, eReplicaPacketType::CONSTRUCTION);
|
||||
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
if (player->GetPlayerReadyForUpdates()) {
|
||||
Game::server->Send(stream, player->GetSystemAddress(), false);
|
||||
} else {
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID());
|
||||
if (skipChecks) {
|
||||
Game::server->Send(stream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
} else {
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
if (player->GetPlayerReadyForUpdates()) {
|
||||
Game::server->Send(stream, player->GetSystemAddress(), false);
|
||||
} else {
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
const std::unordered_map<LWOOBJID, Entity*> GetAllEntities() const { return m_Entities; }
|
||||
#endif
|
||||
|
||||
void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, bool skipChecks = false);
|
||||
void DestructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
void SerializeEntity(Entity* entity);
|
||||
void SerializeEntity(const Entity& entity);
|
||||
|
||||
@@ -24,13 +24,12 @@ namespace LeaderboardManager {
|
||||
std::map<GameID, Leaderboard::Type> leaderboardCache;
|
||||
}
|
||||
|
||||
Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const uint32_t numResults, const Leaderboard::Type leaderboardType) {
|
||||
Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const Leaderboard::Type leaderboardType) {
|
||||
this->gameID = gameID;
|
||||
this->weekly = weekly;
|
||||
this->infoType = infoType;
|
||||
this->leaderboardType = leaderboardType;
|
||||
this->relatedPlayer = relatedPlayer;
|
||||
this->numResults = numResults;
|
||||
}
|
||||
|
||||
Leaderboard::~Leaderboard() {
|
||||
@@ -145,7 +144,7 @@ void QueryToLdf(Leaderboard& leaderboard, const std::vector<ILeaderboard::Entry>
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ILeaderboard::Entry> FilterToNumResults(const std::vector<ILeaderboard::Entry>& leaderboard, const uint32_t relatedPlayer, const Leaderboard::InfoType infoType, const uint32_t numResults) {
|
||||
std::vector<ILeaderboard::Entry> FilterTo10(const std::vector<ILeaderboard::Entry>& leaderboard, const uint32_t relatedPlayer, const Leaderboard::InfoType infoType) {
|
||||
std::vector<ILeaderboard::Entry> toReturn;
|
||||
|
||||
int32_t index = 0;
|
||||
@@ -156,19 +155,18 @@ std::vector<ILeaderboard::Entry> FilterToNumResults(const std::vector<ILeaderboa
|
||||
}
|
||||
}
|
||||
|
||||
if (leaderboard.size() < numResults) {
|
||||
if (leaderboard.size() < 10) {
|
||||
toReturn.assign(leaderboard.begin(), leaderboard.end());
|
||||
index = 0;
|
||||
} else if (index < numResults) {
|
||||
toReturn.assign(leaderboard.begin(), leaderboard.begin() + numResults); // get the top 10 since we are in the top 10
|
||||
} else if (index < 10) {
|
||||
toReturn.assign(leaderboard.begin(), leaderboard.begin() + 10); // get the top 10 since we are in the top 10
|
||||
index = 0;
|
||||
} else if (index > leaderboard.size() - numResults) {
|
||||
toReturn.assign(leaderboard.end() - numResults, leaderboard.end()); // get the bottom 10 since we are in the bottom 10
|
||||
index = leaderboard.size() - numResults;
|
||||
} else if (index > leaderboard.size() - 10) {
|
||||
toReturn.assign(leaderboard.end() - 10, leaderboard.end()); // get the bottom 10 since we are in the bottom 10
|
||||
index = leaderboard.size() - 10;
|
||||
} else {
|
||||
auto half = numResults / 2;
|
||||
toReturn.assign(leaderboard.begin() + index - half, leaderboard.begin() + index + half); // get the 5 above and below
|
||||
index -= half;
|
||||
toReturn.assign(leaderboard.begin() + index - 5, leaderboard.begin() + index + 5); // get the 5 above and below
|
||||
index -= 5;
|
||||
}
|
||||
|
||||
int32_t i = index;
|
||||
@@ -180,16 +178,14 @@ std::vector<ILeaderboard::Entry> FilterToNumResults(const std::vector<ILeaderboa
|
||||
}
|
||||
|
||||
std::vector<ILeaderboard::Entry> FilterWeeklies(const std::vector<ILeaderboard::Entry>& leaderboard) {
|
||||
using namespace std::chrono;
|
||||
// Filter the leaderboard to only include entries from the last week
|
||||
const auto epochTime = system_clock::now();
|
||||
constexpr auto oneWeek = weeks(1);
|
||||
const auto currentTime = std::chrono::system_clock::now();
|
||||
auto epochTime = currentTime.time_since_epoch().count();
|
||||
constexpr auto SECONDS_IN_A_WEEK = 60 * 60 * 24 * 7; // if you think im taking leap seconds into account thats cute.
|
||||
|
||||
std::vector<ILeaderboard::Entry> weeklyLeaderboard;
|
||||
for (const auto& entry : leaderboard) {
|
||||
const sys_time<seconds> asSysTime(seconds(entry.lastPlayedTimestamp));
|
||||
const auto timeDiff = epochTime - asSysTime;
|
||||
if (timeDiff < oneWeek) {
|
||||
if (epochTime - entry.lastPlayedTimestamp < SECONDS_IN_A_WEEK) {
|
||||
weeklyLeaderboard.push_back(entry);
|
||||
}
|
||||
}
|
||||
@@ -217,15 +213,14 @@ std::vector<ILeaderboard::Entry> ProcessLeaderboard(
|
||||
const std::vector<ILeaderboard::Entry>& leaderboard,
|
||||
const bool weekly,
|
||||
const Leaderboard::InfoType infoType,
|
||||
const uint32_t relatedPlayer,
|
||||
const uint32_t numResults) {
|
||||
const uint32_t relatedPlayer) {
|
||||
std::vector<ILeaderboard::Entry> toReturn;
|
||||
|
||||
if (infoType == Leaderboard::InfoType::Friends) {
|
||||
const auto friendsLeaderboard = FilterFriends(leaderboard, relatedPlayer);
|
||||
toReturn = FilterToNumResults(weekly ? FilterWeeklies(friendsLeaderboard) : friendsLeaderboard, relatedPlayer, infoType, numResults);
|
||||
toReturn = FilterTo10(weekly ? FilterWeeklies(friendsLeaderboard) : friendsLeaderboard, relatedPlayer, infoType);
|
||||
} else {
|
||||
toReturn = FilterToNumResults(weekly ? FilterWeeklies(leaderboard) : leaderboard, relatedPlayer, infoType, numResults);
|
||||
toReturn = FilterTo10(weekly ? FilterWeeklies(leaderboard) : leaderboard, relatedPlayer, infoType);
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
@@ -260,7 +255,7 @@ void Leaderboard::SetupLeaderboard(bool weekly) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto processedLeaderboard = ProcessLeaderboard(leaderboardRes, weekly, infoType, relatedPlayer, numResults);
|
||||
const auto processedLeaderboard = ProcessLeaderboard(leaderboardRes, weekly, infoType, relatedPlayer);
|
||||
|
||||
QueryToLdf(*this, processedLeaderboard);
|
||||
}
|
||||
@@ -306,8 +301,8 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi
|
||||
}
|
||||
}
|
||||
|
||||
void LeaderboardManager::SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID, const uint32_t numResults) {
|
||||
Leaderboard leaderboard(gameID, infoType, weekly, playerID, numResults, GetLeaderboardType(gameID));
|
||||
void LeaderboardManager::SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID) {
|
||||
Leaderboard leaderboard(gameID, infoType, weekly, playerID, GetLeaderboardType(gameID));
|
||||
leaderboard.SetupLeaderboard(weekly);
|
||||
leaderboard.Send(targetID);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
None
|
||||
};
|
||||
Leaderboard() = delete;
|
||||
Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const uint32_t numResults, const Leaderboard::Type = None);
|
||||
Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const Leaderboard::Type = None);
|
||||
|
||||
~Leaderboard();
|
||||
|
||||
@@ -79,7 +79,6 @@ private:
|
||||
InfoType infoType;
|
||||
Leaderboard::Type leaderboardType;
|
||||
bool weekly;
|
||||
uint32_t numResults;
|
||||
public:
|
||||
LeaderboardEntry& PushBackEntry() {
|
||||
return entries.emplace_back();
|
||||
@@ -91,7 +90,7 @@ public:
|
||||
};
|
||||
|
||||
namespace LeaderboardManager {
|
||||
void SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID, const uint32_t numResults);
|
||||
void SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID);
|
||||
|
||||
void SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0);
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "MessageType/Chat.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "CheatDetection.h"
|
||||
#include "CharacterComponent.h"
|
||||
|
||||
UserManager* UserManager::m_Address = nullptr;
|
||||
|
||||
@@ -306,13 +305,13 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
|
||||
LOT pantsLOT = FindCharPantsID(pantsColor);
|
||||
|
||||
if (!name.empty() && Database::Get()->IsNameInUse(name)) {
|
||||
if (!name.empty() && Database::Get()->GetCharacterInfo(name)) {
|
||||
LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str());
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Database::Get()->IsNameInUse(predefinedName)) {
|
||||
if (Database::Get()->GetCharacterInfo(predefinedName)) {
|
||||
LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE);
|
||||
return;
|
||||
@@ -325,7 +324,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
}
|
||||
|
||||
//Now that the name is ok, we can get an objectID from Master:
|
||||
ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) {
|
||||
ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) mutable {
|
||||
if (Database::Get()->GetCharacterInfo(objectID)) {
|
||||
LOG("Character object id unavailable, check object_id_tracker!");
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
||||
@@ -341,10 +340,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" ";
|
||||
xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" ";
|
||||
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\">";
|
||||
xml << "<vl><l id=\"1000\" cid=\"0\"/></vl>";
|
||||
|
||||
xml << "</char>";
|
||||
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\"></char>";
|
||||
|
||||
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
|
||||
|
||||
@@ -373,14 +369,13 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
// 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
|
||||
auto assignedPredefinedName = predefinedName;
|
||||
if (assignedPredefinedName == "INVALID") {
|
||||
if (predefinedName == "INVALID") {
|
||||
std::stringstream nameObjID;
|
||||
nameObjID << "minifig" << objectID;
|
||||
assignedPredefinedName = nameObjID.str();
|
||||
predefinedName = nameObjID.str();
|
||||
}
|
||||
|
||||
std::string_view nameToAssign = !name.empty() && nameOk ? name : assignedPredefinedName;
|
||||
std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName;
|
||||
std::string pendingName = !name.empty() && !nameOk ? name : "";
|
||||
|
||||
ICharInfo::Info info;
|
||||
@@ -526,13 +521,6 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (character) {
|
||||
auto* entity = character->GetEntity();
|
||||
if (entity) {
|
||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent) {
|
||||
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));
|
||||
}
|
||||
}
|
||||
character->SetZoneID(zoneID);
|
||||
character->SetZoneInstance(zoneInstance);
|
||||
character->SetZoneClone(zoneClone);
|
||||
|
||||
@@ -20,7 +20,7 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bi
|
||||
|
||||
//Handle player damage cooldown
|
||||
if (entity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||
const float immunityTime = Game::zoneManager->GetWorldConfig().globalImmunityTime;
|
||||
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
|
||||
//Handle player damage cooldown
|
||||
if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||
destroyableComponent->SetDamageCooldownTimer(Game::zoneManager->GetWorldConfig().globalImmunityTime);
|
||||
destroyableComponent->SetDamageCooldownTimer(Game::zoneManager->GetWorldConfig()->globalImmunityTime);
|
||||
}
|
||||
|
||||
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
||||
|
||||
@@ -42,15 +42,10 @@ void PropertyTeleportBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
|
||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (entity->GetCharacter()) {
|
||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent) {
|
||||
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));
|
||||
characterComponent->SetLastRocketConfig(u"");
|
||||
}
|
||||
|
||||
entity->GetCharacter()->SetZoneID(zoneID);
|
||||
entity->GetCharacter()->SetZoneInstance(zoneInstance);
|
||||
entity->GetCharacter()->SetZoneClone(zoneClone);
|
||||
entity->GetComponent<CharacterComponent>()->SetLastRocketConfig(u"");
|
||||
}
|
||||
|
||||
entity->GetCharacter()->SaveXMLToDatabase();
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "CDActivityRewardsTable.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "LeaderboardManager.h"
|
||||
#include "CharacterComponent.h"
|
||||
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Component(parent) {
|
||||
/*
|
||||
@@ -527,11 +526,6 @@ void ActivityInstance::StartZone() {
|
||||
|
||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (player->GetCharacter()) {
|
||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||
if (characterComponent) {
|
||||
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));
|
||||
}
|
||||
|
||||
player->GetCharacter()->SetZoneID(zoneID);
|
||||
player->GetCharacter()->SetZoneInstance(zoneInstance);
|
||||
player->GetCharacter()->SetZoneClone(zoneClone);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "Mail.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "WorldPackets.h"
|
||||
#include "MessageType/Game.h"
|
||||
#include <ctime>
|
||||
|
||||
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
|
||||
@@ -48,45 +47,6 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character, con
|
||||
m_CountryCode = 0;
|
||||
m_LastUpdateTimestamp = std::time(nullptr);
|
||||
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) {
|
||||
@@ -95,21 +55,15 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) {
|
||||
case 556:
|
||||
case 1101:
|
||||
case 1202:
|
||||
case 1150:
|
||||
case 1151:
|
||||
case 1203:
|
||||
case 1204:
|
||||
case 1250:
|
||||
case 1251:
|
||||
case 1261:
|
||||
case 1301:
|
||||
case 1302:
|
||||
case 1303:
|
||||
case 1350:
|
||||
case 1401:
|
||||
case 1402:
|
||||
case 1403:
|
||||
case 1450:
|
||||
case 1603:
|
||||
case 2001:
|
||||
return true;
|
||||
@@ -127,8 +81,6 @@ CharacterComponent::~CharacterComponent() {
|
||||
void CharacterComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
if (!m_Character || !m_Character->GetParentUser()) return;
|
||||
|
||||
outBitStream.Write(m_ClaimCodes[0] != 0);
|
||||
if (m_ClaimCodes[0] != 0) outBitStream.Write(m_ClaimCodes[0]);
|
||||
outBitStream.Write(m_ClaimCodes[1] != 0);
|
||||
@@ -148,7 +100,7 @@ void CharacterComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInit
|
||||
outBitStream.Write(m_Character->GetEyebrows());
|
||||
outBitStream.Write(m_Character->GetEyes());
|
||||
outBitStream.Write(m_Character->GetMouth());
|
||||
outBitStream.Write<uint64_t>(m_Character->GetParentUser()->GetAccountID());
|
||||
outBitStream.Write<uint64_t>(0); //AccountID, trying out if 0 works.
|
||||
outBitStream.Write(m_Character->GetLastLogin()); //Last login
|
||||
outBitStream.Write<uint64_t>(0); //"prop mod last display time"
|
||||
outBitStream.Write<uint64_t>(m_Uscore); //u-score
|
||||
@@ -245,8 +197,6 @@ void CharacterComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
|
||||
SetReputation(0);
|
||||
}
|
||||
|
||||
auto* vl = character->FirstChildElement("vl");
|
||||
if (vl) LoadVisitedLevelsXml(*vl);
|
||||
character->QueryUnsigned64Attribute("co", &m_ClaimCodes[0]);
|
||||
character->QueryUnsigned64Attribute("co1", &m_ClaimCodes[1]);
|
||||
character->QueryUnsigned64Attribute("co2", &m_ClaimCodes[2]);
|
||||
@@ -376,10 +326,6 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* vl = character->FirstChildElement("vl");
|
||||
if (!vl) vl = character->InsertNewChildElement("vl");
|
||||
UpdateVisitedLevelsXml(*vl);
|
||||
|
||||
if (m_ClaimCodes[0] != 0) character->SetAttribute("co", m_ClaimCodes[0]);
|
||||
if (m_ClaimCodes[1] != 0) character->SetAttribute("co1", m_ClaimCodes[1]);
|
||||
if (m_ClaimCodes[2] != 0) character->SetAttribute("co2", m_ClaimCodes[2]);
|
||||
@@ -840,7 +786,7 @@ void CharacterComponent::AwardClaimCodes() {
|
||||
subject << "%[RewardCodes_" << rewardCode << "_subjectText]";
|
||||
std::ostringstream body;
|
||||
body << "%[RewardCodes_" << rewardCode << "_bodyText]";
|
||||
Mail::SendMail(m_Parent, subject.str(), body.str(), attachmentLOT, 1);
|
||||
Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,9 +807,8 @@ void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const {
|
||||
character->SetZoneID(zoneID);
|
||||
character->SetZoneInstance(zoneInstance);
|
||||
character->SetZoneClone(zoneClone);
|
||||
|
||||
|
||||
characterComponent->SetLastRocketConfig(u"");
|
||||
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));
|
||||
|
||||
character->SaveXMLToDatabase();
|
||||
}
|
||||
@@ -890,30 +835,3 @@ void CharacterComponent::SetRespawnPos(const NiPoint3& position) {
|
||||
void CharacterComponent::SetRespawnRot(const NiQuaternion& rotation) {
|
||||
m_respawnRot = rotation;
|
||||
}
|
||||
|
||||
void CharacterComponent::AddVisitedLevel(const LWOZONEID zoneID) {
|
||||
LWOZONEID toInsert(zoneID.GetMapID(), LWOINSTANCEID_INVALID, zoneID.GetCloneID());
|
||||
m_VisitedLevels.insert(toInsert);
|
||||
}
|
||||
|
||||
void CharacterComponent::UpdateVisitedLevelsXml(tinyxml2::XMLElement& vl) {
|
||||
vl.DeleteChildren();
|
||||
// <vl>
|
||||
for (const auto zoneID : m_VisitedLevels) {
|
||||
// <l id=\"1100\" cid=\"0\"/>
|
||||
auto* l = vl.InsertNewChildElement("l");
|
||||
l->SetAttribute("id", zoneID.GetMapID());
|
||||
l->SetAttribute("cid", zoneID.GetCloneID());
|
||||
}
|
||||
// </vl>
|
||||
}
|
||||
|
||||
void CharacterComponent::LoadVisitedLevelsXml(const tinyxml2::XMLElement& vl) {
|
||||
// <vl>
|
||||
for (const auto* l = vl.FirstChildElement("l"); l != nullptr; l = l->NextSiblingElement("l")) {
|
||||
// <l id=\"1100\" cid=\"0\"/>
|
||||
LWOZONEID toInsert(l->IntAttribute("id"), LWOINSTANCEID_INVALID, l->IntAttribute("cid"));
|
||||
m_VisitedLevels.insert(toInsert);
|
||||
}
|
||||
// </vl>
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "tinyxml2.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include <array>
|
||||
#include <set>
|
||||
#include "Loot.h"
|
||||
|
||||
enum class eGameActivity : uint32_t;
|
||||
@@ -322,17 +321,8 @@ public:
|
||||
* Character info regarding this character, including clothing styles, etc.
|
||||
*/
|
||||
Character* m_Character;
|
||||
|
||||
/* Saves the provided zoneID as a visited level. Ignores InstanceID */
|
||||
void AddVisitedLevel(const LWOZONEID zoneID);
|
||||
/* Updates the VisitedLevels (vl) node of the charxml */
|
||||
void UpdateVisitedLevelsXml(tinyxml2::XMLElement& doc);
|
||||
/* Reads the VisitedLevels (vl) node of the charxml */
|
||||
void LoadVisitedLevelsXml(const tinyxml2::XMLElement& doc);
|
||||
private:
|
||||
|
||||
bool OnRequestServerObjectInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
/**
|
||||
* The map of active venture vision effects
|
||||
*/
|
||||
@@ -627,8 +617,6 @@ private:
|
||||
std::map<LWOOBJID, Loot::Info> m_DroppedLoot;
|
||||
|
||||
uint64_t m_DroppedCoins = 0;
|
||||
|
||||
std::set<LWOZONEID> m_VisitedLevels;
|
||||
};
|
||||
|
||||
#endif // CHARACTERCOMPONENT_H
|
||||
|
||||
@@ -8,10 +8,6 @@ namespace RakNet {
|
||||
class BitStream;
|
||||
}
|
||||
|
||||
namespace GameMessages {
|
||||
struct GameMsg;
|
||||
}
|
||||
|
||||
class Entity;
|
||||
|
||||
/**
|
||||
@@ -56,10 +52,6 @@ public:
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -774,10 +774,10 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
if (Game::zoneManager->GetPlayerLoseCoinOnDeath()) {
|
||||
auto* character = m_Parent->GetCharacter();
|
||||
uint64_t coinsTotal = character->GetCoins();
|
||||
const uint64_t minCoinsToLose = Game::zoneManager->GetWorldConfig().coinsLostOnDeathMin;
|
||||
const uint64_t minCoinsToLose = Game::zoneManager->GetWorldConfig()->coinsLostOnDeathMin;
|
||||
if (coinsTotal >= minCoinsToLose) {
|
||||
const uint64_t maxCoinsToLose = Game::zoneManager->GetWorldConfig().coinsLostOnDeathMax;
|
||||
const float coinPercentageToLose = Game::zoneManager->GetWorldConfig().coinsLostOnDeathPercent;
|
||||
const uint64_t maxCoinsToLose = Game::zoneManager->GetWorldConfig()->coinsLostOnDeathMax;
|
||||
const float coinPercentageToLose = Game::zoneManager->GetWorldConfig()->coinsLostOnDeathPercent;
|
||||
|
||||
uint64_t coinsToLose = std::max(static_cast<uint64_t>(coinsTotal * coinPercentageToLose), minCoinsToLose);
|
||||
coinsToLose = std::min(maxCoinsToLose, coinsToLose);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user