Merge branch 'main' into webapiv2

This commit is contained in:
Aaron Kimbrell 2025-01-02 16:13:17 -06:00 committed by GitHub
commit 9387a8e3d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 656 additions and 287 deletions

View File

@ -32,6 +32,8 @@ Darkflame Universe is a server emulator and does not distribute any LEGO® Unive
* To connect to the server, either delete the file `boot.cfg` which is found in your LEGO Universe client, rename the file `boot.cfg` to something else or follow the steps [here](#allowing-a-user-to-connect-to-your-server) if you wish to keep the file. * To connect to the server, either delete the file `boot.cfg` which is found in your LEGO Universe client, rename the file `boot.cfg` to something else or follow the steps [here](#allowing-a-user-to-connect-to-your-server) if you wish to keep the file.
* When shutting down the server, it is highly recommended to click the `MasterServer.exe` window and hold `ctrl` while pressing `c` to stop the server. * When shutting down the server, it is highly recommended to click the `MasterServer.exe` window and hold `ctrl` while pressing `c` to stop the server.
* We are working on a way to make it so when you close the game, the server stops automatically alongside when you open the game, the server starts automatically. * We are working on a way to make it so when you close the game, the server stops automatically alongside when you open the game, the server starts automatically.
* If you are not setting a server up on mac, you can ignore this note
* Note: you'll need to allow through System Preferences `AuthServer`, `ChatServer`, `MasterServer`, `WorldServer` and `libmariadbcpp.dylib` to run. The initial pop-up will block it due to the binaries being unsigned, after allowing them to run the servers will run as normal.
<font size="32">**If you are not planning on hosting a server for others, working in the codebase or wanting to use MariaDB for a database, you can stop reading here.**</font> <font size="32">**If you are not planning on hosting a server for others, working in the codebase or wanting to use MariaDB for a database, you can stop reading here.**</font>
@ -68,6 +70,12 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
## Install dependencies ## Install dependencies
### Required compiler versions
- g++11 or greater
- MSVC unchecked
- clang unchecked
- appleclang unchecked
### Windows packages ### Windows packages
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed. Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!). You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
@ -202,6 +210,7 @@ If you would like to build the server faster, append `-j<number>` where number i
### Notes ### Notes
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building: Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory. * If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
* By default it should be set to the correct directory.
* If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023. * If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
## Configuring your server ## Configuring your server
@ -224,28 +233,41 @@ Navigate to `build/sharedconfig.ini` and fill in the following fields:
* `chatconfig.ini` contains a port option. * `chatconfig.ini` contains a port option.
* `masterconfig.ini` contains options related to permissions you want to run your servers with. * `masterconfig.ini` contains options related to permissions you want to run your servers with.
* `sharedconfig.ini` contains several options that are shared across all servers * `sharedconfig.ini` contains several options that are shared across all servers
* `worldconfig.ini` contains several options to turn on QOL improvements should you want them. If you would like the most vanilla experience possible, you will need to turn some of these settings off. * `worldconfig.ini` contains several options to turn on Quality of Life improvements should you want them. If you would like the most vanilla experience possible, you will need to turn some of these settings off.
## Verify your setup ## Verify your setup
Your build directory should now look like this: Your build directory should contain at a minimum all of the following files.
* AuthServer All listed files are required for a server to start.
* ChatServer `ini` files can be located at the environment variable `DLU_CONFIG_DIR` and do not need to be located in this directory.
* MasterServer (windows will have .exe at the end of the executables):
* WorldServer
* authconfig.ini
* chatconfig.ini
* masterconfig.ini
* sharedconfig.ini * sharedconfig.ini
* AuthServer(.exe)
* authconfig.ini
* ChatServer(.exe)
* chatconfig.ini
* MasterServer(.exe)
* masterconfig.ini
* WorldServer(.exe)
* worldconfig.ini * worldconfig.ini
* ... * blocklist.dcf
* migrations
* vanity
* navmeshes
* 1 of the following lists based on platform
* windows
* libmariadb.dll
* mariadbcpp.dll
* zlib.dll
* MacOS
* libmariadbcpp.dylib
* *nix
* libmariadbcpp.so
## Running the server ## Running the server
If everything has been configured correctly you should now be able to run the `MasterServer` binary which is located in the `build` directory. Darkflame Universe utilizes port numbers under 1024, so under Linux you either have to give the `AuthServer` binary network permissions or run it under sudo. If everything has been configured correctly you should now be able to run the `MasterServer` binary which is located in the `build` directory. Darkflame Universe utilizes port numbers under 1024, so under Linux you have to give the `AuthServer` binary network permissions by running the following command:
To give `AuthServer` network permissions and not require sudo, run the following command
```bash ```bash
sudo setcap 'cap_net_bind_service=+ep' AuthServer sudo setcap 'cap_net_bind_service=+ep' AuthServer
``` ```
and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0.
### Linux Service ### Linux Service
If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server. If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server.
@ -308,8 +330,14 @@ To connect to a server follow these steps:
* 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 * 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:` * 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
* 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
```cfg
AUTHSERVERIP=0:12.34.56.78,
UGCUSE3DSERVICES=7:0
```
## Updating your server ## Updating your server
To update your server to the latest version navigate to your cloned directory To update your server to the latest version navigate to your cloned directory

View File

@ -27,7 +27,6 @@
#include "Game.h" #include "Game.h"
#include "Server.h" #include "Server.h"
namespace Game { namespace Game {
Logger* logger = nullptr; Logger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;

View File

@ -1,7 +1,4 @@
add_executable(AuthServer "AuthServer.cpp") add_executable(AuthServer "AuthServer.cpp")
target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer) target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer)
target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer)
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")

View File

@ -29,33 +29,35 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
auto& player = Game::playerContainer.GetPlayerDataMutable(playerID); auto& player = Game::playerContainer.GetPlayerDataMutable(playerID);
if (!player) return; if (!player) return;
auto friendsList = Database::Get()->GetFriendsList(playerID); if (player.friends.empty()) {
for (const auto& friendData : friendsList) { auto friendsList = Database::Get()->GetFriendsList(playerID);
FriendData fd; for (const auto& friendData : friendsList) {
fd.isFTP = false; // not a thing in DLU FriendData fd;
fd.friendID = friendData.friendID; fd.isFTP = false; // not a thing in DLU
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT); fd.friendID = friendData.friendID;
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER); GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
if (fd.isBestFriend) player.countOfBestFriends += 1; if (fd.isBestFriend) player.countOfBestFriends += 1;
fd.friendName = friendData.friendName; fd.friendName = friendData.friendName;
//Now check if they're online: //Now check if they're online:
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID); const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
if (fr) { if (fr) {
fd.isOnline = true; fd.isOnline = true;
fd.zoneID = fr.zoneID; fd.zoneID = fr.zoneID;
//Since this friend is online, we need to update them on the fact that we've just logged in: //Since this friend is online, we need to update them on the fact that we've just logged in:
SendFriendUpdate(fr, player, 1, fd.isBestFriend); SendFriendUpdate(fr, player, 1, fd.isBestFriend);
} else { } else {
fd.isOnline = false; fd.isOnline = false;
fd.zoneID = LWOZONEID(); fd.zoneID = LWOZONEID();
}
player.friends.push_back(fd);
} }
player.friends.push_back(fd);
} }
//Now, we need to send the friendlist to the server they came from: //Now, we need to send the friendlist to the server they came from:
@ -140,7 +142,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
// Prevent GM friend spam // Prevent GM friend spam
// If the player we are trying to be friends with is not a civilian and we are a civilian, abort the process // If the player we are trying to be friends with is not a civilian and we are a civilian, abort the process
if (requestee.gmLevel > eGameMasterLevel::CIVILIAN && requestor.gmLevel == eGameMasterLevel::CIVILIAN ) { if (requestee.gmLevel > eGameMasterLevel::CIVILIAN && requestor.gmLevel == eGameMasterLevel::CIVILIAN) {
SendFriendResponse(requestor, requestee, eAddFriendResponseType::MYTHRAN); SendFriendResponse(requestor, requestee, eAddFriendResponseType::MYTHRAN);
return; return;
} }
@ -400,8 +402,8 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
bitStream.Write(Game::playerContainer.GetSimCount()); bitStream.Write(Game::playerContainer.GetSimCount());
bitStream.Write<uint8_t>(request.displayIndividualPlayers); bitStream.Write<uint8_t>(request.displayIndividualPlayers);
bitStream.Write<uint8_t>(request.displayZoneData); bitStream.Write<uint8_t>(request.displayZoneData);
if (request.displayZoneData || request.displayIndividualPlayers){ if (request.displayZoneData || request.displayIndividualPlayers) {
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){ for (auto& [playerID, playerData] : Game::playerContainer.GetAllPlayers()) {
if (!playerData) continue; if (!playerData) continue;
bitStream.Write<uint8_t>(0); // structure packing bitStream.Write<uint8_t>(0); // structure packing
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName)); if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));

View File

@ -131,6 +131,8 @@ int main(int argc, char** argv) {
if (web_server_enabled) { if (web_server_enabled) {
LOG("Web server enabled, will process http requests."); LOG("Web server enabled, will process http requests.");
} }
auto lastTime = std::chrono::high_resolution_clock::now();
Game::logger->Flush(); // once immediately before main loop Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) { while (!Game::ShouldShutdown()) {
@ -142,7 +144,11 @@ int main(int argc, char** argv) {
break; //Exit our loop, shut down. break; //Exit our loop, shut down.
} else framesSinceMasterDisconnect = 0; } else framesSinceMasterDisconnect = 0;
//In world we'd update our other systems here. const auto currentTime = std::chrono::high_resolution_clock::now();
const float deltaTime = std::chrono::duration<float>(currentTime - lastTime).count();
lastTime = currentTime;
Game::playerContainer.Update(deltaTime);
//Check for packets here: //Check for packets here:
Game::server->ReceiveFromMaster(); //ReceiveFromMaster also handles the master packets if needed. Game::server->ReceiveFromMaster(); //ReceiveFromMaster also handles the master packets if needed.
@ -182,7 +188,7 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(chatFrameDelta); //Chat can run at a lower "fps" t += std::chrono::milliseconds(chatFrameDelta); //Chat can run at a lower "fps"
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
Game::playerContainer.Shutdown();
//Delete our objects here: //Delete our objects here:
Database::Destroy("ChatServer"); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
@ -211,150 +217,150 @@ void HandlePacket(Packet* packet) {
inStream.Read(chatMessageID); inStream.Read(chatMessageID);
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); Game::playerContainer.CreateTeamServer(packet);
break; break;
case MessageType::Chat::GET_FRIENDS_LIST: case MessageType::Chat::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet); ChatPacketHandler::HandleFriendlistRequest(packet);
break; break;
case MessageType::Chat::GET_IGNORE_LIST: case MessageType::Chat::GET_IGNORE_LIST:
ChatIgnoreList::GetIgnoreList(packet); ChatIgnoreList::GetIgnoreList(packet);
break; break;
case MessageType::Chat::ADD_IGNORE: case MessageType::Chat::ADD_IGNORE:
ChatIgnoreList::AddIgnore(packet); ChatIgnoreList::AddIgnore(packet);
break; break;
case MessageType::Chat::REMOVE_IGNORE: case MessageType::Chat::REMOVE_IGNORE:
ChatIgnoreList::RemoveIgnore(packet); ChatIgnoreList::RemoveIgnore(packet);
break; break;
case MessageType::Chat::TEAM_GET_STATUS: case MessageType::Chat::TEAM_GET_STATUS:
ChatPacketHandler::HandleTeamStatusRequest(packet); ChatPacketHandler::HandleTeamStatusRequest(packet);
break; break;
case MessageType::Chat::ADD_FRIEND_REQUEST: case MessageType::Chat::ADD_FRIEND_REQUEST:
//this involves someone sending the initial request, the response is below, response as in from the other player. //this involves someone sending the initial request, the response is below, response as in from the other player.
//We basically just check to see if this player is online or not and route the packet. //We basically just check to see if this player is online or not and route the packet.
ChatPacketHandler::HandleFriendRequest(packet); ChatPacketHandler::HandleFriendRequest(packet);
break; break;
case MessageType::Chat::ADD_FRIEND_RESPONSE: case MessageType::Chat::ADD_FRIEND_RESPONSE:
//This isn't the response a server sent, rather it is a player's response to a received request. //This isn't the response a server sent, rather it is a player's response to a received request.
//Here, we'll actually have to add them to eachother's friend lists depending on the response code. //Here, we'll actually have to add them to eachother's friend lists depending on the response code.
ChatPacketHandler::HandleFriendResponse(packet); ChatPacketHandler::HandleFriendResponse(packet);
break; break;
case MessageType::Chat::REMOVE_FRIEND: case MessageType::Chat::REMOVE_FRIEND:
ChatPacketHandler::HandleRemoveFriend(packet); ChatPacketHandler::HandleRemoveFriend(packet);
break; break;
case MessageType::Chat::GENERAL_CHAT_MESSAGE: case MessageType::Chat::GENERAL_CHAT_MESSAGE:
ChatPacketHandler::HandleChatMessage(packet); ChatPacketHandler::HandleChatMessage(packet);
break; break;
case MessageType::Chat::PRIVATE_CHAT_MESSAGE: case MessageType::Chat::PRIVATE_CHAT_MESSAGE:
//This message is supposed to be echo'd to both the sender and the receiver //This message is supposed to be echo'd to both the sender and the receiver
//BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up. //BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up.
ChatPacketHandler::HandlePrivateChatMessage(packet); ChatPacketHandler::HandlePrivateChatMessage(packet);
break; break;
case MessageType::Chat::TEAM_INVITE: case MessageType::Chat::TEAM_INVITE:
ChatPacketHandler::HandleTeamInvite(packet); ChatPacketHandler::HandleTeamInvite(packet);
break; break;
case MessageType::Chat::TEAM_INVITE_RESPONSE: case MessageType::Chat::TEAM_INVITE_RESPONSE:
ChatPacketHandler::HandleTeamInviteResponse(packet); ChatPacketHandler::HandleTeamInviteResponse(packet);
break; break;
case MessageType::Chat::TEAM_LEAVE: case MessageType::Chat::TEAM_LEAVE:
ChatPacketHandler::HandleTeamLeave(packet); ChatPacketHandler::HandleTeamLeave(packet);
break; break;
case MessageType::Chat::TEAM_SET_LEADER: case MessageType::Chat::TEAM_SET_LEADER:
ChatPacketHandler::HandleTeamPromote(packet); ChatPacketHandler::HandleTeamPromote(packet);
break; break;
case MessageType::Chat::TEAM_KICK: case MessageType::Chat::TEAM_KICK:
ChatPacketHandler::HandleTeamKick(packet); ChatPacketHandler::HandleTeamKick(packet);
break; break;
case MessageType::Chat::TEAM_SET_LOOT: case MessageType::Chat::TEAM_SET_LOOT:
ChatPacketHandler::HandleTeamLootOption(packet); ChatPacketHandler::HandleTeamLootOption(packet);
break; break;
case MessageType::Chat::GMLEVEL_UPDATE: case MessageType::Chat::GMLEVEL_UPDATE:
ChatPacketHandler::HandleGMLevelUpdate(packet); ChatPacketHandler::HandleGMLevelUpdate(packet);
break; break;
case MessageType::Chat::LOGIN_SESSION_NOTIFY: case MessageType::Chat::LOGIN_SESSION_NOTIFY:
Game::playerContainer.InsertPlayer(packet); Game::playerContainer.InsertPlayer(packet);
break; break;
case MessageType::Chat::GM_ANNOUNCE:{ case MessageType::Chat::GM_ANNOUNCE: {
// we just forward this packet to every connected server // we just forward this packet to every connected server
inStream.ResetReadPointer(); inStream.ResetReadPointer();
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
} }
break; break;
case MessageType::Chat::UNEXPECTED_DISCONNECT: case MessageType::Chat::UNEXPECTED_DISCONNECT:
Game::playerContainer.RemovePlayer(packet); Game::playerContainer.ScheduleRemovePlayer(packet);
break; break;
case MessageType::Chat::WHO: case MessageType::Chat::WHO:
ChatPacketHandler::HandleWho(packet); ChatPacketHandler::HandleWho(packet);
break; break;
case MessageType::Chat::SHOW_ALL: case MessageType::Chat::SHOW_ALL:
ChatPacketHandler::HandleShowAll(packet); ChatPacketHandler::HandleShowAll(packet);
break; 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:
case MessageType::Chat::WORLD_PARCEL_RESPONSE: case MessageType::Chat::WORLD_PARCEL_RESPONSE:
case MessageType::Chat::TEAM_MISSED_INVITE_CHECK: case MessageType::Chat::TEAM_MISSED_INVITE_CHECK:
case MessageType::Chat::GUILD_CREATE: case MessageType::Chat::GUILD_CREATE:
case MessageType::Chat::GUILD_INVITE: case MessageType::Chat::GUILD_INVITE:
case MessageType::Chat::GUILD_INVITE_RESPONSE: case MessageType::Chat::GUILD_INVITE_RESPONSE:
case MessageType::Chat::GUILD_LEAVE: case MessageType::Chat::GUILD_LEAVE:
case MessageType::Chat::GUILD_KICK: case MessageType::Chat::GUILD_KICK:
case MessageType::Chat::GUILD_GET_STATUS: case MessageType::Chat::GUILD_GET_STATUS:
case MessageType::Chat::GUILD_GET_ALL: case MessageType::Chat::GUILD_GET_ALL:
case MessageType::Chat::BLUEPRINT_MODERATED: case MessageType::Chat::BLUEPRINT_MODERATED:
case MessageType::Chat::BLUEPRINT_MODEL_READY: case MessageType::Chat::BLUEPRINT_MODEL_READY:
case MessageType::Chat::PROPERTY_READY_FOR_APPROVAL: case MessageType::Chat::PROPERTY_READY_FOR_APPROVAL:
case MessageType::Chat::PROPERTY_MODERATION_CHANGED: case MessageType::Chat::PROPERTY_MODERATION_CHANGED:
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED: case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED:
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED_REPORT: case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED_REPORT:
case MessageType::Chat::MAIL: case MessageType::Chat::MAIL:
case MessageType::Chat::WORLD_INSTANCE_LOCATION_REQUEST: case MessageType::Chat::WORLD_INSTANCE_LOCATION_REQUEST:
case MessageType::Chat::REPUTATION_UPDATE: case MessageType::Chat::REPUTATION_UPDATE:
case MessageType::Chat::SEND_CANNED_TEXT: case MessageType::Chat::SEND_CANNED_TEXT:
case MessageType::Chat::CHARACTER_NAME_CHANGE_REQUEST: case MessageType::Chat::CHARACTER_NAME_CHANGE_REQUEST:
case MessageType::Chat::CSR_REQUEST: case MessageType::Chat::CSR_REQUEST:
case MessageType::Chat::CSR_REPLY: case MessageType::Chat::CSR_REPLY:
case MessageType::Chat::GM_KICK: case MessageType::Chat::GM_KICK:
case MessageType::Chat::WORLD_ROUTE_PACKET: case MessageType::Chat::WORLD_ROUTE_PACKET:
case MessageType::Chat::GET_ZONE_POPULATIONS: case MessageType::Chat::GET_ZONE_POPULATIONS:
case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE: case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE:
case MessageType::Chat::MATCH_REQUEST: case MessageType::Chat::MATCH_REQUEST:
case MessageType::Chat::UGCMANIFEST_REPORT_MISSING_FILE: case MessageType::Chat::UGCMANIFEST_REPORT_MISSING_FILE:
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_FILE: case MessageType::Chat::UGCMANIFEST_REPORT_DONE_FILE:
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::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:
case MessageType::Chat::UPDATE_DONATION: case MessageType::Chat::UPDATE_DONATION:
case MessageType::Chat::PRG_CSR_COMMAND: case MessageType::Chat::PRG_CSR_COMMAND:
case MessageType::Chat::HEARTBEAT_REQUEST_FROM_WORLD: case MessageType::Chat::HEARTBEAT_REQUEST_FROM_WORLD:
case MessageType::Chat::UPDATE_FREE_TRIAL_STATUS: case MessageType::Chat::UPDATE_FREE_TRIAL_STATUS:
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID); LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
break; break;
default: default:
LOG("Unknown CHAT Message id: %i", chatMessageID); LOG("Unknown CHAT Message id: %i", chatMessageID);
} }
} }

View File

@ -74,13 +74,32 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
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());
Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID()); Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID());
m_PlayersToRemove.erase(playerId);
} }
void PlayerContainer::RemovePlayer(Packet* packet) { void PlayerContainer::ScheduleRemovePlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
LWOOBJID playerID; LWOOBJID playerID{ LWOOBJID_EMPTY };
inStream.Read(playerID); inStream.Read(playerID);
constexpr float updatePlayerOnLogoutTime = 20.0f;
if (playerID != LWOOBJID_EMPTY) m_PlayersToRemove.insert_or_assign(playerID, updatePlayerOnLogoutTime);
}
void PlayerContainer::Update(const float deltaTime) {
for (auto it = m_PlayersToRemove.begin(); it != m_PlayersToRemove.end();) {
auto& [id, time] = *it;
time -= deltaTime;
if (time <= 0.0f) {
RemovePlayer(id);
it = m_PlayersToRemove.erase(it);
} else {
++it;
}
}
}
void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
//Before they get kicked, we need to also send a message to their friends saying that they disconnected. //Before they get kicked, we need to also send a message to their friends saying that they disconnected.
const auto& player = GetPlayerData(playerID); const auto& player = GetPlayerData(playerID);
@ -434,3 +453,13 @@ const PlayerData& PlayerContainer::GetPlayerData(const LWOOBJID& playerID) {
const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) { const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) {
return GetPlayerDataMutable(playerName); return GetPlayerDataMutable(playerName);
} }
void PlayerContainer::Shutdown() {
m_Players.erase(LWOOBJID_EMPTY);
while (!m_Players.empty()) {
const auto& [id, playerData] = *m_Players.begin();
Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
m_Players.erase(m_Players.begin());
}
for (auto* team : mTeams) if (team) delete team;
}

View File

@ -65,10 +65,12 @@ class PlayerContainer {
public: public:
void Initialize(); void Initialize();
void InsertPlayer(Packet* packet); void InsertPlayer(Packet* packet);
void RemovePlayer(Packet* packet); void ScheduleRemovePlayer(Packet* packet);
void RemovePlayer(const LWOOBJID playerID);
void MuteUpdate(Packet* packet); void MuteUpdate(Packet* packet);
void CreateTeamServer(Packet* packet); void CreateTeamServer(Packet* packet);
void BroadcastMuteUpdate(LWOOBJID player, time_t time); void BroadcastMuteUpdate(LWOOBJID player, time_t time);
void Shutdown();
const PlayerData& GetPlayerData(const LWOOBJID& playerID); const PlayerData& GetPlayerData(const LWOOBJID& playerID);
const PlayerData& GetPlayerData(const std::string& playerName); const PlayerData& GetPlayerData(const std::string& playerName);
@ -93,11 +95,15 @@ public:
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; } uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
const std::vector<TeamData*>& GetAllTeams() { return mTeams;}; const std::vector<TeamData*>& GetAllTeams() { return mTeams;};
void Update(const float deltaTime);
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
private: private:
LWOOBJID m_TeamIDCounter = 0; LWOOBJID m_TeamIDCounter = 0;
std::map<LWOOBJID, PlayerData> m_Players; std::map<LWOOBJID, PlayerData> m_Players;
std::vector<TeamData*> mTeams; std::vector<TeamData*> mTeams;
std::unordered_map<LWOOBJID, std::u16string> m_Names; std::unordered_map<LWOOBJID, std::u16string> m_Names;
std::map<LWOOBJID, float> m_PlayersToRemove;
uint32_t m_MaxNumberOfBestFriends = 5; uint32_t m_MaxNumberOfBestFriends = 5;
uint32_t m_MaxNumberOfFriends = 50; uint32_t m_MaxNumberOfFriends = 50;
uint32_t m_PlayerCount = 0; uint32_t m_PlayerCount = 0;

View File

@ -56,6 +56,7 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
params.playerId params.playerId
); );
if (count->next()) { if (count->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count->getUInt("count"); result->totalEntriesMatchingQuery = count->getUInt("count");
} }
} else { } else {
@ -109,11 +110,13 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
params.playerSort params.playerSort
); );
if (count->next()) { if (count->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count->getUInt("count"); result->totalEntriesMatchingQuery = count->getUInt("count");
} }
} }
while (properties->next()) { while (properties->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
auto& entry = result->entries.emplace_back(); auto& entry = result->entries.emplace_back();
entry.id = properties->getUInt64("id"); entry.id = properties->getUInt64("id");
entry.ownerId = properties->getUInt64("owner_id"); entry.ownerId = properties->getUInt64("owner_id");

View File

@ -775,6 +775,12 @@ void Entity::Initialize() {
// Hacky way to trigger these when the object has had a chance to get constructed // Hacky way to trigger these when the object has had a chance to get constructed
AddCallbackTimer(0, [this]() { AddCallbackTimer(0, [this]() {
this->GetScript()->OnStartup(this); this->GetScript()->OnStartup(this);
if (this->m_ParentEntity) {
GameMessages::ChildLoaded childLoaded;
childLoaded.childID = this->m_ObjectID;
childLoaded.templateID = this->GetLOT();
this->m_ParentEntity->OnChildLoaded(childLoaded);
}
}); });
if (!m_Character && Game::entityManager->GetGhostingEnabled()) { if (!m_Character && Game::entityManager->GetGhostingEnabled()) {
@ -1501,6 +1507,10 @@ void Entity::OnShootingGalleryFire(GameMessages::ShootingGalleryFire& fire) {
GetScript()->OnShootingGalleryFire(*this, fire); GetScript()->OnShootingGalleryFire(*this, fire);
} }
void Entity::OnChildLoaded(GameMessages::ChildLoaded& childLoaded) {
GetScript()->OnChildLoaded(*this, childLoaded);
}
void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) { void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) {
GetScript()->OnRequestActivityExit(sender, player, canceled); GetScript()->OnRequestActivityExit(sender, player, canceled);
} }

View File

@ -16,6 +16,7 @@
namespace GameMessages { namespace GameMessages {
struct ActivityNotify; struct ActivityNotify;
struct ShootingGalleryFire; struct ShootingGalleryFire;
struct ChildLoaded;
}; };
namespace Loot { namespace Loot {
@ -217,6 +218,7 @@ public:
void OnZonePropertyModelRotated(Entity* player); void OnZonePropertyModelRotated(Entity* player);
void OnActivityNotify(GameMessages::ActivityNotify& notify); void OnActivityNotify(GameMessages::ActivityNotify& notify);
void OnShootingGalleryFire(GameMessages::ShootingGalleryFire& notify); void OnShootingGalleryFire(GameMessages::ShootingGalleryFire& notify);
void OnChildLoaded(GameMessages::ChildLoaded& childLoaded);
void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData); void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData);
void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier); void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier);

View File

@ -84,6 +84,10 @@ dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") { } else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true); toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2; m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume1_CaveExit.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 112.416870f, 50.363434f, 87.679268f);
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume2_RoadGaps.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 48.386536f, 50.363434f, 259.361755f);
} else { } else {
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str()); // LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());

View File

@ -285,7 +285,7 @@ void RacingControlComponent::OnRacingClientReady(Entity* player) {
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
void RacingControlComponent::OnRequestDie(Entity* player) { void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string& deathType) {
// Sent by the client when they collide with something which should smash // Sent by the client when they collide with something which should smash
// them. // them.
@ -301,8 +301,9 @@ void RacingControlComponent::OnRequestDie(Entity* player) {
if (!racingPlayer.noSmashOnReload) { if (!racingPlayer.noSmashOnReload) {
racingPlayer.smashedTimes++; racingPlayer.smashedTimes++;
LOG("Death type %s", GeneralUtils::UTF16ToWTF8(deathType).c_str());
GameMessages::SendDie(vehicle, vehicle->GetObjectID(), LWOOBJID_EMPTY, true, GameMessages::SendDie(vehicle, vehicle->GetObjectID(), LWOOBJID_EMPTY, true,
eKillType::VIOLENT, u"", 0, 0, 90.0f, false, true, 0); eKillType::VIOLENT, deathType, 0, 0, 90.0f, false, true, 0);
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>(); auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
uint32_t respawnImagination = 0; uint32_t respawnImagination = 0;

View File

@ -135,7 +135,7 @@ public:
/** /**
* Invoked when the client says it should be smashed. * Invoked when the client says it should be smashed.
*/ */
void OnRequestDie(Entity* player); void OnRequestDie(Entity* player, const std::u16string& deathType = u"");
/** /**
* Invoked when the player has finished respawning. * Invoked when the player has finished respawning.

View File

@ -117,7 +117,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
} }
case MessageType::Game::PLAYER_LOADED: { case MessageType::Game::PLAYER_LOADED: {
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
entity->SetPlayerReadyForUpdates(); entity->SetPlayerReadyForUpdates();
auto* ghostComponent = entity->GetComponent<GhostComponent>(); auto* ghostComponent = entity->GetComponent<GhostComponent>();
@ -135,6 +134,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
} }
} }
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
auto* destroyable = entity->GetComponent<DestroyableComponent>(); auto* destroyable = entity->GetComponent<DestroyableComponent>();
destroyable->SetImagination(destroyable->GetImagination()); destroyable->SetImagination(destroyable->GetImagination());
Game::entityManager->SerializeEntity(entity); Game::entityManager->SerializeEntity(entity);

View File

@ -758,6 +758,13 @@ namespace GameMessages {
NiPoint3 target{}; NiPoint3 target{};
NiQuaternion rotation{}; NiQuaternion rotation{};
}; };
struct ChildLoaded : public GameMsg {
ChildLoaded() : GameMsg(MessageType::Game::CHILD_LOADED) {}
LOT templateID{};
LWOOBJID childID{};
};
}; };
#endif // GAMEMESSAGES_H #endif // GAMEMESSAGES_H

View File

@ -4,68 +4,146 @@
#include "Game.h" #include "Game.h"
#include "BinaryPathFinder.h" #include "BinaryPathFinder.h"
void StartChatServer() { #ifdef _WIN32
#include <windows.h>
#include <handleapi.h>
#include <processthreadsapi.h>
namespace {
const auto startup = STARTUPINFOW{
.cb = sizeof(STARTUPINFOW),
.lpReserved = nullptr,
.lpDesktop = nullptr,
.lpTitle = nullptr,
.dwX = 0,
.dwY = 0,
.dwXSize = 0,
.dwYSize = 0,
.dwXCountChars = 0,
.dwYCountChars = 0,
.dwFillAttribute = 0,
.dwFlags = 0,
.wShowWindow = 0,
.cbReserved2 = 0,
.lpReserved2 = nullptr,
.hStdInput = INVALID_HANDLE_VALUE,
.hStdOutput = INVALID_HANDLE_VALUE,
.hStdError = INVALID_HANDLE_VALUE,
};
}
#else
#include <unistd.h>
#endif
uint32_t StartChatServer() {
if (Game::ShouldShutdown()) { if (Game::ShouldShutdown()) {
LOG("Currently shutting down. Chat will not be restarted."); LOG("Currently shutting down. Chat will not be restarted.");
return; return 0;
}
auto chat_path = BinaryPathFinder::GetBinaryDir() / "ChatServer";
#ifdef _WIN32
chat_path.replace_extension(".exe");
auto chat_startup = startup;
auto chat_info = PROCESS_INFORMATION{};
if (!CreateProcessW(chat_path.wstring().data(), chat_path.wstring().data(),
nullptr, nullptr, false, 0, nullptr, nullptr,
&chat_startup, &chat_info))
{
LOG("Failed to launch ChatServer");
return 0;
}
// get pid and close unused handles
auto chat_pid = chat_info.dwProcessId;
CloseHandle(chat_info.hProcess);
CloseHandle(chat_info.hThread);
#else // *nix systems
const auto chat_pid = fork();
if (chat_pid < 0) {
LOG("Failed to launch ChatServer");
return 0;
} else if (chat_pid == 0) {
// We are the child process
execl(chat_path.string().c_str(), chat_path.string().c_str(), nullptr);
} }
#ifdef __APPLE__
//macOS doesn't need sudo to run on ports < 1024
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start /B " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
}
#endif #endif
LOG("ChatServer PID is %d", chat_pid);
return chat_pid;
} }
void StartAuthServer() { uint32_t StartAuthServer() {
if (Game::ShouldShutdown()) { if (Game::ShouldShutdown()) {
LOG("Currently shutting down. Auth will not be restarted."); LOG("Currently shutting down. Auth will not be restarted.");
return; return 0;
} }
#ifdef __APPLE__ auto auth_path = BinaryPathFinder::GetBinaryDir() / "AuthServer";
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start /B " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
}
#endif
}
void StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID) {
#ifdef _WIN32 #ifdef _WIN32
std::string cmd = "start /B " + (BinaryPathFinder::GetBinaryDir() / "WorldServer.exe").string() + " -zone "; auth_path.replace_extension(".exe");
#else auto auth_startup = startup;
std::string cmd; auto auth_info = PROCESS_INFORMATION{};
if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) { if (!CreateProcessW(auth_path.wstring().data(), auth_path.wstring().data(),
cmd = "sudo " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone "; nullptr, nullptr, false, 0, nullptr, nullptr,
} else { &auth_startup, &auth_info))
cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone "; {
LOG("Failed to launch AuthServer");
return 0;
}
// get pid and close unused handles
auto auth_pid = auth_info.dwProcessId;
CloseHandle(auth_info.hProcess);
CloseHandle(auth_info.hThread);
#else // *nix systems
const auto auth_pid = fork();
if (auth_pid < 0) {
LOG("Failed to launch AuthServer");
return 0;
} else if (auth_pid == 0) {
// We are the child process
execl(auth_path.string().c_str(), auth_path.string().c_str(), nullptr);
} }
#endif #endif
LOG("AuthServer PID is %d", auth_pid);
cmd.append(std::to_string(mapID)); return auth_pid;
cmd.append(" -port "); }
cmd.append(std::to_string(port));
cmd.append(" -instance "); uint32_t StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID) {
cmd.append(std::to_string(lastInstanceID)); auto world_path = BinaryPathFinder::GetBinaryDir() / "WorldServer";
cmd.append(" -maxclients "); #ifdef _WIN32
cmd.append(std::to_string(maxPlayers)); world_path.replace_extension(".exe");
cmd.append(" -clone "); auto cmd = world_path.wstring() + L" -zone " + std::to_wstring(mapID) + L" -port " + std::to_wstring(port) +
cmd.append(std::to_string(cloneID)); L" -instance " + std::to_wstring(lastInstanceID) + L" -maxclients " + std::to_wstring(maxPlayers) +
L" -clone " + std::to_wstring(cloneID);
#ifndef _WIN32
cmd.append("&"); //Sends our next process to the background on Linux auto world_startup = startup;
#endif auto world_info = PROCESS_INFORMATION{};
if (!CreateProcessW(world_path.wstring().data(), cmd.data(),
auto ret = system(cmd.c_str()); nullptr, nullptr, false, 0, nullptr, nullptr,
&world_startup, &world_info))
{
LOG("Failed to launch WorldServer");
return 0;
}
// get pid and close unused handles
auto world_pid = world_info.dwProcessId;
CloseHandle(world_info.hProcess);
CloseHandle(world_info.hThread);
#else
const auto world_pid = fork();
if (world_pid < 0) {
LOG("Failed to launch WorldServer");
return 0;
} else if (world_pid == 0) {
// We are the child process
execl(world_path.string().c_str(), world_path.string().c_str(),
"-zone", std::to_string(mapID).c_str(),
"-port", std::to_string(port).c_str(),
"-instance", std::to_string(lastInstanceID).c_str(),
"-maxclients", std::to_string(maxPlayers).c_str(),
"-clone", std::to_string(cloneID).c_str(), nullptr);
}
#endif
LOG("WorldServer PID is %d", world_pid);
return world_pid;
} }

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "dCommonVars.h" #include "dCommonVars.h"
void StartAuthServer(); uint32_t StartAuthServer();
void StartChatServer(); uint32_t StartChatServer();
void StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID); uint32_t StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID);

View File

@ -10,6 +10,7 @@
#include "MessageType/Server.h" #include "MessageType/Server.h"
#include "MessageType/Master.h" #include "MessageType/Master.h"
#include "BinaryPathFinder.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "MasterPackets.h" #include "MasterPackets.h"
#include "ZoneInstanceManager.h" #include "ZoneInstanceManager.h"
@ -68,7 +69,16 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
LOG("%s Server is listening on %s:%i with encryption: %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption)); LOG("%s Server is listening on %s:%i with encryption: %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption));
else else
LOG("%s Server is listening on %s:%i with encryption: %i, running zone %i / %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption), zoneID, instanceID); LOG("%s Server is listening on %s:%i with encryption: %i, running zone %i / %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption), zoneID, instanceID);
} else { LOG("FAILED TO START SERVER ON IP/PORT: %s:%i", ip.c_str(), port); return; } } else {
LOG("FAILED TO START SERVER ON IP/PORT: %s:%i", ip.c_str(), port);
#ifdef DARKFLAME_PLATFORM_LINUX
if (mServerType == ServerType::Auth) {
const auto cwd = BinaryPathFinder::GetBinaryDir();
LOG("Try running the following command before launching again:\n sudo setcap 'cap_net_bind_service=+ep' \"%s/AuthServer\"", cwd.string().c_str());
}
#endif
return;
}
mLogger->SetLogToConsole(prevLogSetting); mLogger->SetLogToConsole(prevLogSetting);
@ -109,20 +119,23 @@ Packet* dServer::ReceiveFromMaster() {
if (packet) { if (packet) {
if (packet->length < 1) { mMasterPeer->DeallocatePacket(packet); return nullptr; } if (packet->length < 1) { mMasterPeer->DeallocatePacket(packet); return nullptr; }
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { switch (packet->data[0]) {
case ID_DISCONNECTION_NOTIFICATION:
[[fallthrough]];
case ID_CONNECTION_LOST: {
LOG("Lost our connection to master, shutting DOWN!"); LOG("Lost our connection to master, shutting DOWN!");
mMasterConnectionActive = false; mMasterConnectionActive = false;
//ConnectToMaster(); //We'll just shut down now // ConnectToMaster(); // We'll just shut down now
break;
} }
case ID_CONNECTION_REQUEST_ACCEPTED: {
if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) {
LOG("Established connection to master, zone (%i), instance (%i)", this->GetZoneID(), this->GetInstanceID()); LOG("Established connection to master, zone (%i), instance (%i)", this->GetZoneID(), this->GetInstanceID());
mMasterConnectionActive = true; mMasterConnectionActive = true;
mMasterSystemAddress = packet->systemAddress; mMasterSystemAddress = packet->systemAddress;
MasterPackets::SendServerInfo(this, packet); MasterPackets::SendServerInfo(this, packet);
break;
} }
case ID_USER_PACKET_ENUM: {
if (packet->data[0] == ID_USER_PACKET_ENUM) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) { if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<MessageType::Master>(packet->data[3])) { switch (static_cast<MessageType::Master>(packet->data[3])) {
case MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE: { case MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE: {
@ -133,12 +146,13 @@ Packet* dServer::ReceiveFromMaster() {
*mShouldShutdown = -2; *mShouldShutdown = -2;
break; break;
//When we handle these packets in World instead dServer, we just return the packet's pointer. // When we handle these packets in World instead dServer, we just return the packet's pointer.
default: default:
return packet; return packet;
} }
} }
break;
}
} }
mMasterPeer->DeallocatePacket(packet); mMasterPeer->DeallocatePacket(packet);

View File

@ -64,21 +64,22 @@ void AmSkullkinTower::SpawnLegs(Entity* self, const std::string& loc) {
info.rot = NiQuaternion::LookAt(info.pos, self->GetPosition()); info.rot = NiQuaternion::LookAt(info.pos, self->GetPosition());
auto* entity = Game::entityManager->CreateEntity(info); auto* entity = Game::entityManager->CreateEntity(info, nullptr, self);
Game::entityManager->ConstructEntity(entity); Game::entityManager->ConstructEntity(entity);
OnChildLoaded(self, entity);
} }
void AmSkullkinTower::OnChildLoaded(Entity* self, Entity* child) { void AmSkullkinTower::OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {
auto legTable = self->GetVar<std::vector<LWOOBJID>>(u"legTable"); auto legTable = self.GetVar<std::vector<LWOOBJID>>(u"legTable");
legTable.push_back(child->GetObjectID()); legTable.push_back(childLoaded.childID);
self->SetVar(u"legTable", legTable); self.SetVar(u"legTable", legTable);
const auto selfID = self->GetObjectID(); const auto selfID = self.GetObjectID();
auto* const child = Game::entityManager->GetEntity(childLoaded.childID);
if (!child) return;
child->AddDieCallback([this, selfID, child]() { child->AddDieCallback([this, selfID, child]() {
auto* self = Game::entityManager->GetEntity(selfID); auto* self = Game::entityManager->GetEntity(selfID);

View File

@ -8,7 +8,7 @@ public:
void SpawnLegs(Entity* self, const std::string& loc); void SpawnLegs(Entity* self, const std::string& loc);
void OnChildLoaded(Entity* self, Entity* child); void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) override;
void NotifyDie(Entity* self, Entity* other, Entity* killer); void NotifyDie(Entity* self, Entity* other, Entity* killer);

View File

@ -77,8 +77,6 @@ void QbSpawner::OnTimerDone(Entity* self, std::string timerName) {
auto* child = Game::entityManager->CreateEntity(info, nullptr, self); auto* child = Game::entityManager->CreateEntity(info, nullptr, self);
Game::entityManager->ConstructEntity(child); Game::entityManager->ConstructEntity(child);
OnChildLoaded(self, child);
} else { } else {
auto* mob = Game::entityManager->GetEntity(mobTable[i]); auto* mob = Game::entityManager->GetEntity(mobTable[i]);
AggroTargetObject(self, mob); AggroTargetObject(self, mob);
@ -88,16 +86,19 @@ void QbSpawner::OnTimerDone(Entity* self, std::string timerName) {
} }
} }
void QbSpawner::OnChildLoaded(Entity* self, Entity* child) { void QbSpawner::OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {
auto mobTable = self->GetVar<std::vector<LWOOBJID>>(u"mobTable"); auto* const child = Game::entityManager->GetEntity(childLoaded.childID);
if (!child) return;
auto mobTable = self.GetVar<std::vector<LWOOBJID>>(u"mobTable");
auto tableLoc = child->GetVar<int>(u"mobTableLoc"); auto tableLoc = child->GetVar<int>(u"mobTableLoc");
mobTable[tableLoc] = child->GetObjectID(); mobTable[tableLoc] = child->GetObjectID();
self->SetVar<std::vector<LWOOBJID>>(u"mobTable", mobTable); self.SetVar<std::vector<LWOOBJID>>(u"mobTable", mobTable);
AggroTargetObject(self, child); AggroTargetObject(&self, child);
const auto selfID = self->GetObjectID(); const auto selfID = self.GetObjectID();
child->AddDieCallback([this, selfID, child]() { child->AddDieCallback([this, selfID, child]() {
auto* self = Game::entityManager->GetEntity(selfID); auto* self = Game::entityManager->GetEntity(selfID);

View File

@ -6,7 +6,7 @@ public:
void OnStartup(Entity* self) override; void OnStartup(Entity* self) override;
void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override; void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
void OnTimerDone(Entity* self, std::string timerName) override; void OnTimerDone(Entity* self, std::string timerName) override;
void OnChildLoaded(Entity* self, Entity* child); void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) override;
void OnChildRemoved(Entity* self, Entity* child); void OnChildRemoved(Entity* self, Entity* child);
void AggroTargetObject(Entity* self, Entity* enemy); void AggroTargetObject(Entity* self, Entity* enemy);
private: private:

View File

@ -91,7 +91,7 @@ void NtCombatChallengeServer::SpawnTargetDummy(Entity* self) {
info.rot = self->GetRotation(); info.rot = self->GetRotation();
info.settings = { new LDFData<std::string>(u"custom_script_server", "scripts\\02_server\\Map\\NT\\L_NT_COMBAT_CHALLENGE_DUMMY.lua") }; info.settings = { new LDFData<std::string>(u"custom_script_server", "scripts\\02_server\\Map\\NT\\L_NT_COMBAT_CHALLENGE_DUMMY.lua") };
auto* dummy = Game::entityManager->CreateEntity(info); auto* dummy = Game::entityManager->CreateEntity(info, nullptr, self);
dummy->SetVar(u"challengeObjectID", self->GetObjectID()); dummy->SetVar(u"challengeObjectID", self->GetObjectID());
@ -104,26 +104,18 @@ void NtCombatChallengeServer::SetAttackImmunity(LWOOBJID objID, bool bTurnOn) {
} }
void NtCombatChallengeServer::OnChildLoaded(Entity* self, Entity* child) { void NtCombatChallengeServer::OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {
auto targetNumber = self->GetVar<int32_t>(u"TargetNumber"); auto* const child = Game::entityManager->GetEntity(childLoaded.childID);
if (targetNumber == 0) targetNumber = 1;
self->SetVar(u"TargetNumber", targetNumber + 1);
const auto playerID = self->GetVar<LWOOBJID>(u"playerID"); if (child) {
child->SetRotation(NiQuaternion::FromEulerAngles(child->GetRotation().GetEulerAngles() += NiPoint3(0, PI, 0))); // rotate 180 degrees
auto* player = Game::entityManager->GetEntity(playerID);
if (player == nullptr) {
return;
} }
child->SetRotation(NiQuaternion::LookAt(child->GetPosition(), player->GetPosition())); self.SetVar(u"currentTargetID", child->GetObjectID());
self->SetVar(u"currentTargetID", child->GetObjectID());
Game::entityManager->SerializeEntity(child); Game::entityManager->SerializeEntity(child);
child->GetGroups().push_back("targets_" + std::to_string(self->GetObjectID())); child->GetGroups().push_back("targets_" + std::to_string(self.GetObjectID()));
} }
void NtCombatChallengeServer::ResetGame(Entity* self) { void NtCombatChallengeServer::ResetGame(Entity* self) {

View File

@ -12,7 +12,7 @@ public:
void OnMessageBoxResponse(Entity* self, Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) override; void OnMessageBoxResponse(Entity* self, Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) override;
void SpawnTargetDummy(Entity* self); void SpawnTargetDummy(Entity* self);
void SetAttackImmunity(LWOOBJID objID, bool bTurnOn); void SetAttackImmunity(LWOOBJID objID, bool bTurnOn);
void OnChildLoaded(Entity* self, Entity* child); void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) override;
void ResetGame(Entity* self); void ResetGame(Entity* self);
void OnActivityTimerUpdate(Entity* self, float timeRemaining); void OnActivityTimerUpdate(Entity* self, float timeRemaining);
void OnTimerDone(Entity* self, std::string timerName) override; void OnTimerDone(Entity* self, std::string timerName) override;

View File

@ -294,6 +294,9 @@
#include "ShardArmor.h" #include "ShardArmor.h"
#include "TeslaPack.h" #include "TeslaPack.h"
#include "StunImmunity.h" #include "StunImmunity.h"
#include "GfRaceServer.h"
#include "FvRaceServer.h"
#include "VehicleDeathTriggerWaterServer.h"
// Survival scripts // Survival scripts
#include "AgSurvivalStromling.h" #include "AgSurvivalStromling.h"
@ -694,6 +697,9 @@ namespace {
{"scripts\\ai\\AG\\L_AG_SPIDER_BOSS_MESSAGE.lua", []() {return new AgSpiderBossMessage();}}, {"scripts\\ai\\AG\\L_AG_SPIDER_BOSS_MESSAGE.lua", []() {return new AgSpiderBossMessage();}},
{"scripts\\ai\\GF\\L_GF_RACE_INSTANCER.lua", []() {return new GfRaceInstancer();}}, {"scripts\\ai\\GF\\L_GF_RACE_INSTANCER.lua", []() {return new GfRaceInstancer();}},
{"scripts\\ai\\RACING\\TRACK_NS\\NS_RACE_SERVER.lua", []() {return new NsRaceServer();}}, {"scripts\\ai\\RACING\\TRACK_NS\\NS_RACE_SERVER.lua", []() {return new NsRaceServer();}},
{"scripts\\ai\\RACING\\TRACK_GF\\GF_RACE_SERVER.lua", []() {return new GfRaceServer();}},
{"scripts\\ai\\RACING\\TRACK_FV\\FV_RACE_SERVER.lua", []() {return new FvRaceServer();}},
{"scripts\\ai\\RACING\\OBJECTS\\VEHICLE_DEATH_TRIGGER_WATER_SERVER.lua", []() {return new VehicleDeathTriggerWaterServer();}},
}; };
@ -712,6 +718,12 @@ namespace {
"scripts\\ai\\PETS\\PET_BLOCKER.lua", "scripts\\ai\\PETS\\PET_BLOCKER.lua",
"scripts\\ai\\PETS\\PET_FLEA_MISSION.lua", "scripts\\ai\\PETS\\PET_FLEA_MISSION.lua",
"scripts\\ai\\ACT\\L_ACT_PET_INSTANCE_EXIT.lua", "scripts\\ai\\ACT\\L_ACT_PET_INSTANCE_EXIT.lua",
"scripts\\ai\\WILD\\L_WILD_GF_FROG.lua",
"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_Robotanist.lua",
"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_Seperator.lua",
"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_InfectedCitizen.lua",
"scripts\\ai\\MINIGAME\\SIEGE\\OBJECTS\\ATTACKER_BOUNCER_SERVER.lua",
"scripts\\ai\\AG\\L_AG_ZONE_PLAYER.lua",
}; };
}; };

View File

@ -373,6 +373,14 @@ namespace CppScripts {
* @param fire The firing data * @param fire The firing data
*/ */
virtual void OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) {}; virtual void OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) {};
/**
* @brief Handles when a child is loaded
*
* @param self
* @param fire The child info
*/
virtual void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {};
}; };
Script* const GetScript(Entity* parent, const std::string& scriptName); Script* const GetScript(Entity* parent, const std::string& scriptName);

View File

@ -13,6 +13,18 @@ foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_NS})
set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_NS/${file}") set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_NS/${file}")
endforeach() endforeach()
add_subdirectory(TRACK_GF)
foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_GF})
set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_GF/${file}")
endforeach()
add_subdirectory(TRACK_FV)
foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_FV})
set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_FV/${file}")
endforeach()
add_library(dScriptsAiRacing OBJECT ${DSCRIPTS_SOURCES_AI_RACING}) add_library(dScriptsAiRacing OBJECT ${DSCRIPTS_SOURCES_AI_RACING})
target_include_directories(dScriptsAiRacing PUBLIC "." "OBJECTS" "TRACK_NS") target_include_directories(dScriptsAiRacing PUBLIC "." "OBJECTS" "TRACK_NS" "TRACK_GF" "TRACK_FV")
target_precompile_headers(dScriptsAiRacing REUSE_FROM dScriptsBase) target_precompile_headers(dScriptsAiRacing REUSE_FROM dScriptsBase)

View File

@ -7,4 +7,5 @@ set(DSCRIPTS_SOURCES_AI_RACING_OBJECTS
"FvRacePillarDServer.cpp" "FvRacePillarDServer.cpp"
"FvRaceSmashEggImagineServer.cpp" "FvRaceSmashEggImagineServer.cpp"
"RaceSmashServer.cpp" "RaceSmashServer.cpp"
"VehicleDeathTriggerWaterServer.cpp"
PARENT_SCOPE) PARENT_SCOPE)

View File

@ -0,0 +1,16 @@
#include "VehicleDeathTriggerWaterServer.h"
#include "PossessorComponent.h"
#include "RacingControlComponent.h"
void VehicleDeathTriggerWaterServer::OnCollisionPhantom(Entity* self, Entity* target) {
if (target->IsPlayer() && !target->GetIsDead()) {
const std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(RacingControlComponent::ComponentType);
for (auto* const racingController : racingControllers) {
auto* racingControlComponent = racingController->GetComponent<RacingControlComponent>();
if (racingControlComponent) {
racingControlComponent->OnRequestDie(target, u"death_water");
}
}
}
}

View File

@ -0,0 +1,11 @@
#ifndef VEHICLEDEATHTRIGGERWATERSERVER_H
#define VEHICLEDEATHTRIGGERWATERSERVER_H
#include "CppScripts.h"
class VehicleDeathTriggerWaterServer : public CppScripts::Script {
public:
void OnCollisionPhantom(Entity* self, Entity* target) override;
};
#endif //!VEHICLEDEATHTRIGGERWATERSERVER_H

View File

@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_AI_RACING_TRACK_FV
"FvRaceServer.cpp"
PARENT_SCOPE)

View File

@ -0,0 +1,55 @@
#include "FvRaceServer.h"
#include "RacingControlComponent.h"
#include "Entity.h"
using std::unique_ptr;
using std::make_unique;
void FvRaceServer::OnStartup(Entity* self) {
GameMessages::ConfigureRacingControl config;
auto& raceSet = config.racingSettings;
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameType", u"Racing"));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameState", u"Starting"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_Of_PlayersPerTeam", 6));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_to_Start", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_for_Group_Achievements", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Car_Object", 7703));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"Race_PathName", u"MainPath"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Current_Lap", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Laps", 3));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"activityID", 54));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_1", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_2", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_3", 80));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_4", 70));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_5", 60));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_6", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_1", 15));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_2", 25));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_3", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_4", 85));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_5", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_6", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Spawn_Groups", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Spawners", 4847));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Spawners", 4848));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Flag", 4850));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Flag", 4851));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Point", 4846));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Point", 4845));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Mark", 4844));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Mark", 4843));
const std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
for (auto* const racingController : racingControllers) {
auto* racingComponent = racingController->GetComponent<RacingControlComponent>();
if (racingComponent) racingComponent->MsgConfigureRacingControl(config);
}
}

View File

@ -0,0 +1,11 @@
#ifndef FVRACESERVER_H
#define FVRACESERVER_H
#include "RaceImaginationServer.h"
class FvRaceServer : public RaceImaginationServer {
public:
void OnStartup(Entity* self) override;
};
#endif //!FVRACESERVER_H

View File

@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_AI_RACING_TRACK_GF
"GfRaceServer.cpp"
PARENT_SCOPE)

View File

@ -0,0 +1,55 @@
#include "GfRaceServer.h"
#include "RacingControlComponent.h"
#include "Entity.h"
using std::unique_ptr;
using std::make_unique;
void GfRaceServer::OnStartup(Entity* self) {
GameMessages::ConfigureRacingControl config;
auto& raceSet = config.racingSettings;
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameType", u"Racing"));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameState", u"Starting"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_Of_PlayersPerTeam", 6));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_to_Start", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_for_Group_Achievements", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Car_Object", 7703));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"Race_PathName", u"MainPath"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Current_Lap", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Laps", 3));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"activityID", 39));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_1", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_2", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_3", 80));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_4", 70));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_5", 60));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_6", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_1", 15));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_2", 25));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_3", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_4", 85));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_5", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_6", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Spawn_Groups", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Spawners", 4847));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Spawners", 4848));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Flag", 4850));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Flag", 4851));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Point", 4846));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Point", 4845));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Mark", 4844));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Mark", 4843));
const std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
for (auto* const racingController : racingControllers) {
auto* racingComponent = racingController->GetComponent<RacingControlComponent>();
if (racingComponent) racingComponent->MsgConfigureRacingControl(config);
}
}

View File

@ -0,0 +1,11 @@
#ifndef GFRACESERVER_H
#define GFRACESERVER_H
#include "RaceImaginationServer.h"
class GfRaceServer : public RaceImaginationServer {
public:
void OnStartup(Entity* self) override;
};
#endif //!GFRACESERVER_H

View File

@ -7,15 +7,5 @@ master_server_port=2000
# The port number to start world servers on. Will be incremented for each world # The port number to start world servers on. Will be incremented for each world
world_port_start=3000 world_port_start=3000
# Use sudo when launching the auth server.
# Required by default if on Linux as auth runs on port 1001
use_sudo_auth=1
# Use sudo when launching the chat server
use_sudo_chat=0
# Use sudo when launching world servers
use_sudo_world=0
# 0 or 1, should autostart auth, chat, and char servers # 0 or 1, should autostart auth, chat, and char servers
prestart_servers=1 prestart_servers=1