mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-20 11:59:40 -06:00
Compare commits
95 Commits
websockets
...
fix-clone-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8ba79bb7b | ||
|
|
b798da8ef8 | ||
|
|
154112050f | ||
|
|
6d3bf2fdc3 | ||
|
|
566a18df38 | ||
|
|
f6c13d9ee6 | ||
|
|
8198ad70f6 | ||
|
|
4c3bace601 | ||
|
|
6d2a21450b | ||
|
|
f9e74e6994 | ||
|
|
21a2ddcfd9 | ||
|
|
50e6cf9059 | ||
|
|
3364884126 | ||
|
|
3890c0a86c | ||
|
|
c083f21e44 | ||
|
|
c9e95839ee | ||
|
|
dd957ed0c7 | ||
|
|
12296ce553 | ||
|
|
24f4c9d413 | ||
|
|
ba964932b7 | ||
|
|
4c42eea819 | ||
|
|
6b52cf67a0 | ||
|
|
71f708f1b5 | ||
|
|
49aa632d42 | ||
|
|
5ec4142ca1 | ||
|
|
5e9fe40bec | ||
|
|
9524198044 | ||
|
|
a5d0788488 | ||
|
|
a1ba5b8f12 | ||
|
|
48510b7315 | ||
|
|
c697f8ad97 | ||
|
|
55d181ea4b | ||
|
|
ecbb465020 | ||
|
|
ec9927acbb | ||
|
|
1f580491c7 | ||
|
|
2618e9a864 | ||
|
|
0f0d0a6dee | ||
|
|
f63a9a6bea | ||
|
|
f0f98a6108 | ||
|
|
4ed7bd6767 | ||
|
|
9f92f48a0f | ||
|
|
48e3471831 | ||
|
|
3c244cce27 | ||
|
|
8ba35be64d | ||
|
|
f7c9267ba4 | ||
|
|
b6e9d6872d | ||
|
|
c83797984a | ||
|
|
04487efa25 | ||
|
|
2f315d9288 | ||
|
|
6ae1c7a376 | ||
|
|
c19ee04c8a | ||
|
|
2858345269 | ||
|
|
37e14979a4 | ||
|
|
b509fd4f10 | ||
|
|
820c0f0083 | ||
|
|
68eb20966f | ||
|
|
92155a3cb4 | ||
|
|
437362cce6 | ||
|
|
34665f6f5c | ||
|
|
32487dcd5f | ||
|
|
891b176b4f | ||
|
|
e42df5b02e | ||
| 61921cfb62 | |||
|
|
91f6b2bf81 | ||
|
|
01917841cb | ||
|
|
e18c504ee4 | ||
|
|
b6f7b4c092 | ||
|
|
522299c9ec | ||
|
|
0e551429d3 | ||
|
|
c77e9ce33a | ||
|
|
3ebc6709db | ||
|
|
841b754b01 | ||
|
|
62c3f489fe | ||
|
|
5ccb8357fd | ||
|
|
4bacb8a2ee | ||
|
|
89678c4a05 | ||
|
|
4f97ecc073 | ||
|
|
c9e4cde68d | ||
|
|
0a12672889 | ||
|
|
00a69909f8 | ||
|
|
7c8ca1c1cb | ||
|
|
4930fb93b3 | ||
|
|
b31f9670d1 | ||
|
|
1cc1782b35 | ||
|
|
55d409eb82 | ||
|
|
65f3c33ca5 | ||
|
|
93fa4e268f | ||
|
|
1fb1da101c | ||
|
|
6f94043b33 | ||
|
|
5785764a95 | ||
|
|
fa53fa7935 | ||
|
|
6b0f3a66e9 | ||
|
|
f5c212fb86 | ||
|
|
3d595ce4ac | ||
|
|
99f6cf2d92 |
5
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
5
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -16,7 +16,10 @@ body:
|
|||||||
I have validated that this issue is not a syntax error of either MySQL or SQLite.
|
I have validated that this issue is not a syntax error of either MySQL or SQLite.
|
||||||
required: true
|
required: true
|
||||||
- label: >
|
- label: >
|
||||||
I have pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there.
|
I have downloaded/pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there.
|
||||||
|
required: true
|
||||||
|
- label: >
|
||||||
|
I have verified that my boot.cfg is configured as per the [README](https://github.com/DarkflameUniverse/DarkflameServer?tab=readme-ov-file#allowing-a-user-to-connect-to-your-server).
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: server-version
|
id: server-version
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debuggi
|
|||||||
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
|
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
|
||||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
|
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
set(FETCHCONTENT_QUIET FALSE) # GLM takes a long time to clone, this will at least show _something_ while its downloading
|
||||||
|
|
||||||
# Read variables from file
|
# Read variables from file
|
||||||
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
|
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
|
||||||
@@ -306,7 +307,7 @@ add_subdirectory(dServer)
|
|||||||
add_subdirectory(dWeb)
|
add_subdirectory(dWeb)
|
||||||
|
|
||||||
# Create a list of common libraries shared between all binaries
|
# Create a list of common libraries shared between all binaries
|
||||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "magic_enum")
|
set(COMMON_LIBRARIES glm::glm "dCommon" "dDatabase" "dNet" "raknet" "magic_enum")
|
||||||
|
|
||||||
# Add platform specific common libraries
|
# Add platform specific common libraries
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
|||||||
25
Dockerfile
25
Dockerfile
@@ -11,7 +11,12 @@ COPY --chmod=0500 ./build.sh /app/
|
|||||||
|
|
||||||
RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt
|
RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt
|
||||||
|
|
||||||
RUN ./build.sh
|
RUN --mount=type=cache,target=/app/build,id=build-cache \
|
||||||
|
mkdir -p /app/build /tmp/persisted-build && \
|
||||||
|
cd /app/build && \
|
||||||
|
cmake .. && \
|
||||||
|
make -j$(nproc --ignore 1) && \
|
||||||
|
cp -r /app/build/* /tmp/persisted-build/
|
||||||
|
|
||||||
FROM debian:12 as runtime
|
FROM debian:12 as runtime
|
||||||
|
|
||||||
@@ -23,23 +28,23 @@ RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \
|
|||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Grab libraries and load them
|
# Grab libraries and load them
|
||||||
COPY --from=build /app/build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
|
COPY --from=build /tmp/persisted-build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
|
||||||
RUN ldconfig
|
RUN ldconfig
|
||||||
|
|
||||||
# Server bins
|
# Server bins
|
||||||
COPY --from=build /app/build/*Server /app/
|
COPY --from=build /tmp/persisted-build/*Server /app/
|
||||||
|
|
||||||
# Necessary suplimentary files
|
# Necessary suplimentary files
|
||||||
COPY --from=build /app/build/*.ini /app/configs/
|
COPY --from=build /tmp/persisted-build/*.ini /app/configs/
|
||||||
COPY --from=build /app/build/vanity/*.* /app/vanity/
|
COPY --from=build /tmp/persisted-build/vanity/*.* /app/vanity/
|
||||||
COPY --from=build /app/build/navmeshes /app/navmeshes
|
COPY --from=build /tmp/persisted-build/navmeshes /app/navmeshes
|
||||||
COPY --from=build /app/build/migrations /app/migrations
|
COPY --from=build /tmp/persisted-build/migrations /app/migrations
|
||||||
COPY --from=build /app/build/*.dcf /app/
|
COPY --from=build /tmp/persisted-build/*.dcf /app/
|
||||||
|
|
||||||
# backup of config and vanity files to copy to the host incase
|
# backup of config and vanity files to copy to the host incase
|
||||||
# of a mount clobbering the copy from above
|
# of a mount clobbering the copy from above
|
||||||
COPY --from=build /app/build/*.ini /app/default-configs/
|
COPY --from=build /tmp/persisted-build/*.ini /app/default-configs/
|
||||||
COPY --from=build /app/build/vanity/*.* /app/default-vanity/
|
COPY --from=build /tmp/persisted-build/vanity/*.* /app/default-vanity/
|
||||||
|
|
||||||
# needed as the container runs with the root user
|
# needed as the container runs with the root user
|
||||||
# and therefore sudo doesn't exist
|
# and therefore sudo doesn't exist
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -187,7 +187,8 @@ Now that you are logged in, run the following commands.
|
|||||||
```bash
|
```bash
|
||||||
# Creates a user for this computer which uses a password and grant said user all privileges.
|
# Creates a user for this computer which uses a password and grant said user all privileges.
|
||||||
# Change mydarkflameuser to a custom username and password to a custom password.
|
# Change mydarkflameuser to a custom username and password to a custom password.
|
||||||
GRANT ALL ON *.* TO 'mydarkflameuser'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;
|
CREATE USER 'mydarkflameuser'@'localhost' IDENTIFIED BY 'password';
|
||||||
|
GRANT ALL ON *.* TO 'mydarkflameuser'@'localhost' WITH GRANT OPTION;
|
||||||
FLUSH PRIVILEGES;
|
FLUSH PRIVILEGES;
|
||||||
|
|
||||||
# Then create a database for Darkflame Universe to use.
|
# Then create a database for Darkflame Universe to use.
|
||||||
@@ -324,13 +325,15 @@ While a character has a gmlevel of anything but `0`, some gameplay behavior will
|
|||||||
Some changes to the client `boot.cfg` file are needed to play on your server.
|
Some changes to the client `boot.cfg` file are needed to play on your server.
|
||||||
|
|
||||||
## Allowing a user to connect to your server
|
## Allowing a user to connect to your server
|
||||||
|
**ALL OF THESE CHANGES ARE REQUIRED. PLEASE FULLY READ THIS SECTION**
|
||||||
|
|
||||||
To connect to a server follow these steps:
|
To connect to a server follow these steps:
|
||||||
* In the client directory, locate `boot.cfg`
|
* In the client directory, locate `boot.cfg`
|
||||||
* Open it in a text editor and locate where it says `AUTHSERVERIP=0:`
|
* Open `boot.cfg` in a text editor and locate the line `UGCUSE3DSERVICES=7:`
|
||||||
* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers
|
|
||||||
* Next locate the line `UGCUSE3DSERVICES=7:`
|
|
||||||
* Ensure the number after the 7 is a `0`
|
* Ensure the number after the 7 is a `0`
|
||||||
* Alternatively, remove the line with `UGCUSE3DSERVICES` altogether
|
* Alternatively, remove the line with `UGCUSE3DSERVICES` altogether
|
||||||
|
* Next locate where it says `AUTHSERVERIP=0:`
|
||||||
|
* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers
|
||||||
* Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system
|
* Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system
|
||||||
* Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users.
|
* Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users.
|
||||||
As an example, here is what the boot.cfg is required to contain for a server with the ip 12.34.56.78
|
As an example, here is what the boot.cfg is required to contain for a server with the ip 12.34.56.78
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ FetchContent_Declare(
|
|||||||
googletest
|
googletest
|
||||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||||
GIT_TAG release-1.12.1
|
GIT_TAG release-1.12.1
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW 1
|
||||||
)
|
)
|
||||||
|
|
||||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
//Auth includes:
|
//Auth includes:
|
||||||
#include "AuthPackets.h"
|
#include "AuthPackets.h"
|
||||||
#include "eConnectionType.h"
|
#include "ServiceType.h"
|
||||||
#include "MessageType/Server.h"
|
#include "MessageType/Server.h"
|
||||||
#include "MessageType/Auth.h"
|
#include "MessageType/Auth.h"
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ int main(int argc, char** argv) {
|
|||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal, masterPassword);
|
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServiceType::AUTH, Game::config, &Game::lastSignal, masterPassword);
|
||||||
|
|
||||||
//Run it until server gets a kill message from Master:
|
//Run it until server gets a kill message from Master:
|
||||||
auto t = std::chrono::high_resolution_clock::now();
|
auto t = std::chrono::high_resolution_clock::now();
|
||||||
@@ -167,11 +167,11 @@ void HandlePacket(Packet* packet) {
|
|||||||
if (packet->length < 4) return;
|
if (packet->length < 4) return;
|
||||||
|
|
||||||
if (packet->data[0] == ID_USER_PACKET_ENUM) {
|
if (packet->data[0] == ID_USER_PACKET_ENUM) {
|
||||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::SERVER) {
|
if (static_cast<ServiceType>(packet->data[1]) == ServiceType::COMMON) {
|
||||||
if (static_cast<MessageType::Server>(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) {
|
if (static_cast<MessageType::Server>(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) {
|
||||||
AuthPackets::HandleHandshake(Game::server, packet);
|
AuthPackets::HandleHandshake(Game::server, packet);
|
||||||
}
|
}
|
||||||
} else if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::AUTH) {
|
} else if (static_cast<ServiceType>(packet->data[1]) == ServiceType::AUTH) {
|
||||||
if (static_cast<MessageType::Auth>(packet->data[3]) == MessageType::Auth::LOGIN_REQUEST) {
|
if (static_cast<MessageType::Auth>(packet->data[3]) == MessageType::Auth::LOGIN_REQUEST) {
|
||||||
AuthPackets::HandleLoginRequest(Game::server, packet);
|
AuthPackets::HandleLoginRequest(Game::server, packet);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
set(DCHATFILTER_SOURCES "dChatFilter.cpp")
|
set(DCHATFILTER_SOURCES "dChatFilter.cpp")
|
||||||
|
|
||||||
add_library(dChatFilter STATIC ${DCHATFILTER_SOURCES})
|
add_library(dChatFilter STATIC ${DCHATFILTER_SOURCES})
|
||||||
target_link_libraries(dChatFilter dDatabase)
|
target_link_libraries(dChatFilter dDatabase glm::glm)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
set(DCHATSERVER_SOURCES
|
set(DCHATSERVER_SOURCES
|
||||||
"ChatIgnoreList.cpp"
|
"ChatIgnoreList.cpp"
|
||||||
"ChatJSONUtils.cpp"
|
|
||||||
"ChatPacketHandler.cpp"
|
"ChatPacketHandler.cpp"
|
||||||
"PlayerContainer.cpp"
|
"ChatJSONUtils.cpp"
|
||||||
"ChatWeb.cpp"
|
"ChatWeb.cpp"
|
||||||
|
"PlayerContainer.cpp"
|
||||||
|
"TeamContainer.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(ChatServer "ChatServer.cpp")
|
add_executable(ChatServer "ChatServer.cpp")
|
||||||
@@ -13,6 +14,6 @@ add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}
|
|||||||
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
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" "${PROJECT_SOURCE_DIR}/dChatFilter")
|
||||||
|
|
||||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter glm::glm)
|
||||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer dWeb)
|
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose dWeb)
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
// not allowing teams, rejecting DMs, friends requets etc.
|
// not allowing teams, rejecting DMs, friends requets etc.
|
||||||
// The only thing not auto-handled is instance activities force joining the team on the server.
|
// The only thing not auto-handled is instance activities force joining the team on the server.
|
||||||
|
|
||||||
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
|
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const MessageType::Client type) {
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(receivingPlayer);
|
bitStream.Write(receivingPlayer);
|
||||||
|
|
||||||
//portion that will get routed:
|
//portion that will get routed:
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, type);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatIgnoreList::GetIgnoreList(Packet* packet) {
|
void ChatIgnoreList::GetIgnoreList(Packet* packet) {
|
||||||
@@ -48,9 +48,9 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::GET_IGNORE);
|
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::GET_IGNORE_LIST_RESPONSE);
|
||||||
|
|
||||||
bitStream.Write<uint8_t>(false); // Probably is Is Free Trial, but we don't care about that
|
bitStream.Write<uint8_t>(false); // Is Free Trial, but we don't care about that
|
||||||
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
|
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
|
||||||
|
|
||||||
bitStream.Write<uint16_t>(receiver.ignoredPlayers.size());
|
bitStream.Write<uint16_t>(receiver.ignoredPlayers.size());
|
||||||
@@ -86,7 +86,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
|
|||||||
std::string toIgnoreStr = toIgnoreName.GetAsString();
|
std::string toIgnoreStr = toIgnoreName.GetAsString();
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::ADD_IGNORE);
|
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::ADD_IGNORE_RESPONSE);
|
||||||
|
|
||||||
// Check if the player exists
|
// Check if the player exists
|
||||||
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
|
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
|
||||||
@@ -161,7 +161,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) {
|
|||||||
receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end());
|
receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end());
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::REMOVE_IGNORE);
|
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::REMOVE_IGNORE_RESPONSE);
|
||||||
|
|
||||||
bitStream.Write<int8_t>(0);
|
bitStream.Write<int8_t>(0);
|
||||||
LUWString playerNameSend(removedIgnoreStr, 33);
|
LUWString playerNameSend(removedIgnoreStr, 33);
|
||||||
|
|||||||
@@ -5,17 +5,16 @@ struct Packet;
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The ignore list allows players to ignore someone silently. Requests will generally be blocked by the client, but they should also be checked
|
||||||
|
* on the server as well so the sender can get a generic error code in response.
|
||||||
|
*
|
||||||
|
*/
|
||||||
namespace ChatIgnoreList {
|
namespace ChatIgnoreList {
|
||||||
void GetIgnoreList(Packet* packet);
|
void GetIgnoreList(Packet* packet);
|
||||||
void AddIgnore(Packet* packet);
|
void AddIgnore(Packet* packet);
|
||||||
void RemoveIgnore(Packet* packet);
|
void RemoveIgnore(Packet* packet);
|
||||||
|
|
||||||
enum class Response : uint8_t {
|
|
||||||
ADD_IGNORE = 32,
|
|
||||||
REMOVE_IGNORE = 33,
|
|
||||||
GET_IGNORE = 34,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class AddResponse : uint8_t {
|
enum class AddResponse : uint8_t {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
ALREADY_IGNORED,
|
ALREADY_IGNORED,
|
||||||
|
|||||||
@@ -18,19 +18,12 @@ void to_json(json& data, const PlayerData& playerData) {
|
|||||||
|
|
||||||
void to_json(json& data, const PlayerContainer& playerContainer) {
|
void to_json(json& data, const PlayerContainer& playerContainer) {
|
||||||
data = json::array();
|
data = json::array();
|
||||||
for(auto& playerData : playerContainer.GetAllPlayers()) {
|
for (auto& playerData : playerContainer.GetAllPlayers()) {
|
||||||
if (playerData.first == LWOOBJID_EMPTY) continue;
|
if (playerData.first == LWOOBJID_EMPTY) continue;
|
||||||
data.push_back(playerData.second);
|
data.push_back(playerData.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void to_json(json& data, const TeamContainer& teamContainer) {
|
|
||||||
for (auto& teamData : Game::playerContainer.GetTeams()) {
|
|
||||||
if (!teamData) continue;
|
|
||||||
data.push_back(*teamData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void to_json(json& data, const TeamData& teamData) {
|
void to_json(json& data, const TeamData& teamData) {
|
||||||
data["id"] = teamData.teamID;
|
data["id"] = teamData.teamID;
|
||||||
data["loot_flag"] = teamData.lootFlag;
|
data["loot_flag"] = teamData.lootFlag;
|
||||||
@@ -47,3 +40,10 @@ void to_json(json& data, const TeamData& teamData) {
|
|||||||
members.push_back(playerData);
|
members.push_back(playerData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TeamContainer::to_json(json& data, const TeamContainer::Data& teamContainer) {
|
||||||
|
for (auto& teamData : TeamContainer::GetTeams()) {
|
||||||
|
if (!teamData) continue;
|
||||||
|
data.push_back(*teamData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,16 @@
|
|||||||
|
|
||||||
#include "json_fwd.hpp"
|
#include "json_fwd.hpp"
|
||||||
#include "PlayerContainer.h"
|
#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 PlayerData& playerData);
|
||||||
void to_json(nlohmann::json& data, const PlayerContainer& playerContainer);
|
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);
|
void to_json(nlohmann::json& data, const TeamData& teamData);
|
||||||
|
|
||||||
#endif // __CHATJSONUTILS_H__
|
namespace TeamContainer {
|
||||||
|
void to_json(nlohmann::json& data, const TeamContainer::Data& teamData);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !__CHATJSONUTILS_H__
|
||||||
|
|||||||
@@ -12,15 +12,14 @@
|
|||||||
#include "RakString.h"
|
#include "RakString.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
#include "eObjectBits.h"
|
#include "eObjectBits.h"
|
||||||
#include "eConnectionType.h"
|
#include "ServiceType.h"
|
||||||
#include "MessageType/Chat.h"
|
#include "MessageType/Chat.h"
|
||||||
#include "MessageType/Client.h"
|
#include "MessageType/Client.h"
|
||||||
#include "MessageType/Game.h"
|
#include "MessageType/Game.h"
|
||||||
#include "StringifiedEnum.h"
|
#include "StringifiedEnum.h"
|
||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
#include "ChatPackets.h"
|
#include "ChatPackets.h"
|
||||||
#include "json.hpp"
|
#include "TeamContainer.h"
|
||||||
#include "ChatWeb.h"
|
|
||||||
|
|
||||||
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||||
//Get from the packet which player we want to do something with:
|
//Get from the packet which player we want to do something with:
|
||||||
@@ -62,11 +61,11 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
|||||||
|
|
||||||
//Now, we need to send the friendlist to the server they came from:
|
//Now, we need to send the friendlist to the server they came from:
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(playerID);
|
bitStream.Write(playerID);
|
||||||
|
|
||||||
//portion that will get routed:
|
//portion that will get routed:
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::GET_FRIENDS_LIST_RESPONSE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::GET_FRIENDS_LIST_RESPONSE);
|
||||||
bitStream.Write<uint8_t>(0);
|
bitStream.Write<uint8_t>(0);
|
||||||
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
|
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
|
||||||
bitStream.Write<uint16_t>(player.friends.size());
|
bitStream.Write<uint16_t>(player.friends.size());
|
||||||
@@ -75,7 +74,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
|||||||
data.Serialize(bitStream);
|
data.Serialize(bitStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemAddress sysAddr = player.sysAddr;
|
SystemAddress sysAddr = player.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +123,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
|||||||
requesteeFriendData.isOnline = false;
|
requesteeFriendData.isOnline = false;
|
||||||
requesteeFriendData.zoneID = requestor.zoneID;
|
requesteeFriendData.zoneID = requestor.zoneID;
|
||||||
requestee.friends.push_back(requesteeFriendData);
|
requestee.friends.push_back(requesteeFriendData);
|
||||||
requestee.sysAddr = UNASSIGNED_SYSTEM_ADDRESS;
|
requestee.worldServerSysAddr = UNASSIGNED_SYSTEM_ADDRESS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,8 +190,8 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
|||||||
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus);
|
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus);
|
||||||
// Sent the best friend update here if the value is 3
|
// Sent the best friend update here if the value is 3
|
||||||
if (bestFriendStatus == 3U) {
|
if (bestFriendStatus == 3U) {
|
||||||
if (requestee.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true);
|
if (requestee.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true);
|
||||||
if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true);
|
if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true);
|
||||||
|
|
||||||
for (auto& friendData : requestor.friends) {
|
for (auto& friendData : requestor.friends) {
|
||||||
if (friendData.friendID == requestee.playerID) {
|
if (friendData.friendID == requestee.playerID) {
|
||||||
@@ -213,7 +212,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true);
|
if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
|
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
|
||||||
@@ -366,7 +365,7 @@ void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) {
|
|||||||
|
|
||||||
void ChatPacketHandler::HandleWho(Packet* packet) {
|
void ChatPacketHandler::HandleWho(Packet* packet) {
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM_SKIP_HEADER;
|
||||||
ChatPackets::FindPlayerRequest request;
|
FindPlayerRequest request;
|
||||||
request.Deserialize(inStream);
|
request.Deserialize(inStream);
|
||||||
|
|
||||||
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
||||||
@@ -376,33 +375,33 @@ void ChatPacketHandler::HandleWho(Packet* packet) {
|
|||||||
bool online = player;
|
bool online = player;
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(request.requestor);
|
bitStream.Write(request.requestor);
|
||||||
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::WHO_RESPONSE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::WHO_RESPONSE);
|
||||||
bitStream.Write<uint8_t>(online);
|
bitStream.Write<uint8_t>(online);
|
||||||
bitStream.Write(player.zoneID.GetMapID());
|
bitStream.Write(player.zoneID.GetMapID());
|
||||||
bitStream.Write(player.zoneID.GetInstanceID());
|
bitStream.Write(player.zoneID.GetInstanceID());
|
||||||
bitStream.Write(player.zoneID.GetCloneID());
|
bitStream.Write(player.zoneID.GetCloneID());
|
||||||
bitStream.Write(request.playerName);
|
bitStream.Write(request.playerName);
|
||||||
|
|
||||||
SystemAddress sysAddr = sender.sysAddr;
|
SystemAddress sysAddr = sender.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM_SKIP_HEADER;
|
||||||
ChatPackets::ShowAllRequest request;
|
ShowAllRequest request;
|
||||||
request.Deserialize(inStream);
|
request.Deserialize(inStream);
|
||||||
|
|
||||||
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
||||||
if (!sender) return;
|
if (!sender) return;
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(request.requestor);
|
bitStream.Write(request.requestor);
|
||||||
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::SHOW_ALL_RESPONSE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::SHOW_ALL_RESPONSE);
|
||||||
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
|
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
|
||||||
bitStream.Write(Game::playerContainer.GetPlayerCount());
|
bitStream.Write(Game::playerContainer.GetPlayerCount());
|
||||||
bitStream.Write(Game::playerContainer.GetSimCount());
|
bitStream.Write(Game::playerContainer.GetSimCount());
|
||||||
@@ -420,7 +419,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SystemAddress sysAddr = sender.sysAddr;
|
SystemAddress sysAddr = sender.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,114 +427,127 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
|||||||
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
|
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
|
||||||
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM_SKIP_HEADER;
|
||||||
ChatMessage data;
|
LWOOBJID playerID;
|
||||||
LWOOBJID sender;
|
inStream.Read(playerID);
|
||||||
inStream.Read(sender);
|
|
||||||
LOG("Got a message from player %llu", sender);
|
|
||||||
|
|
||||||
data.sender = Game::playerContainer.GetPlayerData(sender);
|
const auto& sender = Game::playerContainer.GetPlayerData(playerID);
|
||||||
if (!data.sender || data.sender.GetIsMuted()) return;
|
if (!sender || sender.GetIsMuted()) return;
|
||||||
|
|
||||||
|
eChatChannel channel;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
|
||||||
inStream.IgnoreBytes(4);
|
inStream.IgnoreBytes(4);
|
||||||
inStream.Read(data.channel);
|
inStream.Read(channel);
|
||||||
inStream.Read(size);
|
inStream.Read(size);
|
||||||
inStream.IgnoreBytes(77);
|
inStream.IgnoreBytes(77);
|
||||||
|
|
||||||
data.message = LUWString(size);
|
LUWString message(size);
|
||||||
inStream.Read(data.message);
|
inStream.Read(message);
|
||||||
|
|
||||||
LOG("Got message from (%s) via [%s]: %s", data.sender.playerName.c_str(), StringifiedEnum::ToString(data.channel).data(), data.message.GetAsString().c_str());
|
LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str());
|
||||||
|
|
||||||
|
|
||||||
switch (data.channel) {
|
|
||||||
case eChatChannel::TEAM: {
|
|
||||||
auto* team = Game::playerContainer.GetTeam(data.sender.playerID);
|
|
||||||
if (team == nullptr) return;
|
|
||||||
data.teamID = team->teamID;
|
|
||||||
|
|
||||||
for (const auto memberId : team->memberIDs) {
|
switch (channel) {
|
||||||
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
case eChatChannel::TEAM: {
|
||||||
if (!otherMember) return;
|
auto* team = TeamContainer::GetTeam(playerID);
|
||||||
SendPrivateChatMessage(data.sender, otherMember, otherMember, data.message, eChatChannel::TEAM, eChatMessageResponseCode::SENT);
|
if (team == nullptr) return;
|
||||||
}
|
|
||||||
break;
|
for (const auto memberId : team->memberIDs) {
|
||||||
|
const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||||
|
if (!otherMember) return;
|
||||||
|
SendPrivateChatMessage(sender, otherMember, otherMember, message, eChatChannel::TEAM, eChatMessageResponseCode::SENT);
|
||||||
}
|
}
|
||||||
default:
|
break;
|
||||||
LOG_DEBUG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(data.channel).data());
|
}
|
||||||
break;
|
default:
|
||||||
|
LOG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(channel).data());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
ChatWeb::SendWSChatMessage(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the structure the client uses to send this packet is shared in many chat messages
|
// the structure the client uses to send this packet is shared in many chat messages
|
||||||
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
|
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
|
||||||
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
||||||
ChatMessage data;
|
|
||||||
data.channel = eChatChannel::GENERAL;
|
|
||||||
|
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM_SKIP_HEADER;
|
||||||
LWOOBJID playerID;
|
LWOOBJID playerID;
|
||||||
inStream.Read(playerID);
|
inStream.Read(playerID);
|
||||||
|
|
||||||
data.sender = Game::playerContainer.GetPlayerData(playerID);
|
const auto& sender = Game::playerContainer.GetPlayerData(playerID);
|
||||||
if (!data.sender || data.sender.GetIsMuted()) return;
|
if (!sender || sender.GetIsMuted()) return;
|
||||||
|
|
||||||
|
eChatChannel channel;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
LUWString LUReceiverName;
|
||||||
|
|
||||||
inStream.IgnoreBytes(4);
|
inStream.IgnoreBytes(4);
|
||||||
inStream.Read(data.channel);
|
inStream.Read(channel);
|
||||||
if (data.channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!");
|
if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!");
|
||||||
|
|
||||||
inStream.Read(size);
|
inStream.Read(size);
|
||||||
inStream.IgnoreBytes(77);
|
inStream.IgnoreBytes(77);
|
||||||
|
|
||||||
LUWString LUReceiverName;
|
|
||||||
inStream.Read(LUReceiverName);
|
inStream.Read(LUReceiverName);
|
||||||
auto receiverName = LUReceiverName.GetAsString();
|
auto receiverName = LUReceiverName.GetAsString();
|
||||||
inStream.IgnoreBytes(2);
|
inStream.IgnoreBytes(2);
|
||||||
|
|
||||||
data.message = LUWString(size);
|
LUWString message(size);
|
||||||
inStream.Read(data.message);
|
inStream.Read(message);
|
||||||
|
|
||||||
LOG("Got a message from (%s) via [%s]: %s to %s", data.sender.playerName.c_str(), StringifiedEnum::ToString(data.channel).data(), data.message.GetAsString().c_str(), receiverName.c_str());
|
LOG("Got a message from (%s) via [%s]: %s to %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str(), receiverName.c_str());
|
||||||
|
|
||||||
data.receiver = Game::playerContainer.GetPlayerData(receiverName);
|
const auto& receiver = Game::playerContainer.GetPlayerData(receiverName);
|
||||||
if (!data.receiver) {
|
if (!receiver) {
|
||||||
PlayerData otherPlayer;
|
PlayerData otherPlayer;
|
||||||
otherPlayer.playerName = receiverName;
|
otherPlayer.playerName = receiverName;
|
||||||
auto responseType = Database::Get()->GetCharacterInfo(receiverName)
|
auto responseType = Database::Get()->GetCharacterInfo(receiverName)
|
||||||
? eChatMessageResponseCode::NOTONLINE
|
? eChatMessageResponseCode::NOTONLINE
|
||||||
: eChatMessageResponseCode::GENERALERROR;
|
: eChatMessageResponseCode::GENERALERROR;
|
||||||
|
|
||||||
SendPrivateChatMessage(data.sender, otherPlayer, data.sender, data.message, data.channel, responseType);
|
SendPrivateChatMessage(sender, otherPlayer, sender, message, eChatChannel::GENERAL, responseType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if they are friends
|
// Check to see if they are friends
|
||||||
// only freinds can whispr each other
|
// only freinds can whispr each other
|
||||||
for (const auto& fr : data.receiver.friends) {
|
for (const auto& fr : receiver.friends) {
|
||||||
if (fr.friendID == data.sender.playerID) {
|
if (fr.friendID == sender.playerID) {
|
||||||
data.channel = eChatChannel::PRIVATE_CHAT;
|
//To the sender:
|
||||||
// To the sender:
|
SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::PRIVATE_CHAT, eChatMessageResponseCode::SENT);
|
||||||
SendPrivateChatMessage(data.sender, data.receiver, data.sender, data.message, data.channel, eChatMessageResponseCode::SENT);
|
//To the receiver:
|
||||||
// To the receiver:
|
SendPrivateChatMessage(sender, receiver, receiver, message, eChatChannel::PRIVATE_CHAT, eChatMessageResponseCode::RECEIVEDNEWWHISPER);
|
||||||
SendPrivateChatMessage(data.sender, data.receiver, data.receiver, data.message, data.channel, eChatMessageResponseCode::RECEIVEDNEWWHISPER);
|
|
||||||
// To the websocket:
|
|
||||||
ChatWeb::SendWSChatMessage(data);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SendPrivateChatMessage(data.sender, data.receiver, data.sender, data.message, data.channel, eChatMessageResponseCode::NOTFRIENDS);
|
SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::GENERAL, eChatMessageResponseCode::NOTFRIENDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPacketHandler::OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr) {
|
||||||
|
ChatPackets::AchievementNotify notify{};
|
||||||
|
notify.Deserialize(bitstream);
|
||||||
|
const auto& playerData = Game::playerContainer.GetPlayerData(notify.earnerName.GetAsString());
|
||||||
|
if (!playerData) return;
|
||||||
|
|
||||||
|
for (const auto& myFriend : playerData.friends) {
|
||||||
|
auto& friendData = Game::playerContainer.GetPlayerData(myFriend.friendID);
|
||||||
|
if (friendData) {
|
||||||
|
notify.targetPlayerName.string = GeneralUtils::ASCIIToUTF16(friendData.playerName);
|
||||||
|
LOG_DEBUG("Sending achievement notify to %s", notify.targetPlayerName.GetAsString().c_str());
|
||||||
|
|
||||||
|
RakNet::BitStream worldStream;
|
||||||
|
BitStreamUtils::WriteHeader(worldStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
|
worldStream.Write(friendData.playerID);
|
||||||
|
notify.WriteHeader(worldStream);
|
||||||
|
notify.Serialize(worldStream);
|
||||||
|
Game::server->Send(worldStream, friendData.worldServerSysAddr, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
|
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(routeTo.playerID);
|
bitStream.Write(routeTo.playerID);
|
||||||
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE);
|
||||||
bitStream.Write(sender.playerID);
|
bitStream.Write(sender.playerID);
|
||||||
bitStream.Write(channel);
|
bitStream.Write(channel);
|
||||||
bitStream.Write<uint32_t>(0); // not used
|
bitStream.Write<uint32_t>(0); // not used
|
||||||
@@ -548,387 +560,7 @@ void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const P
|
|||||||
bitStream.Write(responseCode);
|
bitStream.Write(responseCode);
|
||||||
bitStream.Write(message);
|
bitStream.Write(message);
|
||||||
|
|
||||||
SystemAddress sysAddr = routeTo.sysAddr;
|
SystemAddress sysAddr = routeTo.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -947,11 +579,11 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
|
|||||||
[bool] - is FTP*/
|
[bool] - is FTP*/
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(friendData.playerID);
|
bitStream.Write(friendData.playerID);
|
||||||
|
|
||||||
//portion that will get routed:
|
//portion that will get routed:
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::UPDATE_FRIEND_NOTIFY);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::UPDATE_FRIEND_NOTIFY);
|
||||||
bitStream.Write<uint8_t>(notifyType);
|
bitStream.Write<uint8_t>(notifyType);
|
||||||
|
|
||||||
std::string playerName = playerData.playerName.c_str();
|
std::string playerName = playerData.playerName.c_str();
|
||||||
@@ -970,7 +602,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
|
|||||||
bitStream.Write<uint8_t>(isBestFriend); //isBFF
|
bitStream.Write<uint8_t>(isBestFriend); //isBFF
|
||||||
bitStream.Write<uint8_t>(0); //isFTP
|
bitStream.Write<uint8_t>(0); //isFTP
|
||||||
|
|
||||||
SystemAddress sysAddr = friendData.sysAddr;
|
SystemAddress sysAddr = friendData.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -984,28 +616,28 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(receiver.playerID);
|
bitStream.Write(receiver.playerID);
|
||||||
|
|
||||||
//portion that will get routed:
|
//portion that will get routed:
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_REQUEST);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::ADD_FRIEND_REQUEST);
|
||||||
bitStream.Write(LUWString(sender.playerName));
|
bitStream.Write(LUWString(sender.playerName));
|
||||||
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
|
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
|
||||||
|
|
||||||
SystemAddress sysAddr = receiver.sysAddr;
|
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
|
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(receiver.playerID);
|
bitStream.Write(receiver.playerID);
|
||||||
|
|
||||||
// Portion that will get routed:
|
// Portion that will get routed:
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE);
|
||||||
bitStream.Write(responseCode);
|
bitStream.Write(responseCode);
|
||||||
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
|
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
|
||||||
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
|
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
// Then write the player name
|
// Then write the player name
|
||||||
bitStream.Write(LUWString(sender.playerName));
|
bitStream.Write(LUWString(sender.playerName));
|
||||||
// Then if this is an acceptance code, write the following extra info.
|
// Then if this is an acceptance code, write the following extra info.
|
||||||
@@ -1015,20 +647,20 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
|
|||||||
bitStream.Write(isBestFriendRequest); //isBFF
|
bitStream.Write(isBestFriendRequest); //isBFF
|
||||||
bitStream.Write<uint8_t>(0); //isFTP
|
bitStream.Write<uint8_t>(0); //isFTP
|
||||||
}
|
}
|
||||||
SystemAddress sysAddr = receiver.sysAddr;
|
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
|
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
bitStream.Write(receiver.playerID);
|
bitStream.Write(receiver.playerID);
|
||||||
|
|
||||||
//portion that will get routed:
|
//portion that will get routed:
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::REMOVE_FRIEND_RESPONSE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::REMOVE_FRIEND_RESPONSE);
|
||||||
bitStream.Write<uint8_t>(isSuccessful); //isOnline
|
bitStream.Write<uint8_t>(isSuccessful); //isOnline
|
||||||
bitStream.Write(LUWString(personToRemove));
|
bitStream.Write(LUWString(personToRemove));
|
||||||
|
|
||||||
SystemAddress sysAddr = receiver.sysAddr;
|
SystemAddress sysAddr = receiver.worldServerSysAddr;
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
#include "dNetCommon.h"
|
#include "dNetCommon.h"
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include "PlayerContainer.h"
|
|
||||||
#include "eChatMessageResponseCode.h"
|
struct PlayerData;
|
||||||
|
|
||||||
enum class eAddFriendResponseType : uint8_t;
|
enum class eAddFriendResponseType : uint8_t;
|
||||||
|
|
||||||
@@ -34,13 +34,14 @@ enum class eChatChannel : uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class eChatMessageResponseCode : uint8_t {
|
||||||
struct ChatMessage {
|
SENT = 0,
|
||||||
LUWString message;
|
NOTONLINE,
|
||||||
PlayerData sender;
|
GENERALERROR,
|
||||||
PlayerData receiver;
|
RECEIVEDNEWWHISPER,
|
||||||
eChatChannel channel;
|
NOTFRIENDS,
|
||||||
LWOOBJID teamID;
|
SENDERFREETRIAL,
|
||||||
|
RECEIVERFREETRIAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace ChatPacketHandler {
|
namespace ChatPacketHandler {
|
||||||
@@ -51,30 +52,14 @@ namespace ChatPacketHandler {
|
|||||||
void HandleGMLevelUpdate(Packet* packet);
|
void HandleGMLevelUpdate(Packet* packet);
|
||||||
void HandleWho(Packet* packet);
|
void HandleWho(Packet* packet);
|
||||||
void HandleShowAll(Packet* packet);
|
void HandleShowAll(Packet* packet);
|
||||||
|
|
||||||
void HandleChatMessage(Packet* packet);
|
void HandleChatMessage(Packet* packet);
|
||||||
void HandlePrivateChatMessage(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 HandleTeamInvite(Packet* packet);
|
void OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr);
|
||||||
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.
|
//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 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 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 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);
|
void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful);
|
||||||
|
|||||||
@@ -13,13 +13,14 @@
|
|||||||
#include "Diagnostics.h"
|
#include "Diagnostics.h"
|
||||||
#include "AssetManager.h"
|
#include "AssetManager.h"
|
||||||
#include "BinaryPathFinder.h"
|
#include "BinaryPathFinder.h"
|
||||||
#include "eConnectionType.h"
|
#include "ServiceType.h"
|
||||||
#include "PlayerContainer.h"
|
#include "PlayerContainer.h"
|
||||||
#include "ChatPacketHandler.h"
|
#include "ChatPacketHandler.h"
|
||||||
#include "MessageType/Chat.h"
|
#include "MessageType/Chat.h"
|
||||||
#include "MessageType/World.h"
|
#include "MessageType/World.h"
|
||||||
#include "ChatIgnoreList.h"
|
#include "ChatIgnoreList.h"
|
||||||
#include "StringifiedEnum.h"
|
#include "StringifiedEnum.h"
|
||||||
|
#include "TeamContainer.h"
|
||||||
|
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Server.h"
|
#include "Server.h"
|
||||||
@@ -122,7 +123,7 @@ int main(int argc, char** argv) {
|
|||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal, masterPassword);
|
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServiceType::CHAT, Game::config, &Game::lastSignal, masterPassword);
|
||||||
|
|
||||||
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
|
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
|
||||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
|
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
|
||||||
@@ -196,6 +197,7 @@ int main(int argc, char** argv) {
|
|||||||
std::this_thread::sleep_until(t);
|
std::this_thread::sleep_until(t);
|
||||||
}
|
}
|
||||||
Game::playerContainer.Shutdown();
|
Game::playerContainer.Shutdown();
|
||||||
|
TeamContainer::Shutdown();
|
||||||
//Delete our objects here:
|
//Delete our objects here:
|
||||||
Database::Destroy("ChatServer");
|
Database::Destroy("ChatServer");
|
||||||
delete Game::server;
|
delete Game::server;
|
||||||
@@ -206,7 +208,6 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HandlePacket(Packet* packet) {
|
void HandlePacket(Packet* packet) {
|
||||||
LOG("Received packet with ID: %i", packet->data[0]);
|
|
||||||
if (packet->length < 1) return;
|
if (packet->length < 1) return;
|
||||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||||
LOG("A server has disconnected, erasing their connected players from the list.");
|
LOG("A server has disconnected, erasing their connected players from the list.");
|
||||||
@@ -217,20 +218,24 @@ void HandlePacket(Packet* packet) {
|
|||||||
CINSTREAM;
|
CINSTREAM;
|
||||||
inStream.SetReadOffset(BYTES_TO_BITS(1));
|
inStream.SetReadOffset(BYTES_TO_BITS(1));
|
||||||
|
|
||||||
eConnectionType connection;
|
ServiceType connection;
|
||||||
MessageType::Chat chatMessageID;
|
|
||||||
|
|
||||||
inStream.Read(connection);
|
inStream.Read(connection);
|
||||||
if (connection != eConnectionType::CHAT) return;
|
if (connection != ServiceType::CHAT) return;
|
||||||
|
|
||||||
|
MessageType::Chat chatMessageID;
|
||||||
inStream.Read(chatMessageID);
|
inStream.Read(chatMessageID);
|
||||||
|
|
||||||
|
// Our packing byte wasnt there? Probably a false packet
|
||||||
|
if (inStream.GetNumberOfUnreadBits() < 8) return;
|
||||||
|
inStream.IgnoreBytes(1);
|
||||||
|
|
||||||
switch (chatMessageID) {
|
switch (chatMessageID) {
|
||||||
case MessageType::Chat::GM_MUTE:
|
case MessageType::Chat::GM_MUTE:
|
||||||
Game::playerContainer.MuteUpdate(packet);
|
Game::playerContainer.MuteUpdate(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::CREATE_TEAM:
|
case MessageType::Chat::CREATE_TEAM:
|
||||||
Game::playerContainer.CreateTeamServer(packet);
|
TeamContainer::CreateTeamServer(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::GET_FRIENDS_LIST:
|
case MessageType::Chat::GET_FRIENDS_LIST:
|
||||||
@@ -250,7 +255,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_GET_STATUS:
|
case MessageType::Chat::TEAM_GET_STATUS:
|
||||||
ChatPacketHandler::HandleTeamStatusRequest(packet);
|
TeamContainer::HandleTeamStatusRequest(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::ADD_FRIEND_REQUEST:
|
case MessageType::Chat::ADD_FRIEND_REQUEST:
|
||||||
@@ -280,27 +285,27 @@ void HandlePacket(Packet* packet) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_INVITE:
|
case MessageType::Chat::TEAM_INVITE:
|
||||||
ChatPacketHandler::HandleTeamInvite(packet);
|
TeamContainer::HandleTeamInvite(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_INVITE_RESPONSE:
|
case MessageType::Chat::TEAM_INVITE_RESPONSE:
|
||||||
ChatPacketHandler::HandleTeamInviteResponse(packet);
|
TeamContainer::HandleTeamInviteResponse(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_LEAVE:
|
case MessageType::Chat::TEAM_LEAVE:
|
||||||
ChatPacketHandler::HandleTeamLeave(packet);
|
TeamContainer::HandleTeamLeave(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_SET_LEADER:
|
case MessageType::Chat::TEAM_SET_LEADER:
|
||||||
ChatPacketHandler::HandleTeamPromote(packet);
|
TeamContainer::HandleTeamPromote(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_KICK:
|
case MessageType::Chat::TEAM_KICK:
|
||||||
ChatPacketHandler::HandleTeamKick(packet);
|
TeamContainer::HandleTeamKick(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_SET_LOOT:
|
case MessageType::Chat::TEAM_SET_LOOT:
|
||||||
ChatPacketHandler::HandleTeamLootOption(packet);
|
TeamContainer::HandleTeamLootOption(packet);
|
||||||
break;
|
break;
|
||||||
case MessageType::Chat::GMLEVEL_UPDATE:
|
case MessageType::Chat::GMLEVEL_UPDATE:
|
||||||
ChatPacketHandler::HandleGMLevelUpdate(packet);
|
ChatPacketHandler::HandleGMLevelUpdate(packet);
|
||||||
@@ -322,6 +327,9 @@ void HandlePacket(Packet* packet) {
|
|||||||
case MessageType::Chat::SHOW_ALL:
|
case MessageType::Chat::SHOW_ALL:
|
||||||
ChatPacketHandler::HandleShowAll(packet);
|
ChatPacketHandler::HandleShowAll(packet);
|
||||||
break;
|
break;
|
||||||
|
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
|
||||||
|
ChatPacketHandler::OnAchievementNotify(inStream, packet->systemAddress);
|
||||||
|
break;
|
||||||
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
|
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
|
||||||
case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
|
case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
|
||||||
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
|
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
|
||||||
@@ -357,7 +365,6 @@ void HandlePacket(Packet* packet) {
|
|||||||
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
||||||
case MessageType::Chat::UGCC_REQUEST:
|
case MessageType::Chat::UGCC_REQUEST:
|
||||||
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
||||||
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
|
|
||||||
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
||||||
case MessageType::Chat::PLAYER_READY:
|
case MessageType::Chat::PLAYER_READY:
|
||||||
case MessageType::Chat::GET_DONATION_TOTAL:
|
case MessageType::Chat::GET_DONATION_TOTAL:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "JSONUtils.h"
|
#include "JSONUtils.h"
|
||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
#include "dChatFilter.h"
|
#include "dChatFilter.h"
|
||||||
|
#include "TeamContainer.h"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ void HandleHTTPPlayersRequest(HTTPReply& reply, std::string body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HandleHTTPTeamsRequest(HTTPReply& reply, std::string body) {
|
void HandleHTTPTeamsRequest(HTTPReply& reply, std::string body) {
|
||||||
const json data = Game::playerContainer.GetTeamContainer();
|
const json data = TeamContainer::GetTeamContainer();
|
||||||
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
|
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
|
||||||
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
|
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) {
|
|||||||
ChatPackets::Announcement announcement;
|
ChatPackets::Announcement announcement;
|
||||||
announcement.title = good_data["title"];
|
announcement.title = good_data["title"];
|
||||||
announcement.message = good_data["message"];
|
announcement.message = good_data["message"];
|
||||||
announcement.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
announcement.Broadcast();
|
||||||
|
|
||||||
reply.status = eHTTPStatusCode::OK;
|
reply.status = eHTTPStatusCode::OK;
|
||||||
reply.message = "{\"status\":\"Announcement Sent\"}";
|
reply.message = "{\"status\":\"Announcement Sent\"}";
|
||||||
@@ -80,34 +81,17 @@ void HandleWSChat(mg_connection* connection, json data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG("%s: %s", user.c_str(), message.c_str());
|
LOG("%s: %s", user.c_str(), message.c_str());
|
||||||
|
|
||||||
|
// TODO: Implement chat message handling from websocket message
|
||||||
|
|
||||||
// bodge to test
|
|
||||||
CBITSTREAM;
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE);
|
|
||||||
bitStream.Write(zone);
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE);
|
|
||||||
|
|
||||||
bitStream.Write<uint64_t>(0);
|
|
||||||
bitStream.Write(eChatChannel::LOCAL);
|
|
||||||
|
|
||||||
bitStream.Write<uint32_t>(message.size());
|
|
||||||
bitStream.Write(LUWString(user));
|
|
||||||
|
|
||||||
bitStream.Write<uint64_t>(0);
|
|
||||||
bitStream.Write<uint16_t>(0);
|
|
||||||
bitStream.Write<char>(0);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < message.size(); ++i) {
|
|
||||||
bitStream.Write<uint16_t>(message[i]);
|
|
||||||
}
|
|
||||||
bitStream.Write<uint16_t>(0);
|
|
||||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ChatWeb {
|
namespace ChatWeb {
|
||||||
void RegisterRoutes() {
|
void RegisterRoutes() {
|
||||||
|
|
||||||
// REST API v1 routes
|
// REST API v1 routes
|
||||||
|
|
||||||
std::string v1_route = "/api/v1/";
|
std::string v1_route = "/api/v1/";
|
||||||
Game::web.RegisterHTTPRoute({
|
Game::web.RegisterHTTPRoute({
|
||||||
.path = v1_route + "players",
|
.path = v1_route + "players",
|
||||||
@@ -127,16 +111,16 @@ namespace ChatWeb {
|
|||||||
.handle = HandleHTTPAnnounceRequest
|
.handle = HandleHTTPAnnounceRequest
|
||||||
});
|
});
|
||||||
|
|
||||||
// WebSocket Events
|
// WebSocket Events Handlers
|
||||||
Game::web.RegisterWSEvent({
|
|
||||||
.name = "chat",
|
// Game::web.RegisterWSEvent({
|
||||||
.handle = HandleWSChat
|
// .name = "chat",
|
||||||
});
|
// .handle = HandleWSChat
|
||||||
|
// });
|
||||||
|
|
||||||
// WebSocket subscriptions
|
// WebSocket subscriptions
|
||||||
Game::web.RegisterWSSubscription("chat");
|
|
||||||
Game::web.RegisterWSSubscription("player");
|
Game::web.RegisterWSSubscription("player");
|
||||||
Game::web.RegisterWSSubscription("team");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType) {
|
void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType) {
|
||||||
@@ -145,25 +129,5 @@ namespace ChatWeb {
|
|||||||
data["update_type"] = magic_enum::enum_name(activityType);
|
data["update_type"] = magic_enum::enum_name(activityType);
|
||||||
Game::web.SendWSMessage("player", data);
|
Game::web.SendWSMessage("player", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendWSChatMessage(const ChatMessage& chatMessage) {
|
|
||||||
json data;
|
|
||||||
data["message"] = chatMessage.message.GetAsString();
|
|
||||||
data["sender"] = chatMessage.sender;
|
|
||||||
data["channel"] = magic_enum::enum_name(chatMessage.channel);
|
|
||||||
|
|
||||||
switch (chatMessage.channel) {
|
|
||||||
case eChatChannel::TEAM:
|
|
||||||
data["teamID"] = chatMessage.teamID;
|
|
||||||
break;
|
|
||||||
case eChatChannel::PRIVATE_CHAT:
|
|
||||||
data["receiver"] = chatMessage.receiver;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Game::web.SendWSMessage("chat", data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
namespace ChatWeb {
|
namespace ChatWeb {
|
||||||
void RegisterRoutes();
|
void RegisterRoutes();
|
||||||
void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType);
|
void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType);
|
||||||
void SendWSChatMessage(const ChatMessage& chatMessage);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
#include "BitStreamUtils.h"
|
#include "BitStreamUtils.h"
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "eConnectionType.h"
|
#include "ServiceType.h"
|
||||||
#include "ChatPackets.h"
|
#include "ChatPackets.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
#include "MessageType/Chat.h"
|
#include "MessageType/Chat.h"
|
||||||
#include "ChatWeb.h"
|
#include "ChatWeb.h"
|
||||||
|
#include "TeamContainer.h"
|
||||||
|
|
||||||
void PlayerContainer::Initialize() {
|
void PlayerContainer::Initialize() {
|
||||||
m_MaxNumberOfBestFriends =
|
m_MaxNumberOfBestFriends =
|
||||||
@@ -53,13 +54,14 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
|||||||
if (!inStream.Read(data.zoneID)) return;
|
if (!inStream.Read(data.zoneID)) return;
|
||||||
if (!inStream.Read(data.muteExpire)) return;
|
if (!inStream.Read(data.muteExpire)) return;
|
||||||
if (!inStream.Read(data.gmLevel)) return;
|
if (!inStream.Read(data.gmLevel)) return;
|
||||||
data.sysAddr = packet->systemAddress;
|
data.worldServerSysAddr = packet->systemAddress;
|
||||||
|
|
||||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||||
m_PlayerCount++;
|
m_PlayerCount++;
|
||||||
|
|
||||||
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
||||||
ChatWeb::SendWSPlayerUpdate(data, isLogin ? eActivityType::PlayerLoggedIn : eActivityType::PlayerChangedZone);
|
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, isLogin ? eActivityType::PlayerLoggedIn : eActivityType::PlayerChangedZone, data.zoneID.GetMapID());
|
||||||
m_PlayersToRemove.erase(playerId);
|
m_PlayersToRemove.erase(playerId);
|
||||||
}
|
}
|
||||||
@@ -100,7 +102,7 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
|
|||||||
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
|
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* team = GetTeam(playerID);
|
auto* team = TeamContainer::GetTeam(playerID);
|
||||||
|
|
||||||
if (team != nullptr) {
|
if (team != nullptr) {
|
||||||
const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
|
const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
|
||||||
@@ -110,7 +112,7 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
|
|||||||
|
|
||||||
if (!otherMember) continue;
|
if (!otherMember) continue;
|
||||||
|
|
||||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
|
TeamContainer::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,43 +145,9 @@ void PlayerContainer::MuteUpdate(Packet* packet) {
|
|||||||
BroadcastMuteUpdate(playerID, expire);
|
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) {
|
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_MUTE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::GM_MUTE);
|
||||||
|
|
||||||
bitStream.Write(player);
|
bitStream.Write(player);
|
||||||
bitStream.Write(time);
|
bitStream.Write(time);
|
||||||
@@ -187,221 +155,6 @@ void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
|||||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
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) {
|
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
|
||||||
const auto iter = m_Names.find(playerID);
|
const auto iter = m_Names.find(playerID);
|
||||||
|
|
||||||
@@ -450,5 +203,4 @@ void PlayerContainer::Shutdown() {
|
|||||||
Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
|
Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
|
||||||
m_Players.erase(m_Players.begin());
|
m_Players.erase(m_Players.begin());
|
||||||
}
|
}
|
||||||
for (auto* team : GetTeams()) if (team) delete team;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ enum class eGameMasterLevel : uint8_t;
|
|||||||
|
|
||||||
struct TeamData;
|
struct TeamData;
|
||||||
|
|
||||||
struct TeamContainer {
|
|
||||||
std::vector<TeamData*> mTeams;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IgnoreData {
|
struct IgnoreData {
|
||||||
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
|
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
|
||||||
inline bool operator==(const std::string& other) const noexcept {
|
inline bool operator==(const std::string& other) const noexcept {
|
||||||
@@ -42,7 +38,7 @@ struct PlayerData {
|
|||||||
return muteExpire == 1 || muteExpire > time(NULL);
|
return muteExpire == 1 || muteExpire > time(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemAddress sysAddr{};
|
SystemAddress worldServerSysAddr{};
|
||||||
LWOZONEID zoneID{};
|
LWOZONEID zoneID{};
|
||||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||||
time_t muteExpire = 0;
|
time_t muteExpire = 0;
|
||||||
@@ -73,7 +69,6 @@ public:
|
|||||||
void ScheduleRemovePlayer(Packet* packet);
|
void ScheduleRemovePlayer(Packet* packet);
|
||||||
void RemovePlayer(const LWOOBJID playerID);
|
void RemovePlayer(const LWOOBJID playerID);
|
||||||
void MuteUpdate(Packet* packet);
|
void MuteUpdate(Packet* packet);
|
||||||
void CreateTeamServer(Packet* packet);
|
|
||||||
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
@@ -81,34 +76,19 @@ public:
|
|||||||
const PlayerData& GetPlayerData(const std::string& playerName);
|
const PlayerData& GetPlayerData(const std::string& playerName);
|
||||||
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
||||||
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
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 GetPlayerCount() { return m_PlayerCount; };
|
||||||
uint32_t GetSimCount() { return m_SimCount; };
|
uint32_t GetSimCount() { return m_SimCount; };
|
||||||
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() const { return m_Players; };
|
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 GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
||||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
||||||
const TeamContainer& GetTeamContainer() { return m_TeamContainer; }
|
|
||||||
std::vector<TeamData*>& GetTeamsMut() { return m_TeamContainer.mTeams; };
|
|
||||||
const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); };
|
|
||||||
|
|
||||||
void Update(const float deltaTime);
|
|
||||||
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
|
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LWOOBJID m_TeamIDCounter = 0;
|
|
||||||
std::map<LWOOBJID, PlayerData> m_Players;
|
std::map<LWOOBJID, PlayerData> m_Players;
|
||||||
TeamContainer m_TeamContainer{};
|
|
||||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||||
std::map<LWOOBJID, float> m_PlayersToRemove;
|
std::map<LWOOBJID, float> m_PlayersToRemove;
|
||||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||||
|
|||||||
669
dChatServer/TeamContainer.cpp
Normal file
669
dChatServer/TeamContainer.cpp
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
#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, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||||
|
bitStream.Write(receiver.playerID);
|
||||||
|
|
||||||
|
//portion that will get routed:
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::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, ServiceType::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, ServiceType::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, ServiceType::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, ServiceType::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, ServiceType::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, ServiceType::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, ServiceType::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);
|
||||||
|
}
|
||||||
59
dChatServer/TeamContainer.h
Normal file
59
dChatServer/TeamContainer.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// 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
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
#include "Sd0.h"
|
||||||
#include "ZCompression.h"
|
#include "ZCompression.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
@@ -44,10 +45,10 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
|
// 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[ZCompression::MAX_SD0_CHUNK_SIZE]);
|
std::unique_ptr<uint8_t[]> uncompressedChunk(new uint8_t[Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE]);
|
||||||
int32_t err{};
|
int32_t err{};
|
||||||
int32_t actualUncompressedSize = ZCompression::Decompress(
|
int32_t actualUncompressedSize = ZCompression::Decompress(
|
||||||
compressedChunk.get(), chunkSize, uncompressedChunk.get(), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
compressedChunk.get(), chunkSize, uncompressedChunk.get(), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
|
||||||
|
|
||||||
if (actualUncompressedSize != -1) {
|
if (actualUncompressedSize != -1) {
|
||||||
uint32_t previousSize = completeUncompressedModel.size();
|
uint32_t previousSize = completeUncompressedModel.size();
|
||||||
@@ -117,7 +118,7 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
|
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
|
||||||
std::istringstream outputStringStream(outputString);
|
std::stringstream outputStringStream(outputString);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
|
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ set(DCOMMON_SOURCES
|
|||||||
"Logger.cpp"
|
"Logger.cpp"
|
||||||
"Game.cpp"
|
"Game.cpp"
|
||||||
"GeneralUtils.cpp"
|
"GeneralUtils.cpp"
|
||||||
"JSONUtils.cpp"
|
|
||||||
"LDFFormat.cpp"
|
"LDFFormat.cpp"
|
||||||
"Metrics.cpp"
|
"Metrics.cpp"
|
||||||
"NiPoint3.cpp"
|
"NiPoint3.cpp"
|
||||||
@@ -17,6 +16,11 @@ set(DCOMMON_SOURCES
|
|||||||
"BrickByBrickFix.cpp"
|
"BrickByBrickFix.cpp"
|
||||||
"BinaryPathFinder.cpp"
|
"BinaryPathFinder.cpp"
|
||||||
"FdbToSqlite.cpp"
|
"FdbToSqlite.cpp"
|
||||||
|
"JSONUtils.cpp"
|
||||||
|
"TinyXmlUtils.cpp"
|
||||||
|
"Sd0.cpp"
|
||||||
|
"Lxfml.cpp"
|
||||||
|
"LxfmlBugged.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
|
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
|
||||||
@@ -50,6 +54,8 @@ elseif (WIN32)
|
|||||||
zlib
|
zlib
|
||||||
URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
|
URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
|
||||||
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
|
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW 1
|
||||||
)
|
)
|
||||||
|
|
||||||
# Disable warning about no project version.
|
# Disable warning about no project version.
|
||||||
@@ -70,5 +76,6 @@ else ()
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_link_libraries(dCommon
|
target_link_libraries(dCommon
|
||||||
|
PUBLIC glm::glm
|
||||||
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
|
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
|
||||||
INTERFACE dDatabase)
|
INTERFACE dDatabase)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
# define DluAssert(expression) assert(expression)
|
# define DluAssert(expression) do { assert(expression); } while(0)
|
||||||
#else
|
#else
|
||||||
# define DluAssert(expression)
|
# define DluAssert(expression)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// C++
|
// C++
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cmath>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -19,6 +20,8 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
|
#include <glm/ext/vector_float3.hpp>
|
||||||
|
|
||||||
enum eInventoryType : uint32_t;
|
enum eInventoryType : uint32_t;
|
||||||
enum class eObjectBits : size_t;
|
enum class eObjectBits : size_t;
|
||||||
enum class eReplicaComponentType : uint32_t;
|
enum class eReplicaComponentType : uint32_t;
|
||||||
@@ -145,7 +148,7 @@ namespace GeneralUtils {
|
|||||||
template <typename... Bases>
|
template <typename... Bases>
|
||||||
struct overload : Bases... {
|
struct overload : Bases... {
|
||||||
using is_transparent = void;
|
using is_transparent = void;
|
||||||
using Bases::operator() ... ;
|
using Bases::operator() ...;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct char_pointer_hash {
|
struct char_pointer_hash {
|
||||||
@@ -202,7 +205,7 @@ namespace GeneralUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires(!Numeric<T>)
|
requires(!Numeric<T>)
|
||||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
||||||
|
|
||||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
||||||
@@ -221,7 +224,7 @@ namespace GeneralUtils {
|
|||||||
*/
|
*/
|
||||||
template <std::floating_point T>
|
template <std::floating_point T>
|
||||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
||||||
try {
|
try {
|
||||||
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
||||||
|
|
||||||
size_t parseNum;
|
size_t parseNum;
|
||||||
@@ -243,7 +246,7 @@ namespace GeneralUtils {
|
|||||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
|
[[nodiscard]] std::optional<T> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
|
||||||
const auto x = TryParse<float>(strX);
|
const auto x = TryParse<float>(strX);
|
||||||
if (!x) return std::nullopt;
|
if (!x) return std::nullopt;
|
||||||
|
|
||||||
@@ -251,7 +254,7 @@ namespace GeneralUtils {
|
|||||||
if (!y) return std::nullopt;
|
if (!y) return std::nullopt;
|
||||||
|
|
||||||
const auto z = TryParse<float>(strZ);
|
const auto z = TryParse<float>(strZ);
|
||||||
return z ? std::make_optional<NiPoint3>(x.value(), y.value(), z.value()) : std::nullopt;
|
return z ? std::make_optional<T>(x.value(), y.value(), z.value()) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,8 +263,8 @@ namespace GeneralUtils {
|
|||||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) {
|
[[nodiscard]] std::optional<T> TryParse(const std::span<const std::string> str) {
|
||||||
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
|
return (str.size() == 3) ? TryParse<T>(str[0], str[1], str[2]) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -299,6 +302,12 @@ namespace GeneralUtils {
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Container>
|
||||||
|
inline Container::value_type GetRandomElement(const Container& container) {
|
||||||
|
DluAssert(!container.empty());
|
||||||
|
return container[GenerateRandomNumber<typename Container::value_type>(0, container.size() - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Casts the value of an enum entry to its underlying type
|
* Casts the value of an enum entry to its underlying type
|
||||||
* @param entry Enum entry to cast
|
* @param entry Enum entry to cast
|
||||||
@@ -323,4 +332,28 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.quora.com/How-do-you-round-to-specific-increments-like-0-5-in-C
|
||||||
|
// Rounds to the nearest floating point value specified.
|
||||||
|
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||||
|
T RountToNearestEven(const T value, const T modulus) {
|
||||||
|
const auto modulo = std::fmod(value, modulus);
|
||||||
|
const auto abs_modulo_2 = std::abs(modulo * 2);
|
||||||
|
const auto abs_modulus = std::abs(modulus);
|
||||||
|
|
||||||
|
bool round_away_from_zero = false;
|
||||||
|
if (abs_modulo_2 > abs_modulus) {
|
||||||
|
round_away_from_zero = true;
|
||||||
|
} else if (abs_modulo_2 == abs_modulus) {
|
||||||
|
const auto trunc_quot = std::floor(std::abs(value / modulus));
|
||||||
|
const auto odd = std::fmod(trunc_quot, T{ 2 }) != 0;
|
||||||
|
round_away_from_zero = odd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (round_away_from_zero) {
|
||||||
|
return value + (std::copysign(modulus, value) - modulo);
|
||||||
|
} else {
|
||||||
|
return value - modulo;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,4 +15,3 @@ std::string JSONUtils::CheckRequiredData(const json& data, const std::vector<std
|
|||||||
}
|
}
|
||||||
return check["error"].empty() ? "" : check.dump();
|
return check["error"].empty() ? "" : check.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "json_fwd.hpp"
|
#include "json_fwd.hpp"
|
||||||
|
|
||||||
namespace JSONUtils {
|
namespace JSONUtils {
|
||||||
// check required data for reqeust
|
// check required fields in json data
|
||||||
std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
|
std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,12 @@ public:
|
|||||||
this->value = value;
|
this->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! Initializer
|
||||||
|
LDFData(const std::string& key, const T& value) {
|
||||||
|
this->key = GeneralUtils::ASCIIToUTF16(key);
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
//! Destructor
|
//! Destructor
|
||||||
~LDFData(void) override {}
|
~LDFData(void) override {}
|
||||||
|
|
||||||
|
|||||||
@@ -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!
|
// 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
|
// 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.
|
// 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(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_DEBUG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->LogDebug(str_, message, ##__VA_ARGS__); } while(0)
|
||||||
|
|
||||||
// Writer class for writing data to files.
|
// Writer class for writing data to files.
|
||||||
class Writer {
|
class Writer {
|
||||||
|
|||||||
130
dCommon/Lxfml.cpp
Normal file
130
dCommon/Lxfml.cpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#include "Lxfml.h"
|
||||||
|
|
||||||
|
#include "GeneralUtils.h"
|
||||||
|
#include "StringifiedEnum.h"
|
||||||
|
#include "TinyXmlUtils.h"
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
|
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
|
||||||
|
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");
|
||||||
|
while (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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part = part->NextSiblingElement("Part");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 };
|
||||||
|
|
||||||
|
NiPoint3 delta = NiPoint3Constant::ZERO;
|
||||||
|
if (curPosition == NiPoint3Constant::ZERO) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = (highest - lowest) / 2.0f;
|
||||||
|
} else {
|
||||||
|
lowest = curPosition;
|
||||||
|
highest = curPosition;
|
||||||
|
delta = NiPoint3Constant::ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newRootPos = lowest + delta;
|
||||||
|
|
||||||
|
// Need to snap this chosen position to the nearest valid spot
|
||||||
|
// on the LEGO grid
|
||||||
|
newRootPos.x = GeneralUtils::RountToNearestEven(newRootPos.x, 0.8f);
|
||||||
|
newRootPos.z = GeneralUtils::RountToNearestEven(newRootPos.z, 0.8f);
|
||||||
|
|
||||||
|
// 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 + curPosition.x;
|
||||||
|
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y + curPosition.y;
|
||||||
|
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z + curPosition.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");
|
||||||
|
while (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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part = part->NextSiblingElement("Part");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyxml2::XMLPrinter printer;
|
||||||
|
doc.Print(&printer);
|
||||||
|
|
||||||
|
toReturn.lxfml = printer.CStr();
|
||||||
|
toReturn.center = newRootPos;
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
27
dCommon/Lxfml.h
Normal file
27
dCommon/Lxfml.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// 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, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
|
||||||
|
|
||||||
|
// these are only for the migrations due to a bug in one of the implementations.
|
||||||
|
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);
|
||||||
|
[[nodiscard]] Result NormalizePositionAfterFirstPart(const std::string_view data, const NiPoint3& position);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!LXFML_H
|
||||||
210
dCommon/LxfmlBugged.cpp
Normal file
210
dCommon/LxfmlBugged.cpp
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
#include "Lxfml.h"
|
||||||
|
|
||||||
|
#include "GeneralUtils.h"
|
||||||
|
#include "StringifiedEnum.h"
|
||||||
|
#include "TinyXmlUtils.h"
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
|
// this file should not be touched
|
||||||
|
|
||||||
|
Lxfml::Result Lxfml::NormalizePositionOnlyFirstPart(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lxfml::Result Lxfml::NormalizePositionAfterFirstPart(const std::string_view data, const NiPoint3& position) {
|
||||||
|
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");
|
||||||
|
bool firstPart = true;
|
||||||
|
while (part) {
|
||||||
|
if (firstPart) {
|
||||||
|
firstPart = false;
|
||||||
|
} else {
|
||||||
|
LOG("Found extra bricks");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part = part->NextSiblingElement("Part");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newRootPos = position;
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
bool firstPart = true;
|
||||||
|
while (part) {
|
||||||
|
if (firstPart) {
|
||||||
|
firstPart = false;
|
||||||
|
} else {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part = part->NextSiblingElement("Part");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyxml2::XMLPrinter printer;
|
||||||
|
doc.Print(&printer);
|
||||||
|
|
||||||
|
toReturn.lxfml = printer.CStr();
|
||||||
|
toReturn.center = newRootPos;
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
@@ -6,10 +6,14 @@
|
|||||||
\brief Defines a point in space in XYZ coordinates
|
\brief Defines a point in space in XYZ coordinates
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
class NiPoint3;
|
class NiPoint3;
|
||||||
class NiQuaternion;
|
|
||||||
typedef NiPoint3 Vector3; //!< The Vector3 class is technically the NiPoint3 class, but typedef'd for clarity in some cases
|
typedef NiPoint3 Vector3; //!< The Vector3 class is technically the NiPoint3 class, but typedef'd for clarity in some cases
|
||||||
|
|
||||||
|
#include <glm/ext/vector_float3.hpp>
|
||||||
|
|
||||||
|
#include "NiQuaternion.h"
|
||||||
|
|
||||||
//! A custom class the defines a point in space
|
//! A custom class the defines a point in space
|
||||||
class NiPoint3 {
|
class NiPoint3 {
|
||||||
public:
|
public:
|
||||||
@@ -21,6 +25,12 @@ public:
|
|||||||
//! Initializer
|
//! Initializer
|
||||||
constexpr NiPoint3() = default;
|
constexpr NiPoint3() = default;
|
||||||
|
|
||||||
|
constexpr NiPoint3(const glm::vec3& vec) noexcept
|
||||||
|
: x{ vec.x }
|
||||||
|
, y{ vec.y }
|
||||||
|
, z{ vec.z } {
|
||||||
|
}
|
||||||
|
|
||||||
//! Initializer
|
//! Initializer
|
||||||
/*!
|
/*!
|
||||||
\param x The x coordinate
|
\param x The x coordinate
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "NiQuaternion.h"
|
#include "NiQuaternion.h"
|
||||||
|
#include <glm/ext/quaternion_float.hpp>
|
||||||
|
|
||||||
// MARK: Getters / Setters
|
// MARK: Getters / Setters
|
||||||
|
|
||||||
|
|||||||
@@ -3,37 +3,18 @@
|
|||||||
// C++
|
// C++
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
// MARK: Member Functions
|
// MARK: Member Functions
|
||||||
|
|
||||||
Vector3 NiQuaternion::GetEulerAngles() const {
|
Vector3 QuatUtils::Euler(const NiQuaternion& quat) {
|
||||||
Vector3 angles;
|
return glm::eulerAngles(quat);
|
||||||
|
|
||||||
// roll (x-axis rotation)
|
|
||||||
const float sinr_cosp = 2 * (w * x + y * z);
|
|
||||||
const float cosr_cosp = 1 - 2 * (x * x + y * y);
|
|
||||||
angles.x = std::atan2(sinr_cosp, cosr_cosp);
|
|
||||||
|
|
||||||
// pitch (y-axis rotation)
|
|
||||||
const float sinp = 2 * (w * y - z * x);
|
|
||||||
|
|
||||||
if (std::abs(sinp) >= 1) {
|
|
||||||
angles.y = std::copysign(3.14 / 2, sinp); // use 90 degrees if out of range
|
|
||||||
} else {
|
|
||||||
angles.y = std::asin(sinp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// yaw (z-axis rotation)
|
|
||||||
const float siny_cosp = 2 * (w * z + x * y);
|
|
||||||
const float cosy_cosp = 1 - 2 * (y * y + z * z);
|
|
||||||
angles.z = std::atan2(siny_cosp, cosy_cosp);
|
|
||||||
|
|
||||||
return angles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Helper Functions
|
// MARK: Helper Functions
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space (Y-locked)
|
//! Look from a specific point in space to another point in space (Y-locked)
|
||||||
NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
NiQuaternion QuatUtils::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
||||||
//To make sure we don't orient around the X/Z axis:
|
//To make sure we don't orient around the X/Z axis:
|
||||||
NiPoint3 source = sourcePoint;
|
NiPoint3 source = sourcePoint;
|
||||||
NiPoint3 dest = destPoint;
|
NiPoint3 dest = destPoint;
|
||||||
@@ -51,11 +32,11 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
|
|||||||
NiPoint3 vecB = vecA.CrossProduct(posZ);
|
NiPoint3 vecB = vecA.CrossProduct(posZ);
|
||||||
|
|
||||||
if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle;
|
if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle;
|
||||||
return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle);
|
return glm::angleAxis(rotAngle, glm::vec3{vecA.x, vecA.y, vecA.z});
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space
|
//! Look from a specific point in space to another point in space
|
||||||
NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
NiQuaternion QuatUtils::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
||||||
NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize();
|
NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize();
|
||||||
|
|
||||||
NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
|
NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
|
||||||
@@ -67,37 +48,26 @@ NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiP
|
|||||||
NiPoint3 vecB = vecA.CrossProduct(posZ);
|
NiPoint3 vecB = vecA.CrossProduct(posZ);
|
||||||
|
|
||||||
if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle;
|
if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle;
|
||||||
return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle);
|
return glm::angleAxis(rotAngle, glm::vec3{vecA.x, vecA.y, vecA.z});
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Creates a Quaternion from a specific axis and angle relative to that axis
|
//! Creates a Quaternion from a specific axis and angle relative to that axis
|
||||||
NiQuaternion NiQuaternion::CreateFromAxisAngle(const Vector3& axis, float angle) {
|
NiQuaternion QuatUtils::AxisAngle(const Vector3& axis, float angle) {
|
||||||
float halfAngle = angle * 0.5f;
|
return glm::angleAxis(angle, glm::vec3(axis.x, axis.y, axis.z));
|
||||||
float s = static_cast<float>(sin(halfAngle));
|
|
||||||
|
|
||||||
NiQuaternion q;
|
|
||||||
q.x = axis.GetX() * s;
|
|
||||||
q.y = axis.GetY() * s;
|
|
||||||
q.z = axis.GetZ() * s;
|
|
||||||
q.w = static_cast<float>(cos(halfAngle));
|
|
||||||
|
|
||||||
return q;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NiQuaternion NiQuaternion::FromEulerAngles(const NiPoint3& eulerAngles) {
|
NiQuaternion QuatUtils::FromEuler(const NiPoint3& eulerAngles) {
|
||||||
// Abbreviations for the various angular functions
|
return glm::quat(glm::vec3(eulerAngles.x, eulerAngles.y, eulerAngles.z));
|
||||||
float cy = cos(eulerAngles.z * 0.5);
|
}
|
||||||
float sy = sin(eulerAngles.z * 0.5);
|
|
||||||
float cp = cos(eulerAngles.y * 0.5);
|
Vector3 QuatUtils::Forward(const NiQuaternion& quat) {
|
||||||
float sp = sin(eulerAngles.y * 0.5);
|
return quat * glm::vec3(0, 0, 1);
|
||||||
float cr = cos(eulerAngles.x * 0.5);
|
}
|
||||||
float sr = sin(eulerAngles.x * 0.5);
|
|
||||||
|
Vector3 QuatUtils::Up(const NiQuaternion& quat) {
|
||||||
NiQuaternion q;
|
return quat * glm::vec3(0, 1, 0);
|
||||||
q.w = cr * cp * cy + sr * sp * sy;
|
}
|
||||||
q.x = sr * cp * cy - cr * sp * sy;
|
|
||||||
q.y = cr * sp * cy + sr * cp * sy;
|
Vector3 QuatUtils::Right(const NiQuaternion& quat) {
|
||||||
q.z = cr * cp * sy - sr * sp * cy;
|
return quat * glm::vec3(1, 0, 0);
|
||||||
|
|
||||||
return q;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,158 +1,27 @@
|
|||||||
#ifndef __NIQUATERNION_H__
|
#ifndef NIQUATERNION_H
|
||||||
#define __NIQUATERNION_H__
|
#define NIQUATERNION_H
|
||||||
|
|
||||||
// Custom Classes
|
// Custom Classes
|
||||||
#include "NiPoint3.h"
|
#include "NiPoint3.h"
|
||||||
|
|
||||||
/*!
|
#define GLM_FORCE_QUAT_DATA_WXYZ
|
||||||
\file NiQuaternion.hpp
|
|
||||||
\brief Defines a quaternion in space in WXYZ coordinates
|
|
||||||
*/
|
|
||||||
|
|
||||||
class NiQuaternion;
|
#include <glm/ext/quaternion_float.hpp>
|
||||||
typedef NiQuaternion Quaternion; //!< A typedef for a shorthand version of NiQuaternion
|
|
||||||
|
|
||||||
//! A class that defines a rotation in space
|
using Quaternion = glm::quat;
|
||||||
class NiQuaternion {
|
using NiQuaternion = Quaternion;
|
||||||
public:
|
|
||||||
float w{ 1 }; //!< The w coordinate
|
|
||||||
float x{ 0 }; //!< The x coordinate
|
|
||||||
float y{ 0 }; //!< The y coordinate
|
|
||||||
float z{ 0 }; //!< The z coordinate
|
|
||||||
|
|
||||||
|
namespace QuatUtils {
|
||||||
//! The initializer
|
constexpr NiQuaternion IDENTITY = glm::identity<NiQuaternion>();
|
||||||
constexpr NiQuaternion() = default;
|
Vector3 Forward(const NiQuaternion& quat);
|
||||||
|
Vector3 Up(const NiQuaternion& quat);
|
||||||
//! The initializer
|
Vector3 Right(const NiQuaternion& quat);
|
||||||
/*!
|
NiQuaternion LookAt(const NiPoint3& from, const NiPoint3& to);
|
||||||
\param w The w coordinate
|
NiQuaternion LookAtUnlocked(const NiPoint3& from, const NiPoint3& to);
|
||||||
\param x The x coordinate
|
Vector3 Euler(const NiQuaternion& quat);
|
||||||
\param y The y coordinate
|
NiQuaternion AxisAngle(const Vector3& axis, float angle);
|
||||||
\param z The z coordinate
|
NiQuaternion FromEuler(const NiPoint3& eulerAngles);
|
||||||
*/
|
constexpr float PI_OVER_180 = glm::pi<float>() / 180.0f;
|
||||||
constexpr NiQuaternion(const float w, const float x, const float y, const float z) noexcept
|
|
||||||
: w{ w }
|
|
||||||
, x{ x }
|
|
||||||
, y{ y }
|
|
||||||
, z{ z } {
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Setters / Getters
|
|
||||||
|
|
||||||
//! Gets the W coordinate
|
|
||||||
/*!
|
|
||||||
\return The w coordinate
|
|
||||||
*/
|
|
||||||
[[nodiscard]] constexpr float GetW() const noexcept;
|
|
||||||
|
|
||||||
//! Sets the W coordinate
|
|
||||||
/*!
|
|
||||||
\param w The w coordinate
|
|
||||||
*/
|
|
||||||
constexpr void SetW(const float w) noexcept;
|
|
||||||
|
|
||||||
//! Gets the X coordinate
|
|
||||||
/*!
|
|
||||||
\return The x coordinate
|
|
||||||
*/
|
|
||||||
[[nodiscard]] constexpr float GetX() const noexcept;
|
|
||||||
|
|
||||||
//! Sets the X coordinate
|
|
||||||
/*!
|
|
||||||
\param x The x coordinate
|
|
||||||
*/
|
|
||||||
constexpr void SetX(const float x) noexcept;
|
|
||||||
|
|
||||||
//! Gets the Y coordinate
|
|
||||||
/*!
|
|
||||||
\return The y coordinate
|
|
||||||
*/
|
|
||||||
[[nodiscard]] constexpr float GetY() const noexcept;
|
|
||||||
|
|
||||||
//! Sets the Y coordinate
|
|
||||||
/*!
|
|
||||||
\param y The y coordinate
|
|
||||||
*/
|
|
||||||
constexpr void SetY(const float y) noexcept;
|
|
||||||
|
|
||||||
//! Gets the Z coordinate
|
|
||||||
/*!
|
|
||||||
\return The z coordinate
|
|
||||||
*/
|
|
||||||
[[nodiscard]] constexpr float GetZ() const noexcept;
|
|
||||||
|
|
||||||
//! Sets the Z coordinate
|
|
||||||
/*!
|
|
||||||
\param z The z coordinate
|
|
||||||
*/
|
|
||||||
constexpr void SetZ(const float z) noexcept;
|
|
||||||
|
|
||||||
// MARK: Member Functions
|
|
||||||
|
|
||||||
//! Returns the forward vector from the quaternion
|
|
||||||
/*!
|
|
||||||
\return The forward vector of the quaternion
|
|
||||||
*/
|
|
||||||
[[nodiscard]] constexpr Vector3 GetForwardVector() const noexcept;
|
|
||||||
|
|
||||||
//! Returns the up vector from the quaternion
|
|
||||||
/*!
|
|
||||||
\return The up vector fo the quaternion
|
|
||||||
*/
|
|
||||||
[[nodiscard]] constexpr Vector3 GetUpVector() const noexcept;
|
|
||||||
|
|
||||||
//! Returns the right vector from the quaternion
|
|
||||||
/*!
|
|
||||||
\return The right vector of the quaternion
|
|
||||||
*/
|
|
||||||
[[nodiscard]] constexpr Vector3 GetRightVector() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] Vector3 GetEulerAngles() const;
|
|
||||||
|
|
||||||
// MARK: Operators
|
|
||||||
|
|
||||||
//! Operator to check for equality
|
|
||||||
constexpr bool operator==(const NiQuaternion& rot) const noexcept;
|
|
||||||
|
|
||||||
//! Operator to check for inequality
|
|
||||||
constexpr bool operator!=(const NiQuaternion& rot) const noexcept;
|
|
||||||
|
|
||||||
// MARK: Helper Functions
|
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space (Y-locked)
|
|
||||||
/*!
|
|
||||||
\param sourcePoint The source location
|
|
||||||
\param destPoint The destination location
|
|
||||||
\return The Quaternion with the rotation towards the destination
|
|
||||||
*/
|
|
||||||
[[nodiscard]] static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
|
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space
|
|
||||||
/*!
|
|
||||||
\param sourcePoint The source location
|
|
||||||
\param destPoint The destination location
|
|
||||||
\return The Quaternion with the rotation towards the destination
|
|
||||||
*/
|
|
||||||
[[nodiscard]] static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
|
|
||||||
|
|
||||||
//! Creates a Quaternion from a specific axis and angle relative to that axis
|
|
||||||
/*!
|
|
||||||
\param axis The axis that is used
|
|
||||||
\param angle The angle relative to this axis
|
|
||||||
\return A quaternion created from the axis and angle
|
|
||||||
*/
|
|
||||||
[[nodiscard]] static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle);
|
|
||||||
|
|
||||||
[[nodiscard]] static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Static Variables
|
#endif // !NIQUATERNION_H
|
||||||
namespace NiQuaternionConstant {
|
|
||||||
constexpr NiQuaternion IDENTITY(1, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include constexpr and inline function definitions in a seperate file for readability
|
|
||||||
#include "NiQuaternion.inl"
|
|
||||||
|
|
||||||
#endif // !__NIQUATERNION_H__
|
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#ifndef __NIQUATERNION_H__
|
|
||||||
#error "This should only be included inline in NiQuaternion.h: Do not include directly!"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: Setters / Getters
|
|
||||||
|
|
||||||
//! Gets the W coordinate
|
|
||||||
constexpr float NiQuaternion::GetW() const noexcept {
|
|
||||||
return this->w;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the W coordinate
|
|
||||||
constexpr void NiQuaternion::SetW(const float w) noexcept {
|
|
||||||
this->w = w;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the X coordinate
|
|
||||||
constexpr float NiQuaternion::GetX() const noexcept {
|
|
||||||
return this->x;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the X coordinate
|
|
||||||
constexpr void NiQuaternion::SetX(const float x) noexcept {
|
|
||||||
this->x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the Y coordinate
|
|
||||||
constexpr float NiQuaternion::GetY() const noexcept {
|
|
||||||
return this->y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the Y coordinate
|
|
||||||
constexpr void NiQuaternion::SetY(const float y) noexcept {
|
|
||||||
this->y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the Z coordinate
|
|
||||||
constexpr float NiQuaternion::GetZ() const noexcept {
|
|
||||||
return this->z;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the Z coordinate
|
|
||||||
constexpr void NiQuaternion::SetZ(const float z) noexcept {
|
|
||||||
this->z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Member Functions
|
|
||||||
|
|
||||||
//! Returns the forward vector from the quaternion
|
|
||||||
constexpr Vector3 NiQuaternion::GetForwardVector() const noexcept {
|
|
||||||
return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y));
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Returns the up vector from the quaternion
|
|
||||||
constexpr Vector3 NiQuaternion::GetUpVector() const noexcept {
|
|
||||||
return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x));
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Returns the right vector from the quaternion
|
|
||||||
constexpr Vector3 NiQuaternion::GetRightVector() const noexcept {
|
|
||||||
return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y));
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Operators
|
|
||||||
|
|
||||||
//! Operator to check for equality
|
|
||||||
constexpr bool NiQuaternion::operator==(const NiQuaternion& rot) const noexcept {
|
|
||||||
return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator to check for inequality
|
|
||||||
constexpr bool NiQuaternion::operator!=(const NiQuaternion& rot) const noexcept {
|
|
||||||
return !(*this == rot);
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ struct LocalSpaceInfo {
|
|||||||
|
|
||||||
struct PositionUpdate {
|
struct PositionUpdate {
|
||||||
NiPoint3 position = NiPoint3Constant::ZERO;
|
NiPoint3 position = NiPoint3Constant::ZERO;
|
||||||
NiQuaternion rotation = NiQuaternionConstant::IDENTITY;
|
NiQuaternion rotation = QuatUtils::IDENTITY;
|
||||||
bool onGround = false;
|
bool onGround = false;
|
||||||
bool onRail = false;
|
bool onRail = false;
|
||||||
NiPoint3 velocity = NiPoint3Constant::ZERO;
|
NiPoint3 velocity = NiPoint3Constant::ZERO;
|
||||||
|
|||||||
150
dCommon/Sd0.cpp
Normal file
150
dCommon/Sd0.cpp
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
42
dCommon/Sd0.h
Normal file
42
dCommon/Sd0.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// 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
|
||||||
37
dCommon/TinyXmlUtils.cpp
Normal file
37
dCommon/TinyXmlUtils.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
66
dCommon/TinyXmlUtils.h
Normal file
66
dCommon/TinyXmlUtils.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// 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,11 +8,5 @@ namespace ZCompression {
|
|||||||
int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst);
|
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);
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Pack.h"
|
#include "Pack.h"
|
||||||
|
|
||||||
#include "BinaryIO.h"
|
#include "BinaryIO.h"
|
||||||
|
#include "Sd0.h"
|
||||||
#include "ZCompression.h"
|
#include "ZCompression.h"
|
||||||
|
|
||||||
Pack::Pack(const std::filesystem::path& filePath) {
|
Pack::Pack(const std::filesystem::path& filePath) {
|
||||||
@@ -106,7 +107,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
|
pos += size; // Move pointer position the amount of bytes read to the right
|
||||||
|
|
||||||
int32_t err;
|
int32_t err;
|
||||||
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
|
||||||
|
|
||||||
free(chunk);
|
free(chunk);
|
||||||
}
|
}
|
||||||
|
|||||||
14
dCommon/dEnums/ServiceType.h
Normal file
14
dCommon/dEnums/ServiceType.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __SERVICETYPE__H__
|
||||||
|
#define __SERVICETYPE__H__
|
||||||
|
|
||||||
|
enum class ServiceType : uint16_t {
|
||||||
|
COMMON = 0,
|
||||||
|
AUTH,
|
||||||
|
CHAT,
|
||||||
|
WORLD = 4,
|
||||||
|
CLIENT,
|
||||||
|
MASTER,
|
||||||
|
UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!__SERVICETYPE__H__
|
||||||
@@ -3,13 +3,14 @@
|
|||||||
#ifndef __DCOMMONVARS__H__
|
#ifndef __DCOMMONVARS__H__
|
||||||
#define __DCOMMONVARS__H__
|
#define __DCOMMONVARS__H__
|
||||||
|
|
||||||
|
#include <compare>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <string>
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include "eConnectionType.h"
|
|
||||||
#include "MessageType/Client.h"
|
|
||||||
#include "BitStreamUtils.h"
|
#include "BitStreamUtils.h"
|
||||||
|
#include "MessageType/Client.h"
|
||||||
|
#include "ServiceType.h"
|
||||||
|
|
||||||
#pragma warning (disable:4251) //Disables SQL warnings
|
#pragma warning (disable:4251) //Disables SQL warnings
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
|
|||||||
#define CBITSTREAM RakNet::BitStream bitStream;
|
#define CBITSTREAM RakNet::BitStream bitStream;
|
||||||
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
|
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||||
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
|
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
|
||||||
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
|
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::GAME_MSG);
|
||||||
#define SEND_PACKET Game::server->Send(bitStream, sysAddr, false);
|
#define SEND_PACKET Game::server->Send(bitStream, sysAddr, false);
|
||||||
#define SEND_PACKET_BROADCAST Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
#define SEND_PACKET_BROADCAST Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||||
|
|
||||||
@@ -98,6 +99,8 @@ public:
|
|||||||
constexpr LWOZONEID() noexcept = default;
|
constexpr LWOZONEID() noexcept = default;
|
||||||
constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; }
|
constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; }
|
||||||
constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; }
|
constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; }
|
||||||
|
constexpr bool operator==(const LWOZONEID&) const = default;
|
||||||
|
constexpr auto operator<=>(const LWOZONEID&) const = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc...
|
LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc...
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ enum class eCharacterVersion : uint32_t {
|
|||||||
VAULT_SIZE,
|
VAULT_SIZE,
|
||||||
// Fixes speed base value in level component
|
// Fixes speed base value in level component
|
||||||
SPEED_BASE,
|
SPEED_BASE,
|
||||||
UP_TO_DATE, // will become NJ_JAYMISSIONS
|
// Fixes nexus force explorer missions
|
||||||
|
NJ_JAYMISSIONS,
|
||||||
|
UP_TO_DATE, // will become NEXUS_FORCE_EXPLORER
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__ECHARACTERVERSION__H__
|
#endif //!__ECHARACTERVERSION__H__
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
#ifndef __ECHATMESSAGERESPONSECODES__H__
|
|
||||||
#define __ECHATMESSAGERESPONSECODES__H__
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
enum class eChatMessageResponseCode : uint8_t {
|
|
||||||
SENT = 0,
|
|
||||||
NOTONLINE,
|
|
||||||
GENERALERROR,
|
|
||||||
RECEIVEDNEWWHISPER,
|
|
||||||
NOTFRIENDS,
|
|
||||||
SENDERFREETRIAL,
|
|
||||||
RECEIVERFREETRIAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__ECHATMESSAGERESPONSECODES__H__
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#ifndef __ECONNECTIONTYPE__H__
|
|
||||||
#define __ECONNECTIONTYPE__H__
|
|
||||||
|
|
||||||
enum class eConnectionType : uint16_t {
|
|
||||||
SERVER = 0,
|
|
||||||
AUTH,
|
|
||||||
CHAT,
|
|
||||||
WORLD = 4,
|
|
||||||
CLIENT,
|
|
||||||
MASTER,
|
|
||||||
UNKNOWN
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__ECONNECTIONTYPE__H__
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef __EHTTPMETHODS__H__
|
#ifndef __EHTTPMETHODS__H__
|
||||||
#define __EHTTPMETHODS__H__
|
#define __EHTTPMETHODS__H__
|
||||||
|
|
||||||
|
#include "dPlatforms.h"
|
||||||
|
|
||||||
#ifdef DARKFLAME_PLATFORM_WIN32
|
#ifdef DARKFLAME_PLATFORM_WIN32
|
||||||
#pragma push_macro("DELETE")
|
#pragma push_macro("DELETE")
|
||||||
#undef DELETE
|
#undef DELETE
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ DEFINE_TABLE_STORAGE(CDScriptComponentTable);
|
|||||||
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
||||||
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
|
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
|
||||||
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
||||||
DEFINE_TABLE_STORAGE(CDZoneTableTable);
|
|
||||||
|
|
||||||
void CDClientManager::LoadValuesFromDatabase() {
|
void CDClientManager::LoadValuesFromDatabase() {
|
||||||
if (!CDClientDatabase::isConnected) {
|
if (!CDClientDatabase::isConnected) {
|
||||||
@@ -149,7 +148,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
|||||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||||
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
|
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
|
||||||
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
CDZoneTableTable::Instance().LoadValuesFromDatabase();
|
CDZoneTableTable::LoadValuesFromDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDClientManager::LoadValuesFromDefaults() {
|
void CDClientManager::LoadValuesFromDefaults() {
|
||||||
|
|||||||
@@ -1,67 +1,53 @@
|
|||||||
#include "CDZoneTableTable.h"
|
#include "CDZoneTableTable.h"
|
||||||
|
|
||||||
void CDZoneTableTable::LoadValuesFromDatabase() {
|
namespace CDZoneTableTable {
|
||||||
|
Table entries;
|
||||||
|
|
||||||
// First, get the size of the table
|
void LoadValuesFromDatabase() {
|
||||||
uint32_t size = 0;
|
// Get the data from the database
|
||||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ZoneTable");
|
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ZoneTable");
|
||||||
while (!tableSize.eof()) {
|
while (!tableData.eof()) {
|
||||||
size = tableSize.getIntField(0, 0);
|
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;
|
||||||
|
|
||||||
tableSize.nextRow();
|
entries[entry.zoneID] = entry;
|
||||||
|
tableData.nextRow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tableSize.finalize();
|
//! 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;
|
||||||
|
}
|
||||||
|
|
||||||
// Now get the data
|
return nullptr;
|
||||||
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
|
bool mountsAllowed; //!< Whether or not mounts are allowed
|
||||||
};
|
};
|
||||||
|
|
||||||
class CDZoneTableTable : public CDTable<CDZoneTableTable, std::map<uint32_t, CDZoneTable>> {
|
namespace CDZoneTableTable {
|
||||||
public:
|
using Table = std::map<uint32_t, CDZoneTable>;
|
||||||
void LoadValuesFromDatabase();
|
void LoadValuesFromDatabase();
|
||||||
|
|
||||||
// Queries the table with a zoneID to find.
|
// Queries the table with a zoneID to find.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ target_include_directories(dDatabaseCDClient PUBLIC "."
|
|||||||
"${PROJECT_SOURCE_DIR}/dCommon"
|
"${PROJECT_SOURCE_DIR}/dCommon"
|
||||||
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
||||||
)
|
)
|
||||||
target_link_libraries(dDatabaseCDClient PRIVATE sqlite3)
|
target_link_libraries(dDatabaseCDClient PRIVATE sqlite3 glm::glm)
|
||||||
|
|
||||||
if (${CDCLIENT_CACHE_ALL})
|
if (${CDCLIENT_CACHE_ALL})
|
||||||
add_compile_definitions(dDatabaseCDClient PRIVATE CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})
|
add_compile_definitions(dDatabaseCDClient PRIVATE CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
add_subdirectory(CDClientDatabase)
|
add_subdirectory(CDClientDatabase)
|
||||||
add_subdirectory(GameDatabase)
|
add_subdirectory(GameDatabase)
|
||||||
|
|
||||||
add_library(dDatabase STATIC "MigrationRunner.cpp")
|
add_library(dDatabase STATIC "MigrationRunner.cpp" "ModelNormalizeMigration.cpp")
|
||||||
|
|
||||||
add_custom_target(conncpp_dylib
|
add_custom_target(conncpp_dylib
|
||||||
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})
|
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})
|
||||||
@@ -10,4 +10,5 @@ add_dependencies(dDatabase conncpp_dylib)
|
|||||||
|
|
||||||
target_include_directories(dDatabase PUBLIC ".")
|
target_include_directories(dDatabase PUBLIC ".")
|
||||||
target_link_libraries(dDatabase
|
target_link_libraries(dDatabase
|
||||||
PUBLIC dDatabaseCDClient dDatabaseGame)
|
PUBLIC dDatabaseCDClient dDatabaseGame
|
||||||
|
PRIVATE glm::glm)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ target_include_directories(dDatabaseGame PUBLIC "."
|
|||||||
|
|
||||||
target_link_libraries(dDatabaseGame
|
target_link_libraries(dDatabaseGame
|
||||||
INTERFACE dCommon
|
INTERFACE dCommon
|
||||||
PRIVATE sqlite3 MariaDB::ConnCpp)
|
PRIVATE sqlite3 MariaDB::ConnCpp glm::glm)
|
||||||
|
|
||||||
# Glob together all headers that need to be precompiled
|
# Glob together all headers that need to be precompiled
|
||||||
file(
|
file(
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public:
|
|||||||
std::string bcryptPassword;
|
std::string bcryptPassword;
|
||||||
uint32_t id{};
|
uint32_t id{};
|
||||||
uint32_t playKeyId{};
|
uint32_t playKeyId{};
|
||||||
|
uint64_t muteExpire{};
|
||||||
bool banned{};
|
bool banned{};
|
||||||
bool locked{};
|
bool locked{};
|
||||||
eGameMasterLevel maxGmLevel{};
|
eGameMasterLevel maxGmLevel{};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
enum class eActivityType : uint32_t {
|
enum class eActivityType : uint32_t {
|
||||||
PlayerLoggedIn,
|
PlayerLoggedIn,
|
||||||
PlayerLoggedOut,
|
PlayerLoggedOut,
|
||||||
PlayerChangedZone,
|
PlayerChangedZone
|
||||||
};
|
};
|
||||||
|
|
||||||
class IActivityLog {
|
class IActivityLog {
|
||||||
|
|||||||
@@ -8,15 +8,15 @@
|
|||||||
class IBehaviors {
|
class IBehaviors {
|
||||||
public:
|
public:
|
||||||
struct Info {
|
struct Info {
|
||||||
int32_t behaviorId{};
|
LWOOBJID behaviorId{};
|
||||||
uint32_t characterId{};
|
uint32_t characterId{};
|
||||||
std::string behaviorInfo;
|
std::string behaviorInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This Add also takes care of updating if it exists.
|
// This Add also takes care of updating if it exists.
|
||||||
virtual void AddBehavior(const Info& info) = 0;
|
virtual void AddBehavior(const Info& info) = 0;
|
||||||
virtual std::string GetBehavior(const int32_t behaviorId) = 0;
|
virtual std::string GetBehavior(const LWOOBJID behaviorId) = 0;
|
||||||
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
|
virtual void RemoveBehavior(const LWOOBJID behaviorId) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!IBEHAVIORS_H
|
#endif //!IBEHAVIORS_H
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ public:
|
|||||||
// Update the property details for the given property id.
|
// Update the property details for the given property id.
|
||||||
virtual void UpdatePropertyDetails(const IProperty::Info& info) = 0;
|
virtual void UpdatePropertyDetails(const IProperty::Info& info) = 0;
|
||||||
|
|
||||||
|
// Update the last updated time for the given property id.
|
||||||
|
virtual void UpdateLastSave(const IProperty::Info& info) = 0;
|
||||||
|
|
||||||
// Update the property performance cost for the given property id.
|
// Update the property performance cost for the given property id.
|
||||||
virtual void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) = 0;
|
virtual void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
NiPoint3 position;
|
NiPoint3 position;
|
||||||
NiQuaternion rotation;
|
NiQuaternion rotation = QuatUtils::IDENTITY;
|
||||||
LWOOBJID id{};
|
LWOOBJID id{};
|
||||||
LOT lot{};
|
LOT lot{};
|
||||||
uint32_t ugcId{};
|
uint32_t ugcId{};
|
||||||
std::array<int32_t, 5> behaviors{};
|
std::array<LWOOBJID, 5> behaviors{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inserts a new UGC model into the database.
|
// Inserts a new UGC model into the database.
|
||||||
virtual void InsertNewUgcModel(
|
virtual void InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data,
|
std::stringstream& sd0Data,
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) = 0;
|
const uint32_t characterId) = 0;
|
||||||
@@ -34,9 +34,17 @@ public:
|
|||||||
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
|
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.
|
// Update the model position and rotation for the given property id.
|
||||||
virtual void UpdateModel(const LWOOBJID& propertyId, 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<std::pair<LWOOBJID, std::string>, 5>& behaviors) = 0;
|
||||||
|
virtual void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<LWOOBJID, 5> behaviorIDs) {
|
||||||
|
std::array<std::pair<LWOOBJID, std::string>, 5> behaviors;
|
||||||
|
for (int32_t i = 0; i < behaviors.size(); i++) behaviors[i].first = behaviorIDs[i];
|
||||||
|
UpdateModel(modelID, position, rotation, behaviors);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the model for the given property id.
|
// Remove the model for the given property id.
|
||||||
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
||||||
|
|
||||||
|
// Gets a model by ID
|
||||||
|
virtual Model GetModel(const LWOOBJID modelID) = 0;
|
||||||
};
|
};
|
||||||
#endif //!__IPROPERTIESCONTENTS__H__
|
#endif //!__IPROPERTIESCONTENTS__H__
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ public:
|
|||||||
struct Model {
|
struct Model {
|
||||||
std::stringstream lxfmlData;
|
std::stringstream lxfmlData;
|
||||||
LWOOBJID id{};
|
LWOOBJID id{};
|
||||||
|
LWOOBJID modelID{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gets all UGC models for the given property id.
|
// Gets all UGC models for the given property id.
|
||||||
@@ -27,6 +28,6 @@ public:
|
|||||||
virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0;
|
virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0;
|
||||||
|
|
||||||
// Inserts a new UGC model into the database.
|
// Inserts a new UGC model into the database.
|
||||||
virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) = 0;
|
virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) = 0;
|
||||||
};
|
};
|
||||||
#endif //!__IUGC__H__
|
#endif //!__IUGC__H__
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public:
|
|||||||
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
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 UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
||||||
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
||||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
|
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
|
||||||
std::vector<IUgc::Model> GetAllUgcModels() override;
|
std::vector<IUgc::Model> GetAllUgcModels() override;
|
||||||
void CreateMigrationHistoryTable() override;
|
void CreateMigrationHistoryTable() override;
|
||||||
bool IsMigrationRun(const std::string_view str) override;
|
bool IsMigrationRun(const std::string_view str) override;
|
||||||
@@ -70,18 +70,19 @@ public:
|
|||||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||||
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
||||||
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
||||||
|
void UpdateLastSave(const IProperty::Info& info) override;
|
||||||
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
||||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||||
void RemoveUnreferencedUgcModels() override;
|
void RemoveUnreferencedUgcModels() override;
|
||||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) 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 UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<LWOOBJID, std::string>, 5>& behaviors) override;
|
||||||
void RemoveModel(const LWOOBJID& modelId) override;
|
void RemoveModel(const LWOOBJID& modelId) override;
|
||||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||||
void InsertNewMail(const MailInfo& mail) override;
|
void InsertNewMail(const MailInfo& mail) override;
|
||||||
void InsertNewUgcModel(
|
void InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data,
|
std::stringstream& sd0Data,
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) override;
|
const uint32_t characterId) override;
|
||||||
@@ -109,8 +110,8 @@ public:
|
|||||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||||
void AddBehavior(const IBehaviors::Info& info) override;
|
void AddBehavior(const IBehaviors::Info& info) override;
|
||||||
std::string GetBehavior(const int32_t behaviorId) override;
|
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
||||||
void RemoveBehavior(const int32_t characterId) override;
|
void RemoveBehavior(const LWOOBJID characterId) override;
|
||||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
|
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
|
||||||
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
||||||
@@ -126,6 +127,7 @@ public:
|
|||||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||||
uint32_t GetAccountCount() override;
|
uint32_t GetAccountCount() override;
|
||||||
bool IsNameInUse(const std::string_view name) override;
|
bool IsNameInUse(const std::string_view name) override;
|
||||||
|
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
|
||||||
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
|
|
||||||
std::optional<IAccounts::Info> MySQLDatabase::GetAccountInfo(const std::string_view username) {
|
std::optional<IAccounts::Info> MySQLDatabase::GetAccountInfo(const std::string_view username) {
|
||||||
auto result = ExecuteSelect("SELECT id, password, banned, locked, play_key_id, gm_level FROM accounts WHERE name = ? LIMIT 1;", username);
|
auto result = ExecuteSelect("SELECT id, password, banned, locked, play_key_id, gm_level, mute_expire FROM accounts WHERE name = ? LIMIT 1;", username);
|
||||||
|
|
||||||
if (!result->next()) {
|
if (!result->next()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -16,6 +16,7 @@ std::optional<IAccounts::Info> MySQLDatabase::GetAccountInfo(const std::string_v
|
|||||||
toReturn.banned = result->getBoolean("banned");
|
toReturn.banned = result->getBoolean("banned");
|
||||||
toReturn.locked = result->getBoolean("locked");
|
toReturn.locked = result->getBoolean("locked");
|
||||||
toReturn.playKeyId = result->getUInt("play_key_id");
|
toReturn.playKeyId = result->getUInt("play_key_id");
|
||||||
|
toReturn.muteExpire = result->getUInt64("mute_expire");
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) {
|
void MySQLDatabase::RemoveBehavior(const LWOOBJID behaviorId) {
|
||||||
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) {
|
std::string MySQLDatabase::GetBehavior(const LWOOBJID behaviorId) {
|
||||||
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||||
return result->next() ? result->getString("behavior_info").c_str() : "";
|
return result->next() ? result->getString("behavior_info").c_str() : "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,6 +173,10 @@ void MySQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
|||||||
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ? LIMIT 1;", info.name, info.description, info.id);
|
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ? LIMIT 1;", info.name, info.description, info.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::UpdateLastSave(const IProperty::Info& info) {
|
||||||
|
ExecuteUpdate("UPDATE properties SET last_updated = ? WHERE id = ?;", info.lastUpdatedTime, info.id);
|
||||||
|
}
|
||||||
|
|
||||||
void MySQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
void MySQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
||||||
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ? LIMIT 1;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ? LIMIT 1;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWO
|
|||||||
model.rotation.y = result->getFloat("ry");
|
model.rotation.y = result->getFloat("ry");
|
||||||
model.rotation.z = result->getFloat("rz");
|
model.rotation.z = result->getFloat("rz");
|
||||||
model.ugcId = result->getUInt64("ugc_id");
|
model.ugcId = result->getUInt64("ugc_id");
|
||||||
model.behaviors[0] = result->getInt("behavior_1");
|
model.behaviors[0] = result->getUInt64("behavior_1");
|
||||||
model.behaviors[1] = result->getInt("behavior_2");
|
model.behaviors[1] = result->getUInt64("behavior_2");
|
||||||
model.behaviors[2] = result->getInt("behavior_3");
|
model.behaviors[2] = result->getUInt64("behavior_3");
|
||||||
model.behaviors[3] = result->getInt("behavior_4");
|
model.behaviors[3] = result->getUInt64("behavior_4");
|
||||||
model.behaviors[4] = result->getInt("behavior_5");
|
model.behaviors[4] = result->getUInt64("behavior_5");
|
||||||
|
|
||||||
toReturn.push_back(std::move(model));
|
toReturn.push_back(std::move(model));
|
||||||
}
|
}
|
||||||
@@ -52,14 +52,39 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
void MySQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<LWOOBJID, std::string>, 5>& behaviors) {
|
||||||
ExecuteUpdate(
|
ExecuteUpdate(
|
||||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||||
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
"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,
|
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, propertyId);
|
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, modelID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||||
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", 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->getUInt64("behavior_1");
|
||||||
|
model.behaviors[1] = result->getUInt64("behavior_2");
|
||||||
|
model.behaviors[2] = result->getUInt64("behavior_3");
|
||||||
|
model.behaviors[3] = result->getUInt64("behavior_4");
|
||||||
|
model.behaviors[4] = result->getUInt64("behavior_5");
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
|
std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
|
||||||
auto result = ExecuteSelect(
|
auto result = ExecuteSelect(
|
||||||
"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;",
|
"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;",
|
||||||
propertyId);
|
propertyId);
|
||||||
|
|
||||||
std::vector<IUgc::Model> toReturn;
|
std::vector<IUgc::Model> toReturn;
|
||||||
@@ -13,7 +13,8 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
|
|||||||
// blob is owned by the query, so we need to do a deep copy :/
|
// blob is owned by the query, so we need to do a deep copy :/
|
||||||
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
|
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
|
||||||
model.lxfmlData << blob->rdbuf();
|
model.lxfmlData << blob->rdbuf();
|
||||||
model.id = result->getUInt64("id");
|
model.id = result->getUInt64("ugcID");
|
||||||
|
model.modelID = result->getUInt64("modelID");
|
||||||
toReturn.push_back(std::move(model));
|
toReturn.push_back(std::move(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,13 +22,14 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
|
std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
|
||||||
auto result = ExecuteSelect("SELECT id, lxfml FROM ugc;");
|
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;");
|
||||||
|
|
||||||
std::vector<IUgc::Model> models;
|
std::vector<IUgc::Model> models;
|
||||||
models.reserve(result->rowsCount());
|
models.reserve(result->rowsCount());
|
||||||
while (result->next()) {
|
while (result->next()) {
|
||||||
IUgc::Model model;
|
IUgc::Model model;
|
||||||
model.id = result->getInt64("id");
|
model.id = result->getInt64("ugcID");
|
||||||
|
model.modelID = result->getUInt64("modelID");
|
||||||
|
|
||||||
// blob is owned by the query, so we need to do a deep copy :/
|
// blob is owned by the query, so we need to do a deep copy :/
|
||||||
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
|
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
|
||||||
@@ -43,7 +45,7 @@ void MySQLDatabase::RemoveUnreferencedUgcModels() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MySQLDatabase::InsertNewUgcModel(
|
void MySQLDatabase::InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data, // cant be const sad
|
std:: stringstream& sd0Data, // cant be const sad
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) {
|
const uint32_t characterId) {
|
||||||
@@ -65,7 +67,7 @@ void MySQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
|
|||||||
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
|
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) {
|
void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
|
||||||
const std::istream stream(lxfml.rdbuf());
|
const std::istream stream(lxfml.rdbuf());
|
||||||
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
|
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 RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
||||||
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
||||||
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
||||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
|
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
|
||||||
std::vector<IUgc::Model> GetAllUgcModels() override;
|
std::vector<IUgc::Model> GetAllUgcModels() override;
|
||||||
void CreateMigrationHistoryTable() override;
|
void CreateMigrationHistoryTable() override;
|
||||||
bool IsMigrationRun(const std::string_view str) override;
|
bool IsMigrationRun(const std::string_view str) override;
|
||||||
@@ -68,18 +68,19 @@ public:
|
|||||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||||
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
||||||
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
||||||
|
void UpdateLastSave(const IProperty::Info& info) override;
|
||||||
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
||||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||||
void RemoveUnreferencedUgcModels() override;
|
void RemoveUnreferencedUgcModels() override;
|
||||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) 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 UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<LWOOBJID, std::string>, 5>& behaviors) override;
|
||||||
void RemoveModel(const LWOOBJID& modelId) override;
|
void RemoveModel(const LWOOBJID& modelId) override;
|
||||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||||
void InsertNewMail(const MailInfo& mail) override;
|
void InsertNewMail(const MailInfo& mail) override;
|
||||||
void InsertNewUgcModel(
|
void InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data,
|
std::stringstream& sd0Data,
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) override;
|
const uint32_t characterId) override;
|
||||||
@@ -107,8 +108,8 @@ public:
|
|||||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||||
void AddBehavior(const IBehaviors::Info& info) override;
|
void AddBehavior(const IBehaviors::Info& info) override;
|
||||||
std::string GetBehavior(const int32_t behaviorId) override;
|
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
||||||
void RemoveBehavior(const int32_t characterId) override;
|
void RemoveBehavior(const LWOOBJID characterId) override;
|
||||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
|
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
|
||||||
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
||||||
@@ -124,6 +125,7 @@ public:
|
|||||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||||
uint32_t GetAccountCount() override;
|
uint32_t GetAccountCount() override;
|
||||||
bool IsNameInUse(const std::string_view name) override;
|
bool IsNameInUse(const std::string_view name) override;
|
||||||
|
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
|
||||||
private:
|
private:
|
||||||
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
|
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ std::optional<IAccounts::Info> SQLiteDatabase::GetAccountInfo(const std::string_
|
|||||||
toReturn.banned = result.getIntField("banned");
|
toReturn.banned = result.getIntField("banned");
|
||||||
toReturn.locked = result.getIntField("locked");
|
toReturn.locked = result.getIntField("locked");
|
||||||
toReturn.playKeyId = result.getIntField("play_key_id");
|
toReturn.playKeyId = result.getIntField("play_key_id");
|
||||||
|
toReturn.muteExpire = static_cast<uint64_t>(result.getInt64Field("mute_expire"));
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ void SQLiteDatabase::AddBehavior(const IBehaviors::Info& info) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteDatabase::RemoveBehavior(const int32_t behaviorId) {
|
void SQLiteDatabase::RemoveBehavior(const LWOOBJID behaviorId) {
|
||||||
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SQLiteDatabase::GetBehavior(const int32_t behaviorId) {
|
std::string SQLiteDatabase::GetBehavior(const LWOOBJID behaviorId) {
|
||||||
auto [_, result] = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
auto [_, result] = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||||
return !result.eof() ? result.getStringField("behavior_info") : "";
|
return !result.eof() ? result.getStringField("behavior_info") : "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,6 +175,10 @@ void SQLiteDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
|||||||
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ?;", info.name, info.description, info.id);
|
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ?;", info.name, info.description, info.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SQLiteDatabase::UpdateLastSave(const IProperty::Info& info) {
|
||||||
|
ExecuteUpdate("UPDATE properties SET last_updated = ? WHERE id = ?;", info.lastUpdatedTime, info.id);
|
||||||
|
}
|
||||||
|
|
||||||
void SQLiteDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
void SQLiteDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
||||||
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ?;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ?;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ std::vector<IPropertyContents::Model> SQLiteDatabase::GetPropertyModels(const LW
|
|||||||
model.rotation.y = result.getFloatField("ry");
|
model.rotation.y = result.getFloatField("ry");
|
||||||
model.rotation.z = result.getFloatField("rz");
|
model.rotation.z = result.getFloatField("rz");
|
||||||
model.ugcId = result.getInt64Field("ugc_id");
|
model.ugcId = result.getInt64Field("ugc_id");
|
||||||
model.behaviors[0] = result.getIntField("behavior_1");
|
model.behaviors[0] = result.getInt64Field("behavior_1");
|
||||||
model.behaviors[1] = result.getIntField("behavior_2");
|
model.behaviors[1] = result.getInt64Field("behavior_2");
|
||||||
model.behaviors[2] = result.getIntField("behavior_3");
|
model.behaviors[2] = result.getInt64Field("behavior_3");
|
||||||
model.behaviors[3] = result.getIntField("behavior_4");
|
model.behaviors[3] = result.getInt64Field("behavior_4");
|
||||||
model.behaviors[4] = result.getIntField("behavior_5");
|
model.behaviors[4] = result.getInt64Field("behavior_5");
|
||||||
|
|
||||||
toReturn.push_back(std::move(model));
|
toReturn.push_back(std::move(model));
|
||||||
result.nextRow();
|
result.nextRow();
|
||||||
@@ -52,14 +52,41 @@ void SQLiteDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
void SQLiteDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<LWOOBJID, std::string>, 5>& behaviors) {
|
||||||
ExecuteUpdate(
|
ExecuteUpdate(
|
||||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||||
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
"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,
|
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, propertyId);
|
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, modelID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) {
|
void SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||||
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", 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.getInt64Field("behavior_1");
|
||||||
|
model.behaviors[1] = result.getInt64Field("behavior_2");
|
||||||
|
model.behaviors[2] = result.getInt64Field("behavior_3");
|
||||||
|
model.behaviors[3] = result.getInt64Field("behavior_4");
|
||||||
|
model.behaviors[4] = result.getInt64Field("behavior_5");
|
||||||
|
} while (result.nextRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) {
|
std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) {
|
||||||
auto [_, result] = ExecuteSelect(
|
auto [_, result] = ExecuteSelect(
|
||||||
"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;",
|
"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;",
|
||||||
propertyId);
|
propertyId);
|
||||||
|
|
||||||
std::vector<IUgc::Model> toReturn;
|
std::vector<IUgc::Model> toReturn;
|
||||||
@@ -13,7 +13,8 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
|
|||||||
int blobSize{};
|
int blobSize{};
|
||||||
const auto* blob = result.getBlobField("lxfml", blobSize);
|
const auto* blob = result.getBlobField("lxfml", blobSize);
|
||||||
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
|
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
|
||||||
model.id = result.getInt64Field("id");
|
model.id = result.getInt64Field("ugcID");
|
||||||
|
model.modelID = result.getInt64Field("modelID");
|
||||||
toReturn.push_back(std::move(model));
|
toReturn.push_back(std::move(model));
|
||||||
result.nextRow();
|
result.nextRow();
|
||||||
}
|
}
|
||||||
@@ -22,12 +23,13 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() {
|
std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() {
|
||||||
auto [_, result] = ExecuteSelect("SELECT id, lxfml FROM ugc;");
|
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;");
|
||||||
|
|
||||||
std::vector<IUgc::Model> models;
|
std::vector<IUgc::Model> models;
|
||||||
while (!result.eof()) {
|
while (!result.eof()) {
|
||||||
IUgc::Model model;
|
IUgc::Model model;
|
||||||
model.id = result.getInt64Field("id");
|
model.id = result.getInt64Field("ugcID");
|
||||||
|
model.modelID = result.getInt64Field("modelID");
|
||||||
|
|
||||||
int blobSize{};
|
int blobSize{};
|
||||||
const auto* blob = result.getBlobField("lxfml", blobSize);
|
const auto* blob = result.getBlobField("lxfml", blobSize);
|
||||||
@@ -44,7 +46,7 @@ void SQLiteDatabase::RemoveUnreferencedUgcModels() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteDatabase::InsertNewUgcModel(
|
void SQLiteDatabase::InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data, // cant be const sad
|
std::stringstream& sd0Data, // cant be const sad
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) {
|
const uint32_t characterId) {
|
||||||
@@ -66,7 +68,7 @@ void SQLiteDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
|
|||||||
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
|
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) {
|
void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
|
||||||
const std::istream stream(lxfml.rdbuf());
|
const std::istream stream(lxfml.rdbuf());
|
||||||
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
|
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::istringstream& lxfml) {
|
void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +148,10 @@ void TestSQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestSQLDatabase::UpdateLastSave(const IProperty::Info& info) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void TestSQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) {
|
void TestSQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -164,7 +168,7 @@ void TestSQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const I
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
void TestSQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<LWOOBJID, std::string>, 5>& behaviors) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +192,7 @@ void TestSQLDatabase::InsertNewMail(const MailInfo& mail) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) {
|
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,11 +292,11 @@ void TestSQLDatabase::AddBehavior(const IBehaviors::Info& info) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TestSQLDatabase::GetBehavior(const int32_t behaviorId) {
|
std::string TestSQLDatabase::GetBehavior(const LWOOBJID behaviorId) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSQLDatabase::RemoveBehavior(const int32_t behaviorId) {
|
void TestSQLDatabase::RemoveBehavior(const LWOOBJID behaviorId) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
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 UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
||||||
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
||||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
|
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
|
||||||
std::vector<IUgc::Model> GetAllUgcModels() override;
|
std::vector<IUgc::Model> GetAllUgcModels() override;
|
||||||
void CreateMigrationHistoryTable() override;
|
void CreateMigrationHistoryTable() override;
|
||||||
bool IsMigrationRun(const std::string_view str) override;
|
bool IsMigrationRun(const std::string_view str) override;
|
||||||
@@ -47,18 +47,19 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||||
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
||||||
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
||||||
|
void UpdateLastSave(const IProperty::Info& info) override;
|
||||||
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
||||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||||
void RemoveUnreferencedUgcModels() override;
|
void RemoveUnreferencedUgcModels() override;
|
||||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) 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 UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<LWOOBJID, std::string>, 5>& behaviors) override;
|
||||||
void RemoveModel(const LWOOBJID& modelId) override;
|
void RemoveModel(const LWOOBJID& modelId) override;
|
||||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||||
void InsertNewMail(const MailInfo& mail) override;
|
void InsertNewMail(const MailInfo& mail) override;
|
||||||
void InsertNewUgcModel(
|
void InsertNewUgcModel(
|
||||||
std::istringstream& sd0Data,
|
std::stringstream& sd0Data,
|
||||||
const uint32_t blueprintId,
|
const uint32_t blueprintId,
|
||||||
const uint32_t accountId,
|
const uint32_t accountId,
|
||||||
const uint32_t characterId) override;
|
const uint32_t characterId) override;
|
||||||
@@ -86,8 +87,8 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||||
void AddBehavior(const IBehaviors::Info& info) override;
|
void AddBehavior(const IBehaviors::Info& info) override;
|
||||||
std::string GetBehavior(const int32_t behaviorId) override;
|
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
||||||
void RemoveBehavior(const int32_t behaviorId) override;
|
void RemoveBehavior(const LWOOBJID behaviorId) override;
|
||||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; };
|
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; };
|
||||||
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override { return {}; };
|
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override { return {}; };
|
||||||
@@ -104,6 +105,7 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
uint32_t GetAccountCount() override { return 0; };
|
uint32_t GetAccountCount() override { return 0; };
|
||||||
|
|
||||||
bool IsNameInUse(const std::string_view name) override { return false; };
|
bool IsNameInUse(const std::string_view name) override { return false; };
|
||||||
|
IPropertyContents::Model GetModel(const LWOOBJID modelID) override { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!TESTSQLDATABASE_H
|
#endif //!TESTSQLDATABASE_H
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "BinaryPathFinder.h"
|
#include "BinaryPathFinder.h"
|
||||||
|
#include "ModelNormalizeMigration.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ void MigrationRunner::RunMigrations() {
|
|||||||
Database::Get()->CreateMigrationHistoryTable();
|
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.
|
// 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();
|
const auto migrationFolder = Database::GetMigrationFolder();
|
||||||
if (!Database::Get()->IsMigrationRun("17_migration_for_migrations.sql") && migrationFolder == "mysql") {
|
if (!Database::Get()->IsMigrationRun("17_migration_for_migrations.sql") && migrationFolder == "mysql") {
|
||||||
LOG("Running migration: 17_migration_for_migrations.sql");
|
LOG("Running migration: 17_migration_for_migrations.sql");
|
||||||
@@ -45,6 +46,9 @@ void MigrationRunner::RunMigrations() {
|
|||||||
|
|
||||||
std::string finalSQL = "";
|
std::string finalSQL = "";
|
||||||
bool runSd0Migrations = false;
|
bool runSd0Migrations = false;
|
||||||
|
bool runNormalizeMigrations = false;
|
||||||
|
bool runNormalizeAfterFirstPartMigrations = false;
|
||||||
|
bool runBrickBuildsNotOnGrid = false;
|
||||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
|
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
|
||||||
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
|
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
|
||||||
|
|
||||||
@@ -57,6 +61,12 @@ void MigrationRunner::RunMigrations() {
|
|||||||
LOG("Running migration: %s", migration.name.c_str());
|
LOG("Running migration: %s", migration.name.c_str());
|
||||||
if (migration.name == "5_brick_model_sd0.sql") {
|
if (migration.name == "5_brick_model_sd0.sql") {
|
||||||
runSd0Migrations = true;
|
runSd0Migrations = true;
|
||||||
|
} else if (migration.name.ends_with("_normalize_model_positions.sql")) {
|
||||||
|
runNormalizeMigrations = true;
|
||||||
|
} else if (migration.name.ends_with("_normalize_model_positions_after_first_part.sql")) {
|
||||||
|
runNormalizeAfterFirstPartMigrations = true;
|
||||||
|
} else if (migration.name.ends_with("_brickbuilds_not_on_grid.sql")) {
|
||||||
|
runBrickBuildsNotOnGrid = true;
|
||||||
} else {
|
} else {
|
||||||
finalSQL.append(migration.data.c_str());
|
finalSQL.append(migration.data.c_str());
|
||||||
}
|
}
|
||||||
@@ -64,7 +74,7 @@ void MigrationRunner::RunMigrations() {
|
|||||||
Database::Get()->InsertMigration(migration.name);
|
Database::Get()->InsertMigration(migration.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finalSQL.empty() && !runSd0Migrations) {
|
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations && !runBrickBuildsNotOnGrid) {
|
||||||
LOG("Server database is up to date.");
|
LOG("Server database is up to date.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -88,6 +98,18 @@ void MigrationRunner::RunMigrations() {
|
|||||||
uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml();
|
uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml();
|
||||||
LOG("%i models were truncated from the database.", numberOfTruncatedModels);
|
LOG("%i models were truncated from the database.", numberOfTruncatedModels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runNormalizeMigrations) {
|
||||||
|
ModelNormalizeMigration::Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runNormalizeAfterFirstPartMigrations) {
|
||||||
|
ModelNormalizeMigration::RunAfterFirstPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runBrickBuildsNotOnGrid) {
|
||||||
|
ModelNormalizeMigration::RunBrickBuildGrid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MigrationRunner::RunSQLiteMigrations() {
|
void MigrationRunner::RunSQLiteMigrations() {
|
||||||
|
|||||||
71
dDatabase/ModelNormalizeMigration.cpp
Normal file
71
dDatabase/ModelNormalizeMigration.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#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::NormalizePositionOnlyFirstPart(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelNormalizeMigration::RunAfterFirstPart() {
|
||||||
|
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) need to have their position fixed from the above blunder
|
||||||
|
if (model.lot != 14) continue;
|
||||||
|
|
||||||
|
Sd0 sd0(lxfmlData);
|
||||||
|
const auto asStr = sd0.GetAsStringUncompressed();
|
||||||
|
const auto [newLxfml, newCenter] = Lxfml::NormalizePositionAfterFirstPart(asStr, model.position);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelNormalizeMigration::RunBrickBuildGrid() {
|
||||||
|
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) need to have their position fixed from the above blunder
|
||||||
|
if (model.lot != 14) continue;
|
||||||
|
|
||||||
|
Sd0 sd0(lxfmlData);
|
||||||
|
const auto asStr = sd0.GetAsStringUncompressed();
|
||||||
|
const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr, model.position);
|
||||||
|
|
||||||
|
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
|
||||||
|
LOG("Updated model %llu to have a center of %f %f %f", modelID, newCenter.x, newCenter.y, newCenter.z);
|
||||||
|
auto asStream = sd0.GetAsStream();
|
||||||
|
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
|
||||||
|
Database::Get()->UpdateUgcModelData(id, asStream);
|
||||||
|
}
|
||||||
|
Database::Get()->SetAutoCommit(oldCommit);
|
||||||
|
}
|
||||||
13
dDatabase/ModelNormalizeMigration.h
Normal file
13
dDatabase/ModelNormalizeMigration.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Darkflame Universe
|
||||||
|
// Copyright 2025
|
||||||
|
|
||||||
|
#ifndef MODELNORMALIZEMIGRATION_H
|
||||||
|
#define MODELNORMALIZEMIGRATION_H
|
||||||
|
|
||||||
|
namespace ModelNormalizeMigration {
|
||||||
|
void Run();
|
||||||
|
void RunAfterFirstPart();
|
||||||
|
void RunBrickBuildGrid();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!MODELNORMALIZEMIGRATION_H
|
||||||
@@ -204,6 +204,7 @@ void Character::DoQuickXMLDataParse() {
|
|||||||
while (currentChild) {
|
while (currentChild) {
|
||||||
const auto* temp = currentChild->Attribute("v");
|
const auto* temp = currentChild->Attribute("v");
|
||||||
const auto* id = currentChild->Attribute("id");
|
const auto* id = currentChild->Attribute("id");
|
||||||
|
const auto* si = currentChild->Attribute("si");
|
||||||
if (temp && id) {
|
if (temp && id) {
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
uint64_t value = 0;
|
uint64_t value = 0;
|
||||||
@@ -212,6 +213,9 @@ void Character::DoQuickXMLDataParse() {
|
|||||||
value = std::stoull(temp);
|
value = std::stoull(temp);
|
||||||
|
|
||||||
m_PlayerFlags.insert(std::make_pair(index, value));
|
m_PlayerFlags.insert(std::make_pair(index, value));
|
||||||
|
} else if (si) {
|
||||||
|
auto value = GeneralUtils::TryParse<uint32_t>(si);
|
||||||
|
if (value) m_SessionFlags.insert(value.value());
|
||||||
}
|
}
|
||||||
currentChild = currentChild->NextSiblingElement();
|
currentChild = currentChild->NextSiblingElement();
|
||||||
}
|
}
|
||||||
@@ -492,7 +496,7 @@ void Character::OnZoneLoad() {
|
|||||||
|
|
||||||
// Remove all GM items
|
// Remove all GM items
|
||||||
for (const auto lot : Inventory::GetAllGMItems()) {
|
for (const auto lot : Inventory::GetAllGMItems()) {
|
||||||
inventoryComponent->RemoveItem(lot, inventoryComponent->GetLotCount(lot));
|
inventoryComponent->RemoveItem(lot, inventoryComponent->GetLotCount(lot), eInventoryType::ALL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -654,7 +654,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* The spawn rotation of this character when loading in
|
* The spawn rotation of this character when loading in
|
||||||
*/
|
*/
|
||||||
NiQuaternion m_OriginalRotation;
|
NiQuaternion m_OriginalRotation = QuatUtils::IDENTITY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The respawn points of this character, per world
|
* The respawn points of this character, per world
|
||||||
|
|||||||
614
dGame/Entity.cpp
614
dGame/Entity.cpp
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -34,7 +35,6 @@ namespace tinyxml2 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Player;
|
class Player;
|
||||||
class EntityInfo;
|
|
||||||
class User;
|
class User;
|
||||||
class Spawner;
|
class Spawner;
|
||||||
class ScriptComponent;
|
class ScriptComponent;
|
||||||
@@ -45,6 +45,7 @@ class Item;
|
|||||||
class Character;
|
class Character;
|
||||||
class EntityCallbackTimer;
|
class EntityCallbackTimer;
|
||||||
class PositionUpdate;
|
class PositionUpdate;
|
||||||
|
struct EntityInfo;
|
||||||
enum class eTriggerEventType;
|
enum class eTriggerEventType;
|
||||||
enum class eGameMasterLevel : uint8_t;
|
enum class eGameMasterLevel : uint8_t;
|
||||||
enum class eReplicaComponentType : uint32_t;
|
enum class eReplicaComponentType : uint32_t;
|
||||||
@@ -60,7 +61,7 @@ namespace CppScripts {
|
|||||||
*/
|
*/
|
||||||
class Entity {
|
class Entity {
|
||||||
public:
|
public:
|
||||||
explicit Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser = nullptr, Entity* parentEntity = nullptr);
|
Entity(const LWOOBJID& objectID, const EntityInfo& info, User* parentUser = nullptr, Entity* parentEntity = nullptr);
|
||||||
~Entity();
|
~Entity();
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
@@ -113,7 +114,7 @@ public:
|
|||||||
|
|
||||||
float GetDefaultScale() const;
|
float GetDefaultScale() const;
|
||||||
|
|
||||||
const NiPoint3& GetPosition() const;
|
NiPoint3 GetPosition() const;
|
||||||
|
|
||||||
const NiQuaternion& GetRotation() const;
|
const NiQuaternion& GetRotation() const;
|
||||||
|
|
||||||
@@ -124,6 +125,8 @@ public:
|
|||||||
// then return the collision group from that.
|
// then return the collision group from that.
|
||||||
int32_t GetCollisionGroup() const;
|
int32_t GetCollisionGroup() const;
|
||||||
|
|
||||||
|
const NiPoint3& GetVelocity() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setters
|
* Setters
|
||||||
*/
|
*/
|
||||||
@@ -144,9 +147,11 @@ public:
|
|||||||
|
|
||||||
void SetRotation(const NiQuaternion& rotation);
|
void SetRotation(const NiQuaternion& rotation);
|
||||||
|
|
||||||
void SetRespawnPos(const NiPoint3& position);
|
void SetRespawnPos(const NiPoint3& position) const;
|
||||||
|
|
||||||
void SetRespawnRot(const NiQuaternion& rotation);
|
void SetRespawnRot(const NiQuaternion& rotation) const;
|
||||||
|
|
||||||
|
void SetVelocity(const NiPoint3& velocity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component management
|
* Component management
|
||||||
@@ -157,6 +162,12 @@ public:
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
T* GetComponent() const;
|
T* GetComponent() const;
|
||||||
|
|
||||||
|
template<typename... T>
|
||||||
|
auto GetComponents() const;
|
||||||
|
|
||||||
|
template<typename... T>
|
||||||
|
auto GetComponentsMut() const;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool TryGetComponent(eReplicaComponentType componentId, T*& component) const;
|
bool TryGetComponent(eReplicaComponentType componentId, T*& component) const;
|
||||||
|
|
||||||
@@ -164,8 +175,10 @@ public:
|
|||||||
|
|
||||||
void AddComponent(eReplicaComponentType componentId, Component* component);
|
void AddComponent(eReplicaComponentType componentId, Component* component);
|
||||||
|
|
||||||
|
bool MsgRequestServerObjectInfo(GameMessages::GameMsg& msg);
|
||||||
|
|
||||||
// This is expceted to never return nullptr, an assert checks this.
|
// This is expceted to never return nullptr, an assert checks this.
|
||||||
CppScripts::Script* const GetScript();
|
CppScripts::Script* const GetScript() const;
|
||||||
|
|
||||||
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
|
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
|
||||||
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
|
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
|
||||||
@@ -178,8 +191,8 @@ public:
|
|||||||
void RemoveParent();
|
void RemoveParent();
|
||||||
|
|
||||||
// Adds a timer to start next frame with the given name and time.
|
// Adds a timer to start next frame with the given name and time.
|
||||||
void AddTimer(std::string name, float time);
|
void AddTimer(const std::string& name, float time);
|
||||||
void AddCallbackTimer(float time, std::function<void()> callback);
|
void AddCallbackTimer(float time, const std::function<void()> callback);
|
||||||
bool HasTimer(const std::string& name);
|
bool HasTimer(const std::string& name);
|
||||||
void CancelCallbackTimers();
|
void CancelCallbackTimers();
|
||||||
void CancelAllTimers();
|
void CancelAllTimers();
|
||||||
@@ -191,7 +204,7 @@ public:
|
|||||||
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
|
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
|
||||||
|
|
||||||
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
|
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
|
||||||
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
|
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType) const;
|
||||||
void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
|
void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
|
||||||
void Update(float deltaTime);
|
void Update(float deltaTime);
|
||||||
|
|
||||||
@@ -238,21 +251,21 @@ public:
|
|||||||
void AddDieCallback(const std::function<void()>& callback);
|
void AddDieCallback(const std::function<void()>& callback);
|
||||||
void Resurrect();
|
void Resurrect();
|
||||||
|
|
||||||
void AddLootItem(const Loot::Info& info);
|
void AddLootItem(const Loot::Info& info) const;
|
||||||
void PickupItem(const LWOOBJID& objectID);
|
void PickupItem(const LWOOBJID& objectID) const;
|
||||||
|
|
||||||
bool CanPickupCoins(uint64_t count);
|
bool PickupCoins(uint64_t count) const;
|
||||||
void RegisterCoinDrop(uint64_t count);
|
void RegisterCoinDrop(uint64_t count) const;
|
||||||
|
|
||||||
void ScheduleKillAfterUpdate(Entity* murderer = nullptr);
|
void ScheduleKillAfterUpdate(Entity* murderer = nullptr);
|
||||||
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr);
|
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr) const;
|
||||||
void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; }
|
void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; }
|
||||||
|
|
||||||
const NiPoint3& GetRespawnPosition() const;
|
const NiPoint3& GetRespawnPosition() const;
|
||||||
const NiQuaternion& GetRespawnRotation() const;
|
const NiQuaternion& GetRespawnRotation() const;
|
||||||
|
|
||||||
void Sleep();
|
void Sleep() const;
|
||||||
void Wake();
|
void Wake() const;
|
||||||
bool IsSleeping() const;
|
bool IsSleeping() const;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -262,7 +275,7 @@ public:
|
|||||||
* Retroactively corrects the model vault size due to incorrect initialization in a previous patch.
|
* Retroactively corrects the model vault size due to incorrect initialization in a previous patch.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void RetroactiveVaultSize();
|
void RetroactiveVaultSize() const;
|
||||||
bool GetBoolean(const std::u16string& name) const;
|
bool GetBoolean(const std::u16string& name) const;
|
||||||
int32_t GetI32(const std::u16string& name) const;
|
int32_t GetI32(const std::u16string& name) const;
|
||||||
int64_t GetI64(const std::u16string& name) const;
|
int64_t GetI64(const std::u16string& name) const;
|
||||||
@@ -325,12 +338,17 @@ public:
|
|||||||
|
|
||||||
bool HandleMsg(GameMessages::GameMsg& msg) const;
|
bool HandleMsg(GameMessages::GameMsg& msg) const;
|
||||||
|
|
||||||
|
void RegisterMsg(const MessageType::Game msgId, auto* self, const auto handler) {
|
||||||
|
RegisterMsg(msgId, std::bind(handler, self, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The observable for player entity position updates.
|
* @brief The observable for player entity position updates.
|
||||||
*/
|
*/
|
||||||
static Observable<Entity*, const PositionUpdate&> OnPlayerPositionUpdate;
|
static Observable<Entity*, const PositionUpdate&> OnPlayerPositionUpdate;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
void WriteLDFData(const std::vector<LDFBaseData*>& ldf, RakNet::BitStream& outBitStream) const;
|
||||||
LWOOBJID m_ObjectID;
|
LWOOBJID m_ObjectID;
|
||||||
|
|
||||||
LOT m_TemplateID;
|
LOT m_TemplateID;
|
||||||
@@ -339,7 +357,7 @@ protected:
|
|||||||
std::vector<LDFBaseData*> m_NetworkSettings;
|
std::vector<LDFBaseData*> m_NetworkSettings;
|
||||||
|
|
||||||
NiPoint3 m_DefaultPosition;
|
NiPoint3 m_DefaultPosition;
|
||||||
NiQuaternion m_DefaultRotation;
|
NiQuaternion m_DefaultRotation = QuatUtils::IDENTITY;
|
||||||
float m_Scale;
|
float m_Scale;
|
||||||
|
|
||||||
Spawner* m_Spawner;
|
Spawner* m_Spawner;
|
||||||
@@ -353,7 +371,6 @@ protected:
|
|||||||
Entity* m_ParentEntity; //For spawners and the like
|
Entity* m_ParentEntity; //For spawners and the like
|
||||||
std::vector<Entity*> m_ChildEntities;
|
std::vector<Entity*> m_ChildEntities;
|
||||||
eGameMasterLevel m_GMLevel;
|
eGameMasterLevel m_GMLevel;
|
||||||
uint16_t m_CollectibleID;
|
|
||||||
std::vector<std::string> m_Groups;
|
std::vector<std::string> m_Groups;
|
||||||
uint16_t m_NetworkID;
|
uint16_t m_NetworkID;
|
||||||
std::vector<std::function<void()>> m_DieCallbacks;
|
std::vector<std::function<void()>> m_DieCallbacks;
|
||||||
@@ -379,6 +396,8 @@ protected:
|
|||||||
|
|
||||||
bool m_IsParentChildDirty = true;
|
bool m_IsParentChildDirty = true;
|
||||||
|
|
||||||
|
bool m_IsSleeping = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Collision
|
* Collision
|
||||||
*/
|
*/
|
||||||
@@ -387,7 +406,7 @@ protected:
|
|||||||
// objectID of receiver and map of notification name to script
|
// objectID of receiver and map of notification name to script
|
||||||
std::map<LWOOBJID, std::map<std::string, CppScripts::Script*>> m_Subscriptions;
|
std::map<LWOOBJID, std::map<std::string, CppScripts::Script*>> m_Subscriptions;
|
||||||
|
|
||||||
std::multimap<MessageType::Game, std::function<bool(GameMessages::GameMsg&)>> m_MsgHandlers;
|
std::unordered_multimap<MessageType::Game, std::function<bool(GameMessages::GameMsg&)>> m_MsgHandlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -435,7 +454,7 @@ const T& Entity::GetVar(const std::u16string& name) const {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
T Entity::GetVarAs(const std::u16string& name) const {
|
T Entity::GetVarAs(const std::u16string& name) const {
|
||||||
const auto data = GetVarAsString(name);
|
const auto data = GetVarAsString(name);
|
||||||
|
|
||||||
return GeneralUtils::TryParse<T>(data).value_or(LDFData<T>::Default);
|
return GeneralUtils::TryParse<T>(data).value_or(LDFData<T>::Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,3 +592,13 @@ inline ComponentType* Entity::AddComponent(VaArgs... args) {
|
|||||||
// To allow a static cast here instead of a dynamic one.
|
// To allow a static cast here instead of a dynamic one.
|
||||||
return dynamic_cast<ComponentType*>(componentToReturn);
|
return dynamic_cast<ComponentType*>(componentToReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename... T>
|
||||||
|
auto Entity::GetComponents() const {
|
||||||
|
return GetComponentsMut<const T...>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... T>
|
||||||
|
auto Entity::GetComponentsMut() const {
|
||||||
|
return std::tuple{GetComponent<T>()...};
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exclude the zone control object from any flags
|
// Exclude the zone control object from any flags
|
||||||
if (!controller && info.lot != 14) {
|
if (!controller) {
|
||||||
|
|
||||||
// The client flags means the client should render the entity
|
// The client flags means the client should render the entity
|
||||||
GeneralUtils::SetBit(id, eObjectBits::CLIENT);
|
GeneralUtils::SetBit(id, eObjectBits::CLIENT);
|
||||||
@@ -129,6 +129,8 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
|
|||||||
// Set the zone control entity if the entity is a zone control object, this should only happen once
|
// Set the zone control entity if the entity is a zone control object, this should only happen once
|
||||||
if (controller) {
|
if (controller) {
|
||||||
m_ZoneControlEntity = entity;
|
m_ZoneControlEntity = entity;
|
||||||
|
// Proooooobably shouldn't ghost zoneControl
|
||||||
|
m_ZoneControlEntity->SetIsGhostingCandidate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this entity is a respawn point, if so add it to the registry
|
// Check if this entity is a respawn point, if so add it to the registry
|
||||||
@@ -279,6 +281,8 @@ std::vector<Entity*> EntityManager::GetEntitiesByComponent(const eReplicaCompone
|
|||||||
|
|
||||||
withComp.push_back(entity);
|
withComp.push_back(entity);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (auto* const entity : m_Entities | std::views::values) withComp.push_back(entity);
|
||||||
}
|
}
|
||||||
return withComp;
|
return withComp;
|
||||||
}
|
}
|
||||||
@@ -320,7 +324,7 @@ const std::unordered_map<std::string, LWOOBJID>& EntityManager::GetSpawnPointEnt
|
|||||||
return m_SpawnPoints;
|
return m_SpawnPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr, const bool skipChecks) {
|
void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr) {
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
LOG("Attempted to construct null entity");
|
LOG("Attempted to construct null entity");
|
||||||
return;
|
return;
|
||||||
@@ -363,16 +367,14 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
|||||||
entity->WriteComponents(stream, eReplicaPacketType::CONSTRUCTION);
|
entity->WriteComponents(stream, eReplicaPacketType::CONSTRUCTION);
|
||||||
|
|
||||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
||||||
if (skipChecks) {
|
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||||
Game::server->Send(stream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
// Don't need to construct the player to themselves
|
||||||
} else {
|
if (entity->GetObjectID() == player->GetObjectID()) continue;
|
||||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
if (player->GetPlayerReadyForUpdates()) {
|
||||||
if (player->GetPlayerReadyForUpdates()) {
|
Game::server->Send(stream, player->GetSystemAddress(), false);
|
||||||
Game::server->Send(stream, player->GetSystemAddress(), false);
|
} else {
|
||||||
} else {
|
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID());
|
||||||
if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -419,7 +421,7 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr)
|
|||||||
|
|
||||||
void EntityManager::SerializeEntity(Entity* entity) {
|
void EntityManager::SerializeEntity(Entity* entity) {
|
||||||
if (!entity) return;
|
if (!entity) return;
|
||||||
|
|
||||||
EntityManager::SerializeEntity(*entity);
|
EntityManager::SerializeEntity(*entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,9 +515,9 @@ void EntityManager::UpdateGhosting(Entity* player) {
|
|||||||
|
|
||||||
ghostComponent->ObserveEntity(id);
|
ghostComponent->ObserveEntity(id);
|
||||||
|
|
||||||
ConstructEntity(entity, player->GetSystemAddress());
|
|
||||||
|
|
||||||
entity->SetObservers(entity->GetObservers() + 1);
|
entity->SetObservers(entity->GetObservers() + 1);
|
||||||
|
|
||||||
|
ConstructEntity(entity, player->GetSystemAddress());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -604,3 +606,14 @@ void EntityManager::FireEventServerSide(Entity* origin, std::string args) {
|
|||||||
bool EntityManager::IsExcludedFromGhosting(LOT lot) {
|
bool EntityManager::IsExcludedFromGhosting(LOT lot) {
|
||||||
return std::find(m_GhostingExcludedLOTs.begin(), m_GhostingExcludedLOTs.end(), lot) != m_GhostingExcludedLOTs.end();
|
return std::find(m_GhostingExcludedLOTs.begin(), m_GhostingExcludedLOTs.end(), lot) != m_GhostingExcludedLOTs.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntityManager::SendMessage(GameMessages::GameMsg& msg) const {
|
||||||
|
bool handled = false;
|
||||||
|
const auto entityItr = m_Entities.find(msg.target);
|
||||||
|
if (entityItr != m_Entities.end()) {
|
||||||
|
auto* const entity = entityItr->second;
|
||||||
|
if (entity) handled = entity->HandleMsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,11 +9,15 @@
|
|||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
class Entity;
|
class Entity;
|
||||||
class EntityInfo;
|
struct EntityInfo;
|
||||||
class Player;
|
class Player;
|
||||||
class User;
|
class User;
|
||||||
enum class eReplicaComponentType : uint32_t;
|
enum class eReplicaComponentType : uint32_t;
|
||||||
|
|
||||||
|
namespace GameMessages {
|
||||||
|
struct GameMsg;
|
||||||
|
}
|
||||||
|
|
||||||
struct SystemAddress;
|
struct SystemAddress;
|
||||||
|
|
||||||
class EntityManager {
|
class EntityManager {
|
||||||
@@ -42,7 +46,7 @@ public:
|
|||||||
const std::unordered_map<LWOOBJID, Entity*> GetAllEntities() const { return m_Entities; }
|
const std::unordered_map<LWOOBJID, Entity*> GetAllEntities() const { return m_Entities; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, bool skipChecks = false);
|
void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
void DestructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
void DestructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
void SerializeEntity(Entity* entity);
|
void SerializeEntity(Entity* entity);
|
||||||
void SerializeEntity(const Entity& entity);
|
void SerializeEntity(const Entity& entity);
|
||||||
@@ -72,6 +76,9 @@ public:
|
|||||||
const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; };
|
const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; };
|
||||||
const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; };
|
const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; };
|
||||||
|
|
||||||
|
// Messaging
|
||||||
|
bool SendMessage(GameMessages::GameMsg& msg) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SerializeEntities();
|
void SerializeEntities();
|
||||||
void KillEntities();
|
void KillEntities();
|
||||||
|
|||||||
@@ -24,12 +24,13 @@ namespace LeaderboardManager {
|
|||||||
std::map<GameID, Leaderboard::Type> leaderboardCache;
|
std::map<GameID, Leaderboard::Type> leaderboardCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const Leaderboard::Type leaderboardType) {
|
Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const uint32_t numResults, const Leaderboard::Type leaderboardType) {
|
||||||
this->gameID = gameID;
|
this->gameID = gameID;
|
||||||
this->weekly = weekly;
|
this->weekly = weekly;
|
||||||
this->infoType = infoType;
|
this->infoType = infoType;
|
||||||
this->leaderboardType = leaderboardType;
|
this->leaderboardType = leaderboardType;
|
||||||
this->relatedPlayer = relatedPlayer;
|
this->relatedPlayer = relatedPlayer;
|
||||||
|
this->numResults = numResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
Leaderboard::~Leaderboard() {
|
Leaderboard::~Leaderboard() {
|
||||||
@@ -144,7 +145,7 @@ void QueryToLdf(Leaderboard& leaderboard, const std::vector<ILeaderboard::Entry>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ILeaderboard::Entry> FilterTo10(const std::vector<ILeaderboard::Entry>& leaderboard, const uint32_t relatedPlayer, const Leaderboard::InfoType infoType) {
|
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> toReturn;
|
std::vector<ILeaderboard::Entry> toReturn;
|
||||||
|
|
||||||
int32_t index = 0;
|
int32_t index = 0;
|
||||||
@@ -155,18 +156,19 @@ std::vector<ILeaderboard::Entry> FilterTo10(const std::vector<ILeaderboard::Entr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leaderboard.size() < 10) {
|
if (leaderboard.size() < numResults) {
|
||||||
toReturn.assign(leaderboard.begin(), leaderboard.end());
|
toReturn.assign(leaderboard.begin(), leaderboard.end());
|
||||||
index = 0;
|
index = 0;
|
||||||
} else if (index < 10) {
|
} else if (index < numResults) {
|
||||||
toReturn.assign(leaderboard.begin(), leaderboard.begin() + 10); // get the top 10 since we are in the top 10
|
toReturn.assign(leaderboard.begin(), leaderboard.begin() + numResults); // get the top 10 since we are in the top 10
|
||||||
index = 0;
|
index = 0;
|
||||||
} else if (index > leaderboard.size() - 10) {
|
} else if (index > leaderboard.size() - numResults) {
|
||||||
toReturn.assign(leaderboard.end() - 10, leaderboard.end()); // get the bottom 10 since we are in the bottom 10
|
toReturn.assign(leaderboard.end() - numResults, leaderboard.end()); // get the bottom 10 since we are in the bottom 10
|
||||||
index = leaderboard.size() - 10;
|
index = leaderboard.size() - numResults;
|
||||||
} else {
|
} else {
|
||||||
toReturn.assign(leaderboard.begin() + index - 5, leaderboard.begin() + index + 5); // get the 5 above and below
|
auto half = numResults / 2;
|
||||||
index -= 5;
|
toReturn.assign(leaderboard.begin() + index - half, leaderboard.begin() + index + half); // get the 5 above and below
|
||||||
|
index -= half;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t i = index;
|
int32_t i = index;
|
||||||
@@ -178,14 +180,16 @@ std::vector<ILeaderboard::Entry> FilterTo10(const std::vector<ILeaderboard::Entr
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ILeaderboard::Entry> FilterWeeklies(const std::vector<ILeaderboard::Entry>& leaderboard) {
|
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
|
// Filter the leaderboard to only include entries from the last week
|
||||||
const auto currentTime = std::chrono::system_clock::now();
|
const auto epochTime = system_clock::now();
|
||||||
auto epochTime = currentTime.time_since_epoch().count();
|
constexpr auto oneWeek = weeks(1);
|
||||||
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;
|
std::vector<ILeaderboard::Entry> weeklyLeaderboard;
|
||||||
for (const auto& entry : leaderboard) {
|
for (const auto& entry : leaderboard) {
|
||||||
if (epochTime - entry.lastPlayedTimestamp < SECONDS_IN_A_WEEK) {
|
const sys_time<seconds> asSysTime(seconds(entry.lastPlayedTimestamp));
|
||||||
|
const auto timeDiff = epochTime - asSysTime;
|
||||||
|
if (timeDiff < oneWeek) {
|
||||||
weeklyLeaderboard.push_back(entry);
|
weeklyLeaderboard.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,14 +217,15 @@ std::vector<ILeaderboard::Entry> ProcessLeaderboard(
|
|||||||
const std::vector<ILeaderboard::Entry>& leaderboard,
|
const std::vector<ILeaderboard::Entry>& leaderboard,
|
||||||
const bool weekly,
|
const bool weekly,
|
||||||
const Leaderboard::InfoType infoType,
|
const Leaderboard::InfoType infoType,
|
||||||
const uint32_t relatedPlayer) {
|
const uint32_t relatedPlayer,
|
||||||
|
const uint32_t numResults) {
|
||||||
std::vector<ILeaderboard::Entry> toReturn;
|
std::vector<ILeaderboard::Entry> toReturn;
|
||||||
|
|
||||||
if (infoType == Leaderboard::InfoType::Friends) {
|
if (infoType == Leaderboard::InfoType::Friends) {
|
||||||
const auto friendsLeaderboard = FilterFriends(leaderboard, relatedPlayer);
|
const auto friendsLeaderboard = FilterFriends(leaderboard, relatedPlayer);
|
||||||
toReturn = FilterTo10(weekly ? FilterWeeklies(friendsLeaderboard) : friendsLeaderboard, relatedPlayer, infoType);
|
toReturn = FilterToNumResults(weekly ? FilterWeeklies(friendsLeaderboard) : friendsLeaderboard, relatedPlayer, infoType, numResults);
|
||||||
} else {
|
} else {
|
||||||
toReturn = FilterTo10(weekly ? FilterWeeklies(leaderboard) : leaderboard, relatedPlayer, infoType);
|
toReturn = FilterToNumResults(weekly ? FilterWeeklies(leaderboard) : leaderboard, relatedPlayer, infoType, numResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
@@ -255,7 +260,7 @@ void Leaderboard::SetupLeaderboard(bool weekly) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto processedLeaderboard = ProcessLeaderboard(leaderboardRes, weekly, infoType, relatedPlayer);
|
const auto processedLeaderboard = ProcessLeaderboard(leaderboardRes, weekly, infoType, relatedPlayer, numResults);
|
||||||
|
|
||||||
QueryToLdf(*this, processedLeaderboard);
|
QueryToLdf(*this, processedLeaderboard);
|
||||||
}
|
}
|
||||||
@@ -301,8 +306,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) {
|
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, GetLeaderboardType(gameID));
|
Leaderboard leaderboard(gameID, infoType, weekly, playerID, numResults, GetLeaderboardType(gameID));
|
||||||
leaderboard.SetupLeaderboard(weekly);
|
leaderboard.SetupLeaderboard(weekly);
|
||||||
leaderboard.Send(targetID);
|
leaderboard.Send(targetID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
Leaderboard() = delete;
|
Leaderboard() = delete;
|
||||||
Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const Leaderboard::Type = None);
|
Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const uint32_t numResults, const Leaderboard::Type = None);
|
||||||
|
|
||||||
~Leaderboard();
|
~Leaderboard();
|
||||||
|
|
||||||
@@ -79,6 +79,7 @@ private:
|
|||||||
InfoType infoType;
|
InfoType infoType;
|
||||||
Leaderboard::Type leaderboardType;
|
Leaderboard::Type leaderboardType;
|
||||||
bool weekly;
|
bool weekly;
|
||||||
|
uint32_t numResults;
|
||||||
public:
|
public:
|
||||||
LeaderboardEntry& PushBackEntry() {
|
LeaderboardEntry& PushBackEntry() {
|
||||||
return entries.emplace_back();
|
return entries.emplace_back();
|
||||||
@@ -90,7 +91,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace LeaderboardManager {
|
namespace LeaderboardManager {
|
||||||
void SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID);
|
void SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID, const uint32_t numResults);
|
||||||
|
|
||||||
void SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0);
|
void SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
#include "dZoneManager.h"
|
#include "dZoneManager.h"
|
||||||
#include "eServerDisconnectIdentifiers.h"
|
#include "eServerDisconnectIdentifiers.h"
|
||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
|
#include "BitStreamUtils.h"
|
||||||
|
#include "MessageType/Chat.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
User::User(const SystemAddress& sysAddr, const std::string& username, const std::string& sessionKey) {
|
User::User(const SystemAddress& sysAddr, const std::string& username, const std::string& sessionKey) {
|
||||||
m_AccountID = 0;
|
m_AccountID = 0;
|
||||||
@@ -28,7 +32,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
|
|||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
m_AccountID = userInfo->id;
|
m_AccountID = userInfo->id;
|
||||||
m_MaxGMLevel = userInfo->maxGmLevel;
|
m_MaxGMLevel = userInfo->maxGmLevel;
|
||||||
m_MuteExpire = 0; //res->getUInt64(3);
|
m_MuteExpire = userInfo->muteExpire;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we're loading a zone, we'll load the last used (aka current) character:
|
//If we're loading a zone, we'll load the last used (aka current) character:
|
||||||
@@ -91,8 +95,28 @@ Character* User::GetLastUsedChar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool User::GetIsMuted() const {
|
bool User::GetIsMuted() {
|
||||||
return m_MuteExpire == 1 || m_MuteExpire > time(NULL);
|
using namespace std::chrono;
|
||||||
|
constexpr auto refreshInterval = seconds{ 60 };
|
||||||
|
const auto now = steady_clock::now();
|
||||||
|
if (now - m_LastMuteCheck >= refreshInterval) {
|
||||||
|
m_LastMuteCheck = now;
|
||||||
|
if (const auto info = Database::Get()->GetAccountInfo(m_Username)) {
|
||||||
|
const auto expire = static_cast<time_t>(info->muteExpire);
|
||||||
|
if (expire != m_MuteExpire) {
|
||||||
|
m_MuteExpire = expire;
|
||||||
|
|
||||||
|
if (Game::chatServer && m_LoggedInCharID != 0) {
|
||||||
|
RakNet::BitStream bitStream;
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::GM_MUTE);
|
||||||
|
bitStream.Write(m_LoggedInCharID);
|
||||||
|
bitStream.Write(m_MuteExpire);
|
||||||
|
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_MuteExpire == 1 || m_MuteExpire > std::time(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t User::GetMuteExpire() const {
|
time_t User::GetMuteExpire() const {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
#include "RakNetTypes.h"
|
#include "RakNetTypes.h"
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ public:
|
|||||||
const std::unordered_map<std::string, bool>& GetIsBestFriendMap() { return m_IsBestFriendMap; }
|
const std::unordered_map<std::string, bool>& GetIsBestFriendMap() { return m_IsBestFriendMap; }
|
||||||
void UpdateBestFriendValue(const std::string_view playerName, const bool newValue);
|
void UpdateBestFriendValue(const std::string_view playerName, const bool newValue);
|
||||||
|
|
||||||
bool GetIsMuted() const;
|
bool GetIsMuted();
|
||||||
|
|
||||||
time_t GetMuteExpire() const;
|
time_t GetMuteExpire() const;
|
||||||
void SetMuteExpire(time_t value);
|
void SetMuteExpire(time_t value);
|
||||||
@@ -72,7 +73,8 @@ private:
|
|||||||
bool m_LastChatMessageApproved = false;
|
bool m_LastChatMessageApproved = false;
|
||||||
int m_AmountOfTimesOutOfSync = 0;
|
int m_AmountOfTimesOutOfSync = 0;
|
||||||
const int m_MaxDesyncAllowed = 12;
|
const int m_MaxDesyncAllowed = 12;
|
||||||
time_t m_MuteExpire;
|
uint64_t m_MuteExpire;
|
||||||
|
std::chrono::steady_clock::time_point m_LastMuteCheck{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // USER_H
|
#endif // USER_H
|
||||||
|
|||||||
@@ -25,10 +25,11 @@
|
|||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
#include "eCharacterCreationResponse.h"
|
#include "eCharacterCreationResponse.h"
|
||||||
#include "eRenameResponse.h"
|
#include "eRenameResponse.h"
|
||||||
#include "eConnectionType.h"
|
#include "ServiceType.h"
|
||||||
#include "MessageType/Chat.h"
|
#include "MessageType/Chat.h"
|
||||||
#include "BitStreamUtils.h"
|
#include "BitStreamUtils.h"
|
||||||
#include "CheatDetection.h"
|
#include "CheatDetection.h"
|
||||||
|
#include "CharacterComponent.h"
|
||||||
|
|
||||||
UserManager* UserManager::m_Address = nullptr;
|
UserManager* UserManager::m_Address = nullptr;
|
||||||
|
|
||||||
@@ -216,7 +217,7 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RakNet::BitStream bitStream;
|
RakNet::BitStream bitStream;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHARACTER_LIST_RESPONSE);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::CHARACTER_LIST_RESPONSE);
|
||||||
|
|
||||||
std::vector<Character*> characters = u->GetCharacters();
|
std::vector<Character*> characters = u->GetCharacters();
|
||||||
bitStream.Write<uint8_t>(characters.size());
|
bitStream.Write<uint8_t>(characters.size());
|
||||||
@@ -340,7 +341,10 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
|
|
||||||
xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" ";
|
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 << "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;\"></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;\">";
|
||||||
|
xml << "<vl><l id=\"1000\" cid=\"0\"/></vl>";
|
||||||
|
|
||||||
|
xml << "</char>";
|
||||||
|
|
||||||
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
|
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
|
||||||
|
|
||||||
@@ -423,7 +427,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
Database::Get()->DeleteCharacter(charID);
|
Database::Get()->DeleteCharacter(charID);
|
||||||
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::UNEXPECTED_DISCONNECT);
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::UNEXPECTED_DISCONNECT);
|
||||||
bitStream.Write(objectID);
|
bitStream.Write(objectID);
|
||||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||||
|
|
||||||
@@ -522,6 +526,13 @@ 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) {
|
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);
|
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) {
|
if (character) {
|
||||||
|
auto* entity = Game::entityManager->GetEntity(character->GetObjectID());
|
||||||
|
if (entity) {
|
||||||
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||||
|
if (characterComponent) {
|
||||||
|
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));
|
||||||
|
}
|
||||||
|
}
|
||||||
character->SetZoneID(zoneID);
|
character->SetZoneID(zoneID);
|
||||||
character->SetZoneInstance(zoneInstance);
|
character->SetZoneInstance(zoneInstance);
|
||||||
character->SetZoneClone(zoneClone);
|
character->SetZoneClone(zoneClone);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bi
|
|||||||
|
|
||||||
//Handle player damage cooldown
|
//Handle player damage cooldown
|
||||||
if (entity->IsPlayer() && !this->m_DontApplyImmune) {
|
if (entity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||||
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
const float immunityTime = Game::zoneManager->GetWorldConfig().globalImmunityTime;
|
||||||
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
|||||||
|
|
||||||
//Handle player damage cooldown
|
//Handle player damage cooldown
|
||||||
if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) {
|
if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||||
destroyableComponent->SetDamageCooldownTimer(Game::zoneManager->GetWorldConfig()->globalImmunityTime);
|
destroyableComponent->SetDamageCooldownTimer(Game::zoneManager->GetWorldConfig().globalImmunityTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "QuickBuildComponent.h"
|
#include "QuickBuildComponent.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
#include "TeamManager.h"
|
#include "TeamManager.h"
|
||||||
#include "eConnectionType.h"
|
#include "ServiceType.h"
|
||||||
|
|
||||||
BehaviorSyncEntry::BehaviorSyncEntry() {
|
BehaviorSyncEntry::BehaviorSyncEntry() {
|
||||||
}
|
}
|
||||||
@@ -212,7 +212,7 @@ void BehaviorContext::UpdatePlayerSyncs(float deltaTime) {
|
|||||||
echo.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());
|
echo.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());
|
||||||
|
|
||||||
RakNet::BitStream message;
|
RakNet::BitStream message;
|
||||||
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
|
BitStreamUtils::WriteHeader(message, ServiceType::CLIENT, MessageType::Client::GAME_MSG);
|
||||||
message.Write(this->originator);
|
message.Write(this->originator);
|
||||||
echo.Serialize(message);
|
echo.Serialize(message);
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
|
|||||||
// Write message
|
// Write message
|
||||||
RakNet::BitStream message;
|
RakNet::BitStream message;
|
||||||
|
|
||||||
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
|
BitStreamUtils::WriteHeader(message, ServiceType::CLIENT, MessageType::Client::GAME_MSG);
|
||||||
message.Write(this->originator);
|
message.Write(this->originator);
|
||||||
echo.Serialize(message);
|
echo.Serialize(message);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "BehaviorContext.h"
|
#include "BehaviorContext.h"
|
||||||
#include "EntityManager.h"
|
#include "EntityManager.h"
|
||||||
|
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
||||||
Entity* sourceEntity;
|
Entity* sourceEntity;
|
||||||
if (this->m_orientCaster) sourceEntity = Game::entityManager->GetEntity(context->originator);
|
if (this->m_orientCaster) sourceEntity = Game::entityManager->GetEntity(context->originator);
|
||||||
@@ -16,12 +18,12 @@ void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitS
|
|||||||
if (!destinationEntity) return;
|
if (!destinationEntity) return;
|
||||||
|
|
||||||
sourceEntity->SetRotation(
|
sourceEntity->SetRotation(
|
||||||
NiQuaternion::LookAt(sourceEntity->GetPosition(), destinationEntity->GetPosition())
|
QuatUtils::LookAt(sourceEntity->GetPosition(), destinationEntity->GetPosition())
|
||||||
);
|
);
|
||||||
} else if (this->m_toAngle){
|
} else if (this->m_toAngle){
|
||||||
auto baseAngle = NiPoint3(0, 0, this->m_angle);
|
auto baseAngle = NiPoint3(0, 0, this->m_angle);
|
||||||
if (this->m_relative) baseAngle += sourceEntity->GetRotation().GetForwardVector();
|
if (this->m_relative) baseAngle += QuatUtils::Forward(sourceEntity->GetRotation());
|
||||||
sourceEntity->SetRotation(NiQuaternion::FromEulerAngles(baseAngle));
|
sourceEntity->SetRotation(glm::quat(glm::vec3(baseAngle.x, baseAngle.y, baseAngle.z)));
|
||||||
} else return;
|
} else return;
|
||||||
Game::entityManager->SerializeEntity(sourceEntity);
|
Game::entityManager->SerializeEntity(sourceEntity);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ void ConsumeItemBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bi
|
|||||||
auto inventoryComponent = caster->GetComponent<InventoryComponent>();
|
auto inventoryComponent = caster->GetComponent<InventoryComponent>();
|
||||||
if (!inventoryComponent) return;
|
if (!inventoryComponent) return;
|
||||||
|
|
||||||
if (inventoryComponent->RemoveItem(this->m_ConsumeLOT, this->m_NumToConsume, eInventoryType::INVALID, false, true)){
|
if (inventoryComponent->RemoveItem(this->m_ConsumeLOT, this->m_NumToConsume, eInventoryType::ALL, false, true)){
|
||||||
action_to_cast = m_ActionConsumed;
|
action_to_cast = m_ActionConsumed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStrea
|
|||||||
if (controllablePhysicsComponent != nullptr) {
|
if (controllablePhysicsComponent != nullptr) {
|
||||||
|
|
||||||
if (m_Forward == 1) {
|
if (m_Forward == 1) {
|
||||||
controllablePhysicsComponent->SetVelocity(controllablePhysicsComponent->GetRotation().GetForwardVector() * 25);
|
controllablePhysicsComponent->SetVelocity(QuatUtils::Forward(controllablePhysicsComponent->GetRotation()) * 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(casterEntity);
|
Game::entityManager->SerializeEntity(casterEntity);
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
|||||||
|
|
||||||
const auto time = distance / this->m_projectileSpeed;
|
const auto time = distance / this->m_projectileSpeed;
|
||||||
|
|
||||||
const auto rotation = NiQuaternion::LookAtUnlocked(position, other->GetPosition());
|
const auto rotation = QuatUtils::LookAtUnlocked(position, other->GetPosition());
|
||||||
|
|
||||||
const auto targetPosition = other->GetPosition();
|
const auto targetPosition = other->GetPosition();
|
||||||
|
|
||||||
@@ -112,13 +112,13 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
|||||||
|
|
||||||
bitStream.Write(id);
|
bitStream.Write(id);
|
||||||
|
|
||||||
auto eulerAngles = rotation.GetEulerAngles();
|
auto eulerAngles = QuatUtils::Euler(rotation);
|
||||||
|
|
||||||
eulerAngles.y += angle * (3.14 / 180);
|
eulerAngles.y += angle * (glm::pi<float>() / 180.0f);
|
||||||
|
|
||||||
const auto angledRotation = NiQuaternion::FromEulerAngles(eulerAngles);
|
const auto angledRotation = QuatUtils::FromEuler(eulerAngles);
|
||||||
|
|
||||||
const auto direction = angledRotation.GetForwardVector();
|
const auto direction = QuatUtils::Forward(angledRotation);
|
||||||
|
|
||||||
const auto destination = position + direction * distance;
|
const auto destination = position + direction * distance;
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,15 @@ 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);
|
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()) {
|
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()->SetZoneID(zoneID);
|
||||||
entity->GetCharacter()->SetZoneInstance(zoneInstance);
|
entity->GetCharacter()->SetZoneInstance(zoneInstance);
|
||||||
entity->GetCharacter()->SetZoneClone(zoneClone);
|
entity->GetCharacter()->SetZoneClone(zoneClone);
|
||||||
entity->GetComponent<CharacterComponent>()->SetLastRocketConfig(u"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entity->GetCharacter()->SaveXMLToDatabase();
|
entity->GetCharacter()->SaveXMLToDatabase();
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStrea
|
|||||||
info.spawner = nullptr;
|
info.spawner = nullptr;
|
||||||
info.spawnerID = context->originator;
|
info.spawnerID = context->originator;
|
||||||
info.spawnerNodeID = 0;
|
info.spawnerNodeID = 0;
|
||||||
info.pos = info.pos + (info.rot.GetForwardVector() * m_Distance);
|
info.pos = info.pos + (QuatUtils::Forward(info.rot) * m_Distance);
|
||||||
|
|
||||||
auto* entity = Game::entityManager->CreateEntity(
|
auto* entity = Game::entityManager->CreateEntity(
|
||||||
info,
|
info,
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitS
|
|||||||
if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound)
|
if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto forward = self->GetRotation().GetForwardVector();
|
const auto forward = QuatUtils::Forward(self->GetRotation());
|
||||||
|
|
||||||
// forward is a normalized vector of where the caster is facing.
|
// forward is a normalized vector of where the caster is facing.
|
||||||
// targetPos is the position of the target.
|
// targetPos is the position of the target.
|
||||||
|
|||||||
@@ -64,12 +64,11 @@ void AchievementVendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t altCurrencyCost = itemComp.commendationCost * count;
|
const uint32_t altCurrencyCost = itemComp.commendationCost * count;
|
||||||
if (inventoryComponent->GetLotCount(costLOT) < altCurrencyCost) {
|
if (inventoryComponent->GetLotCount(costLOT) < altCurrencyCost || !inventoryComponent->RemoveItem(costLOT, altCurrencyCost, eInventoryType::ALL)) {
|
||||||
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
|
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inventoryComponent->RemoveItem(costLOT, altCurrencyCost);
|
|
||||||
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
|
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
|
||||||
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
|
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user